apollo_fetch_upload_rails_middleware 0.2.0.alpha → 1.0.0.alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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.