Skip to content

Conversation

@rthideaway
Copy link
Contributor

No description provided.

@fubhy

This comment has been minimized.

@codecov
Copy link

codecov bot commented Jun 24, 2019

Codecov Report

Merging #818 into 8.x-4.x will decrease coverage by 4.92%.
The diff coverage is 0%.

Impacted file tree graph

@@              Coverage Diff              @@
##             8.x-4.x     #818      +/-   ##
=============================================
- Coverage      76.08%   71.15%   -4.93%     
- Complexity       583      606      +23     
=============================================
  Files             85       86       +1     
  Lines           1271     1359      +88     
=============================================
  Hits             967      967              
- Misses           304      392      +88
Impacted Files Coverage Δ Complexity Δ
src/GraphQL/Utility/FileUpload.php 0% <0%> (ø) 23 <23> (?)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4cb3594...f4ab005. Read the comment docs.

@codecov
Copy link

codecov bot commented Jun 24, 2019

Codecov Report

❗ No coverage uploaded for pull request base (8.x-4.x@a7328ad). Click here to learn what that means.
The diff coverage is 41.54%.

Impacted file tree graph

@@            Coverage Diff             @@
##             8.x-4.x     #818   +/-   ##
==========================================
  Coverage           ?   51.68%           
  Complexity         ?      651           
==========================================
  Files              ?      115           
  Lines              ?     1662           
  Branches           ?        0           
==========================================
  Hits               ?      859           
  Misses             ?      803           
  Partials           ?        0           
Impacted Files Coverage Δ Complexity Δ
src/Access/ExplorerAccessCheck.php 0.00% <0.00%> (ø) 2.00 <2.00> (?)
src/Access/VoyagerAccessCheck.php 0.00% <0.00%> (ø) 2.00 <2.00> (?)
src/Cache/Context/StaticCacheContext.php 0.00% <0.00%> (ø) 4.00 <4.00> (?)
src/Controller/SubrequestExtractionController.php 0.00% <0.00%> (ø) 1.00 <0.00> (?)
src/GraphQL/Buffers/EntityBuffer.php 80.76% <0.00%> (ø) 7.00 <0.00> (?)
src/GraphQL/Buffers/EntityRevisionBuffer.php 0.00% <0.00%> (ø) 7.00 <7.00> (?)
src/GraphQL/Resolver/Condition.php 0.00% <0.00%> (ø) 8.00 <8.00> (?)
src/GraphQL/Resolver/Context.php 0.00% <0.00%> (ø) 5.00 <5.00> (?)
src/GraphQL/Resolver/Map.php 0.00% <0.00%> (ø) 4.00 <4.00> (?)
src/GraphQL/Resolver/Path.php 0.00% <0.00%> (ø) 4.00 <4.00> (?)
... and 76 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update a7328ad...40f23c8. Read the comment docs.

@fubhy
Copy link
Contributor

fubhy commented Jun 26, 2019

@rthideaway Instead of making this a service, this should just be a DataProducer. Can you rewrite it accordingly? To understand how file uploads work in this module, check out this test: https://github.com/drupal-graphql/graphql/blob/8.x-4.x/tests/src/Kernel/Framework/UploadMutationTest.php

@fubhy
Copy link
Contributor

fubhy commented Jun 26, 2019

To understand how the file makes its way from the file content of the request into the variable, check out this: https://github.com/drupal-graphql/graphql/blob/8.x-4.x/src/Routing/QueryRouteEnhancer.php#L38-L42

@fubhy fubhy changed the title Introduce file upload GraphQL service. Add file upload data producer. Jun 26, 2019
@rthideaway
Copy link
Contributor Author

will address this today/tomorrow.

@rthideaway
Copy link
Contributor Author

@fubhy how did imagine this to work? What should be the outcome of data producer, the File entity? Was playing with it but figured out that having separate file upload data producer makes the logic more complicated for us.

Imagine the following very basic use case:

  • Given I have a mutation which creates a Resume (Profile entity) along with cv file attached to some file field of Resume (Profile entity)

How the file upload data producer is integrated to it? Because if there should be separate file upload data producer upfront, I guess we need to use compose. But how? File data producer needs some file field config metadata so it can validate the file (list of allowed extensions, max upload size, destination file path, etc). So my compose already needs three steps:

  1. Get the file field config settings of the file we are going to attach. Make contexts out of it for file data producer.
  2. Inject as contexts to file data producer, validate file, eventually create File entity
  3. Create resume. But here I can find out that user don't have permission to create a Resume and all the file upload stuff was uselessly executed. File is already in destination, which I don't want.

In theory also, what if I don't know to which file field I am uploading beforehand as it will be decided during execution of Resume data producer. Can't think of any case now, but for me the file upload works better when it's processed within specific data producer as a service. Like with this resume, if user does not have permission to create a resume I simply don't process the file, right?

Am I missing something here? Sorry if yes :)

@fubhy
Copy link
Contributor

fubhy commented Jul 24, 2019

That all sounds like it might be better done in a Trait then?! Or a service after all? Mutations are different and more complicated than normal field resolvers I guess and harder to compose.

@rthideaway
Copy link
Contributor Author

We already have a service (in this patch). But trait is fine for me as well. Maybe you could take a look at this patch what would you change here? The thing is we need to also get the violations of that trait/service, so we can display them to user (incorrect file extension, file too big, etc)

* @param mixed $violations
* List of violations.
*/
protected function registerViolations($violations) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a service. You can't store stuff within it across potentially multiple mutations or you'll leak violations from one mutations into another.

The service itself (if we add one) should only do low level operations worthy of a service that do not assume too many concrete implementation details.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Atm, there are only two public methods, getViolations and createTemporaryFileUpload. The second one is resetting violations at the start of the process. We used it like calling method for creating file and when empty then getting violations. Next mutation can be called without interfering the first one, as the violations got reseted.

But agree this does not look right. So what do you suggest to do here then, how to return violations? Some object? Could return something like FileUploadResponse, would be that ok?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will just try something and let's see

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to avoid side-effects as much as possible in our code base. Hence, returning a wrapper object instead might make sense indeed.

* @param array $settings
* Fle settings as specified in regular file field config.
*
* @return \Drupal\file\FileInterface|bool
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of returning FALSE i'd vote for returning NULL in case of a failure.

$this->logger->error(sprintf('Unknown error while uploading the file "%s".', $file->getFilename()));
return FALSE;
}
$upload_dir = $settings['uri_scheme'] . '://' . trim($settings['file_directory'], '/');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use camelCase across the board!

$name = $file->getClientOriginalName();
$mime = $this->mimeTypeGuesser->guess($name);
$destination = $this->fileSystem->getDestinationFilename("{$upload_dir}/{$name}", $this->fileSystem::EXISTS_RENAME);
// Begin building file entity.

This comment was marked as resolved.

* @param mixed $violations
* List of violations.
*/
protected function registerViolations($violations) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd like to avoid side-effects as much as possible in our code base. Hence, returning a wrapper object instead might make sense indeed.

@rthideaway
Copy link
Contributor Author

@fubhy addressed your remarks, please re-check it, once you find the time

@fubhy fubhy changed the title Add file upload data producer. Add a file upload utility Oct 15, 2019
@pmelab pmelab added the 4.x label Oct 30, 2019
@bird-cage
Copy link

This would be a useful feature.
Is there any intention of finishing and merging this?

Copy link
Contributor

@klausi klausi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I see 3 things we need to do here:

  1. Adapt Response objects to structure from #1013
  2. Add test overage. This is security critical code since file uploads are a big attack vector.
  3. Update documentation how you have to assemble a file upload in your custom data producer

We could split up 2. and 3. into new pull requests to not make this too long?

/**
* File upload response wrapper.
*/
class FileUploadResponse implements FileUploadResponseInterface {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we now started a Response namespace in #1013 . We should put any new response classes into this same namespace for consistency.

We can probably unify this with the ResponseInterface we have now?

@joaogarin
Copy link
Member

I could take care of number 3 no problem. @rthideaway would you adapt this to the new structure (1) and maybe make a test for it

@klausi
Copy link
Contributor

klausi commented Nov 11, 2020

I will look into test coverage now and what we could do here.

$validators['file_validate_size'] = [$this->getMaxUploadSize($settings)];

// Add the extension check if necessary.
if (!empty($settings['file_extensions'])) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For security reasons we should have a default value of "txt" here if the user forgot to configure allowed file extensions by accident.

@klausi
Copy link
Contributor

klausi commented Nov 11, 2020

First set of tests created in #1112

@klausi klausi merged commit 86d503f into drupal-graphql:8.x-4.x Nov 17, 2020
@klausi
Copy link
Contributor

klausi commented Nov 17, 2020

Merging this here for credits, will then merge #1112

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants