apollo_fetch_upload_rails_middleware 0.2.0.alpha → 1.0.0.alpha

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 0c45b7a219023f114244d9d561288f5f073823f1
4
- data.tar.gz: f8c9bf9b2f05f8b0eec9e9aeb732d84777c76eed
2
+ SHA256:
3
+ metadata.gz: '0742048ae5b92d8ac9f64f6ac57ea00fba3dcb92265c30e3acaa60f8d466524b'
4
+ data.tar.gz: 0bf560ea2ea86edc752ddf6431a30a5b9e5602503507a31e605e0dfab7b2548d
5
5
  SHA512:
6
- metadata.gz: 17fc13fdadcdfcf28f0f1447db947cc4534263f0730f347021717299634bc1949b16c3330b39562761946f30e3322e069e248db19ed39f3c989fcc336bf1afba
7
- data.tar.gz: 0e38e114ec355ef32a44dc0fc6cd5aafe671e7af11240b7bd1b2fd8498b72d4cd097d9f0d163a6bafeadbfc6a34473d94d0a554b65eeba53f27ba3c578f4a0f2
6
+ metadata.gz: e48cd2fe15cb4782ff7f045ef4cea086731e2222c1e634646fe5cb3d0558d64f49dd8f8a56c1875bdbadfa56bfcfcc6b8baafde79e413b213a895a087c88951c
7
+ data.tar.gz: 4ec7740ee4f13872385f19d7f32d109bb3739718314e2c4d0375a77766a7298d0512fdd335822b0ab96c75d51eb7d20fcc07143c1eb27d0b970f2aac2717e126
@@ -6,68 +6,150 @@ module ApolloFetchUploadRailsMiddleware
6
6
 
7
7
  def call(env)
8
8
  request = Rack::Request.new(env)
9
- if request.content_type.try(:start_with?, 'multipart/form-data') && request.params.key?('operations')
9
+
10
+ if request.content_type.try(:start_with?, 'multipart/form-data') &&
11
+ request.params.key?('operations') &&
12
+ request.params.key?('map')
13
+
10
14
  gql_operations = JSON.parse(request.params['operations'])
11
- request.update_param('query', gql_operations['query'])
12
- request.update_param('variables', gql_operations['variables'])
13
- request.update_param('operationName', gql_operations['operationName'])
14
-
15
- # Gather the param key/value pairs for which the values are File instances (sent as part
16
- # of the Form POST from apollo fetch upload client-side middleware,
17
- # cf. https://github.com/apollographql/apollo-fetch/blob/master/packages/apollo-fetch-upload/src/index.ts).
18
- file_kv_pairs = []
19
- request.params.each do |k, v|
20
- next unless k.start_with?('variables.') &&
21
- v.is_a?(Hash) &&
22
- v.key?(:tempfile) &&
23
- v[:tempfile].is_a?(Tempfile)
24
-
25
- file_kv_pairs << [k, v]
15
+ file_to_variables_map = JSON.parse(request.params['map'])
16
+
17
+ # Gather file data.
18
+ file_to_metadata_map = {}
19
+ file_to_variables_map.each_key do |file_index|
20
+ file_to_metadata_map[file_index] = validate_file_info(request.params[file_index.to_s])
21
+ end
22
+
23
+ if gql_operations.is_a?(Hash)
24
+ request.update_param('query', gql_operations['query'])
25
+ request.update_param('operationName', gql_operations['operationName'])
26
+ request.update_param(
27
+ 'variables',
28
+ fill_in_gql_variables(
29
+ file_to_variables_map,
30
+ file_to_metadata_map,
31
+ gql_operations['variables'],
32
+ ),
33
+ )
34
+ elsif gql_operations.is_a?(Array)
35
+ gql_operations.each_with_index do |gql_operation, idx|
36
+ gql_operation['variables'] = fill_in_gql_variables(
37
+ file_to_variables_map,
38
+ file_to_metadata_map,
39
+ gql_operation['variables'],
40
+ batch_index: idx,
41
+ )
42
+ end
43
+
44
+ # cf. https://github.com/rmosolgo/graphql-ruby/blob/master/
45
+ # guides/queries/multiplex.md#apollo-query-batching
46
+ # -> "Apollo sends the params in a _json variable when batching is enabled"
47
+ request.update_param('_json', gql_operations)
48
+ else
49
+ raise 'Invalid JSON in "operations" request param.'
26
50
  end
51
+ end
52
+
53
+ @app.call(env)
54
+ end
55
+
56
+ def fill_in_gql_variables(
57
+ file_to_variables_map,
58
+ file_to_metadata_map,
59
+ variables_h,
60
+ batch_idx: nil
61
+ )
62
+ # file_to_variables_map is of type map<int, [string]>, mapping file indices to the GQL variables which
63
+ # use that file (the author of apollo-upload-client does it this way to support batching more efficiently).
64
+ #
65
+ # request.params should have keys of file indices (as strings) mapping to metadata about the file itself, e.g.
66
+ #
67
+ # "0"=> {
68
+ # :filename=>"f6765.pdf",
69
+ # :type=>"application/pdf",
70
+ # :name=>"0",
71
+ # :tempfile=>
72
+ # <File:/var/folders/cc/fwn8xb8s6jzdg672klspnvg40000gn/T/RackMultipart20180719-18089-y5qrhg.pdf>,
73
+ # :head=>
74
+ # "Content-Disposition: form-data; name=\"0\"; filename=\"f6765.pdf\"\r\n" +
75
+ # "Content-Type: application/pdf\r\n"
76
+ # }
77
+ #
78
+ # So, what we need to do is go through all of the files, and for each GQL variable which uses that file,
79
+ # set it (using pathset, set below) to have that File instance.
27
80
 
28
- variables_wrapper = ActiveSupport::HashWithIndifferentAccess.new(
29
- variables: gql_operations['variables'],
30
- )
81
+ variables_wrapper = { variables: variables_h }.with_indifferent_access
31
82
 
32
- file_kv_pairs.each do |kv_pair|
33
- path, file = kv_pair
83
+ file_to_variables_map.each do |file_index, variable_paths|
84
+ file_info = file_to_metadata_map[file_index]
34
85
 
35
- file_info = {
36
- name: file[:filename],
37
- path: file[:tempfile].path,
38
- size: file[:tempfile].size,
39
- type: file[:type],
40
- }
86
+ variable_paths.each do |variable_path|
87
+ variable_path_components = variable_path.split('.')
88
+
89
+ unless batch_idx.nil?
90
+ # We only care about this variable path if it matches the given batch_idx.
91
+ # Otherwise, it doesn't apply to this operation.
92
+ variable_path_batch_idx = variable_path_components.shift
93
+ next unless batch_idx == variable_path_batch_idx
94
+ end
41
95
 
42
96
  pathset(
43
97
  variables_wrapper,
44
- path.split('.'), # path_components
98
+ variable_path_components,
45
99
  file_info,
46
100
  )
47
101
  end
48
-
49
- request.update_param('variables', variables_wrapper[:variables])
50
102
  end
51
103
 
52
- @app.call(env)
104
+ variables_wrapper[:variables]
53
105
  end
54
106
 
55
107
  private
56
108
 
57
- def pathset(h, path_components, val_h)
58
- if path_components.blank?
59
- h.merge!(val_h)
60
- return
109
+ def pathset(variables_h, path_components, file_info)
110
+ # Find the immediate parent, and then set file info on parent's field
111
+ # named path_components.last.
112
+
113
+ raise 'Not deep enough' unless path_components.size >= 2
114
+
115
+ h_or_a = variables_h
116
+ while path_components.size > 1
117
+ path_component = path_components.shift
118
+ if h_or_a.is_a?(Array)
119
+ h_or_a = h_or_a[path_component.to_i]
120
+ elsif h_or_a.is_a?(Hash)
121
+ h_or_a = h_or_a[path_component]
122
+ else
123
+ raise 'Unexpected type in variables path.'
124
+ end
61
125
  end
62
126
 
63
- curr_path_component = path_components.shift
64
- if h.key?(curr_path_component)
65
- raise 'Invalid path' unless h[curr_path_component].is_a?(Hash)
127
+ # Now the last path component is the field we want to set.
128
+ raise 'Bad path components.' unless path_components.size == 1
129
+ field_name = path_components.first
130
+
131
+ if h_or_a.is_a?(Hash)
132
+ h_or_a[field_name] = file_info
133
+ elsif h_or_a.is_a?(Array)
134
+ h_or_a[field_name.to_i] = file_info
66
135
  else
67
- h[curr_path_component] = {}
136
+ raise 'Unexpected type in variables path.'
68
137
  end
138
+ end
139
+
140
+ def validate_file_info(file_data)
141
+ [:filename, :tempfile, :type].each do |k|
142
+ raise "Missing key '#{k.to_s}' in file data." unless file_data[k].present?
143
+ end
144
+
145
+ raise 'Expected Tempfile in file_data[:tempfile]' unless file_data[:tempfile].is_a?(Tempfile)
69
146
 
70
- pathset(h[curr_path_component], path_components, val_h)
147
+ {
148
+ name: file_data[:filename],
149
+ path: file_data[:tempfile].path,
150
+ size: file_data[:tempfile].size,
151
+ type: file_data[:type],
152
+ }
71
153
  end
72
154
  end
73
155
  end
@@ -1,3 +1,3 @@
1
1
  module ApolloFetchUploadRailsMiddleware
2
- VERSION = '0.2.0.alpha'.freeze
2
+ VERSION = '1.0.0.alpha'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apollo_fetch_upload_rails_middleware
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0.alpha
4
+ version: 1.0.0.alpha
5
5
  platform: ruby
6
6
  authors:
7
7
  - Abe Land
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-11-24 00:00:00.000000000 Z
11
+ date: 2018-07-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -89,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
89
89
  version: 1.3.1
90
90
  requirements: []
91
91
  rubyforge_project:
92
- rubygems_version: 2.6.11
92
+ rubygems_version: 2.7.6
93
93
  signing_key:
94
94
  specification_version: 4
95
95
  summary: Rails middleware for using the apollo-fetch-upload npm package on the client.