active_graphql 0.2.4 → 0.3

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
2
  SHA256:
3
- metadata.gz: 398cdea1d95da44a0fce577bf1fc7764ba5461f4b32ee84116fe5a96d06b47af
4
- data.tar.gz: 0ba5f89799c23e30a5f602e2f33895a8fb4c0758a6b684e0e2af6d7d119eb118
3
+ metadata.gz: bba3575ea3683cf6d8df16cb0b42b1c48b60edd67f8d5119098ec05b9a291171
4
+ data.tar.gz: d315c4e5ac40e378c5c16bc940302de0f177e5ab18f4dbae3091b7527cb46233
5
5
  SHA512:
6
- metadata.gz: c57a57dbdf0e732153e1759966d4adc4c5270be4819908824aed3cbaccbd1510904c207e23ecf63b565c3f498dd074a221184b1c5a9a8457c94a788ea27c3eba
7
- data.tar.gz: a123889abdd1bbb88a520b2e033cafb4fec3f7d5e8e3e7c7fdcdc791ef4bfd42a78e30ec7827d4c2f7c43cb407d300564ce9327a11fc210cb3ddc90814c02f22
6
+ metadata.gz: af76a227b8eaafaac0ce607ae095a6ba847c0e192edb4c2e580dd43acba00753450c5100db504f6fe37c8fbbc5a2616cdf7c204914ae6db5e899acb71e311458
7
+ data.tar.gz: 264571fe765368fd250a5f4685b33a3c3fc6071c84be79c9bd486d87934ee43c72a658ecfdaceab4304b9f495400ec49053c321871fb64cf0ba76ae0e91306ba
@@ -1,11 +1,12 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- active_graphql (0.2.4)
4
+ active_graphql (0.3)
5
5
  activemodel (>= 3.0.0)
6
6
  activesupport (>= 4.0.0)
7
7
  graphlient (>= 0.3)
8
8
  graphql (>= 1.9.0)
9
+ mime-types (>= 3.0)
9
10
 
10
11
  GEM
11
12
  remote: https://rubygems.org/
@@ -55,6 +56,9 @@ GEM
55
56
  jaro_winkler (1.5.4)
56
57
  json (2.3.1)
57
58
  method_source (1.0.0)
59
+ mime-types (3.3.1)
60
+ mime-types-data (~> 3.2015)
61
+ mime-types-data (3.2020.1104)
58
62
  minitest (5.14.2)
59
63
  multipart-post (2.1.1)
60
64
  parallel (1.20.1)
@@ -129,4 +133,4 @@ DEPENDENCIES
129
133
  webmock (~> 3)
130
134
 
131
135
  BUNDLED WITH
132
- 2.0.1
136
+ 2.2.6
@@ -37,6 +37,7 @@ Gem::Specification.new do |spec|
37
37
  spec.add_dependency 'graphlient', '>= 0.3'
38
38
  spec.add_dependency 'activesupport', '>= 4.0.0'
39
39
  spec.add_dependency 'activemodel', '>= 3.0.0'
40
+ spec.add_dependency 'mime-types', '>= 3.0'
40
41
 
41
42
  spec.add_development_dependency "bundler", "~> 2.0"
42
43
  spec.add_development_dependency "webmock", "~> 3"
@@ -1,14 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_graphql/client/actions/variable_detectable'
4
+ require 'active_graphql/client/actions/action/format_outputs'
5
+ require 'active_graphql/client/actions/action/format_inputs'
6
+ require 'active_graphql/client/actions/action/format_variable_inputs'
7
+
3
8
  module ActiveGraphql
4
9
  class Client
5
10
  module Actions
6
11
  # Base class for query/mutation action objects
7
12
  class Action
8
- class InvalidActionError < StandardError; end
13
+ include VariableDetectable
9
14
 
10
- require 'active_graphql/client/actions/action/format_outputs'
11
- require 'active_graphql/client/actions/action/format_inputs'
15
+ class InvalidActionError < StandardError; end
12
16
 
13
17
  attr_reader :name, :type, :output_values, :client, :input_attributes, :meta_attributes
14
18
 
@@ -63,7 +67,7 @@ module ActiveGraphql
63
67
  assert_format
64
68
 
65
69
  <<~TXT
66
- #{type} {
70
+ #{type}#{wrapped_header formatted_variable_headers} {
67
71
  #{name}#{wrapped_header formatted_inputs} {
68
72
  #{formatted_outputs}
69
73
  }
@@ -71,6 +75,10 @@ module ActiveGraphql
71
75
  TXT
72
76
  end
73
77
 
78
+ def graphql_variables
79
+ variable_attributes(input_attributes)
80
+ end
81
+
74
82
  private
75
83
 
76
84
  def join_array_and_hash(*array, **hash)
@@ -85,6 +93,10 @@ module ActiveGraphql
85
93
  FormatOutputs.new(output_values).call
86
94
  end
87
95
 
96
+ def formatted_variable_headers
97
+ FormatVariableInputs.new(graphql_variables).call
98
+ end
99
+
88
100
  def assert_format
89
101
  return unless output_values.empty?
90
102
 
@@ -6,6 +6,8 @@ module ActiveGraphql
6
6
  class Action
7
7
  # converts ruby object in to grapqhl input string
8
8
  class FormatInputs
9
+ include VariableDetectable
10
+
9
11
  def initialize(inputs)
10
12
  @inputs = inputs
11
13
  end
@@ -20,9 +22,9 @@ module ActiveGraphql
20
22
 
21
23
  attr_reader :inputs
22
24
 
23
- def formatted(attributes)
25
+ def formatted(attributes, parent_keys: [])
24
26
  if attributes.is_a?(Hash)
25
- formatted_attributes(attributes)
27
+ formatted_attributes(attributes, parent_keys: parent_keys)
26
28
  else
27
29
  raise(
28
30
  ActiveGraphql::Client::Actions::WrongTypeError,
@@ -31,39 +33,45 @@ module ActiveGraphql
31
33
  end
32
34
  end
33
35
 
34
- def formatted_attributes(attributes)
36
+ def formatted_attributes(attributes, parent_keys: [])
35
37
  attributes = attributes.dup
36
38
  keyword_fields = (attributes.delete(:__keyword_attributes) || []).map(&:to_s)
37
39
 
38
40
  formatted_attributes = attributes.map do |key, val|
39
41
  if keyword_fields.include?(key.to_s)
40
- formatted_key_and_keyword(key, val)
42
+ formatted_key_and_keyword(key, val, parent_keys: parent_keys)
41
43
  else
42
- formatted_key_and_value(key, val)
44
+ formatted_key_and_value(key, val, parent_keys: parent_keys)
43
45
  end
44
46
  end
45
47
 
46
48
  formatted_attributes.join(', ')
47
49
  end
48
50
 
49
- def formatted_key_and_value(key, value)
50
- "#{key}: #{formatted_value(value)}"
51
+ def formatted_key_and_value(key, value, parent_keys:)
52
+ if variable_value?(value)
53
+ "#{key}: $#{[*parent_keys, key].compact.join('_')}"
54
+ else
55
+ "#{key}: #{formatted_value(value, parent_keys: [*parent_keys, key])}"
56
+ end
51
57
  end
52
58
 
53
- def formatted_key_and_keyword(key, value)
59
+ def formatted_key_and_keyword(key, value, parent_keys:)
54
60
  if value.is_a?(String) || value.is_a?(Symbol)
55
61
  "#{key}: #{value}"
56
62
  else
57
- "#{key}: #{formatted_value(value)}"
63
+ "#{key}: #{formatted_value(value, parent_keys: [*parent_keys, key])}"
58
64
  end
59
65
  end
60
66
 
61
- def formatted_value(value) # rubocop:disable Metrics/MethodLength
67
+ def formatted_value(value, parent_keys:) # rubocop:disable Metrics/MethodLength
62
68
  case value
63
69
  when Hash
64
- "{ #{formatted(value)} }"
70
+ "{ #{formatted(value, parent_keys: parent_keys)} }"
65
71
  when Array
66
- formatted_values = value.map { |it| formatted_value(it) }
72
+ formatted_values = value.map.with_index do |it, idx|
73
+ formatted_value(it, parent_keys: [*parent_keys, idx])
74
+ end
67
75
  "[#{formatted_values.join(', ')}]"
68
76
  when nil
69
77
  'null'
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveGraphql
4
+ class Client
5
+ module Actions
6
+ class Action
7
+ # converts ruby object in to varbiable stype grapqhl input
8
+ class FormatVariableInputs
9
+ include VariableDetectable
10
+
11
+ def initialize(inputs)
12
+ @initial_inputs = inputs
13
+ end
14
+
15
+ def call
16
+ return '' if inputs.empty?
17
+
18
+ formatted_attributes(inputs)
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :initial_inputs
24
+
25
+ def formatted_attributes(attributes)
26
+ attributes = attributes.dup
27
+ formatted_attributes = attributes.map do |key, val|
28
+ formatted_key_and_type(key, val)
29
+ end
30
+
31
+ formatted_attributes.join(', ')
32
+ end
33
+
34
+ def inputs
35
+ @inputs ||= variable_attributes(initial_inputs)
36
+ end
37
+
38
+ def formatted_key_and_type(key, value)
39
+ "$#{key}: #{formatted_type(value)}"
40
+ end
41
+
42
+ def formatted_type(value)
43
+ if value.is_a?(Array)
44
+ '[File!]!'
45
+ else
46
+ 'File!'
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveGraphql
4
+ class Client
5
+ module Actions
6
+ # handles all action details which are specific for query type request
7
+ module VariableDetectable
8
+ def variable_attributes(attributes)
9
+ variables_or_nil = attributes.transform_values do |value|
10
+ if value.is_a?(Hash)
11
+ variable_attributes(value)
12
+ elsif variable_value?(value)
13
+ value
14
+ elsif value.is_a?(Array)
15
+ variable_attributes(value.map.with_index { |val, i| [i, val] }.to_h)
16
+ end
17
+ end
18
+
19
+ flatten_keys(variables_or_nil).select { |_, val| val.present? }
20
+ end
21
+
22
+ def variable_value?(value)
23
+ kind_of_file?(value) || (value.is_a?(Array) && kind_of_file?(value.first))
24
+ end
25
+
26
+ private
27
+
28
+ def flatten_keys(attributes, parent_key: nil)
29
+ flattened = {}
30
+ attributes.each do |key, value|
31
+ full_key = [parent_key, key].compact.join('_').to_sym
32
+ if value.is_a?(Hash)
33
+ flattened.merge!(flatten_keys(value, parent_key: full_key))
34
+ else
35
+ flattened[full_key] = value
36
+ end
37
+ end
38
+ flattened
39
+ end
40
+
41
+ def kind_of_file?(value)
42
+ value.is_a?(File)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'mime/types'
5
+ require 'active_graphql/errors'
6
+
7
+ module ActiveGraphql
8
+ class Client
9
+ module Adapters
10
+ class NoMimeTypeException < ActiveGraphql::Errors::Error; end
11
+
12
+ # Converts deeply nested File instances to Faraday::UploadIO
13
+ class FormatMultipartVariables
14
+ def initialize(variables)
15
+ @variables = variables
16
+ end
17
+
18
+ def call
19
+ deep_transform_values(variables) do |variable|
20
+ variable_value(variable)
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :variables
27
+
28
+ def deep_transform_values(hash, &block)
29
+ return hash unless hash.is_a?(Hash)
30
+
31
+ hash.transform_values do |val|
32
+ if val.is_a?(Hash)
33
+ deep_transform_values(val, &block)
34
+ else
35
+ yield(val)
36
+ end
37
+ end
38
+ end
39
+
40
+ def variable_value(variable)
41
+ if variable.is_a?(Array)
42
+ variable.map { |it| variable_value(it) }
43
+ elsif variable.is_a?(Hash)
44
+ variable.transform_values { |it| variable_value(it) }
45
+ elsif variable.is_a?(File)
46
+ file_variable_value(variable)
47
+ else
48
+ variable
49
+ end
50
+ end
51
+
52
+ def file_variable_value(file)
53
+ content_type = MIME::Types.type_for(file.path).first
54
+ return Faraday::UploadIO.new(file.path, content_type) if content_type
55
+
56
+ raise NoMimeTypeException, "Unable to determine mime type for #{file.path}"
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -6,26 +6,37 @@ module ActiveGraphql
6
6
  # Client which makes raw API requests to GraphQL server
7
7
  class GraphlientAdapter
8
8
  require 'graphlient'
9
+ require_relative './graphlient_multipart_adapter'
9
10
 
10
11
  def initialize(config)
11
12
  @url = config[:url]
12
- @adapter_config = config.except(:url)
13
+ @config = config
13
14
  end
14
15
 
15
16
  def post(action)
16
- raw_response = graphql_client.query(action.to_graphql)
17
+ raw_response = graphql_client.query(action.to_graphql, action.graphql_variables)
17
18
  Response.new(raw_response.data)
18
19
  rescue Graphlient::Errors::GraphQLError => e
19
20
  Response.new(nil, e)
20
21
  end
21
22
 
23
+ def adapter_config
24
+ @adapter_config ||= config.except(:url, :multipart).tap do |new_config|
25
+ new_config[:http] = GraphlientMultipartAdapter if multipart?
26
+ end
27
+ end
28
+
22
29
  private
23
30
 
24
- attr_reader :url, :adapter_config
31
+ attr_reader :url, :config
25
32
 
26
33
  def graphql_client
27
34
  @graphql_client ||= Graphlient::Client.new(url, **adapter_config)
28
35
  end
36
+
37
+ def multipart?
38
+ config[:multipart].present?
39
+ end
29
40
  end
30
41
  end
31
42
  end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'graphlient'
4
+ require 'faraday'
5
+ require 'faraday/request/multipart'
6
+ require 'faraday_middleware'
7
+ require 'active_graphql/client/adapters/format_multipart_variables'
8
+
9
+ module ActiveGraphql
10
+ class Client
11
+ module Adapters
12
+ # Adapter enabling multipart data transfer
13
+ class GraphlientMultipartAdapter < Graphlient::Adapters::HTTP::Adapter
14
+ def execute(document:, operation_name:, variables:, context:)
15
+ response = execute_request(
16
+ document: document, operation_name: operation_name,
17
+ variables: variables, context: context
18
+ )
19
+ response.body
20
+ rescue Faraday::ClientError => e
21
+ raise Graphlient::Errors::FaradayServerError, e
22
+ end
23
+
24
+ private
25
+
26
+ def execute_request(document:, operation_name:, variables:, context:)
27
+ connection.post do |req|
28
+ req.headers.merge!(context[:headers] || {})
29
+ req.body = {
30
+ query: document.to_query_string,
31
+ operationName: operation_name,
32
+ variables: FormatMultipartVariables.new(variables).call
33
+ }
34
+ end
35
+ end
36
+
37
+ def connection
38
+ @connection ||= Faraday.new(url: url, headers: headers) do |c|
39
+ c.adapter Faraday::Response::RaiseError
40
+ c.request :multipart
41
+ c.request :url_encoded
42
+ c.response :json
43
+
44
+ block_given? ? yield(c) : c.adapter(Faraday::Adapter::NetHttp)
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -155,7 +155,10 @@ module ActiveGraphql
155
155
 
156
156
  def create(params)
157
157
  action_name = "create_#{active_graphql.resource_name}"
158
- response = exec_graphql { |api| api.mutation(action_name).input(params) }
158
+ response = exec_graphql do |api|
159
+ api.mutation(action_name).input(params)
160
+ end
161
+
159
162
  new(response.result.to_h).tap do |record|
160
163
  record.graphql_errors = response.detailed_errors if !response.success? || !record.valid?
161
164
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveGraphql
4
- VERSION = '0.2.4'
4
+ VERSION = '0.3'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_graphql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: '0.3'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Povilas Jurcys
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-25 00:00:00.000000000 Z
11
+ date: 2021-01-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: graphql
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: 3.0.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: mime-types
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '3.0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '3.0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: bundler
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -178,7 +192,7 @@ dependencies:
178
192
  - - ">="
179
193
  - !ruby/object:Gem::Version
180
194
  version: '0'
181
- description:
195
+ description:
182
196
  email:
183
197
  - po.jurcys@gmail.com
184
198
  executables: []
@@ -212,10 +226,14 @@ files:
212
226
  - lib/active_graphql/client/actions/action.rb
213
227
  - lib/active_graphql/client/actions/action/format_inputs.rb
214
228
  - lib/active_graphql/client/actions/action/format_outputs.rb
229
+ - lib/active_graphql/client/actions/action/format_variable_inputs.rb
215
230
  - lib/active_graphql/client/actions/mutation_action.rb
216
231
  - lib/active_graphql/client/actions/query_action.rb
232
+ - lib/active_graphql/client/actions/variable_detectable.rb
217
233
  - lib/active_graphql/client/adapters.rb
234
+ - lib/active_graphql/client/adapters/format_multipart_variables.rb
218
235
  - lib/active_graphql/client/adapters/graphlient_adapter.rb
236
+ - lib/active_graphql/client/adapters/graphlient_multipart_adapter.rb
219
237
  - lib/active_graphql/client/response.rb
220
238
  - lib/active_graphql/errors.rb
221
239
  - lib/active_graphql/model.rb
@@ -231,8 +249,8 @@ licenses:
231
249
  metadata:
232
250
  homepage_uri: https://github.com/samesystem/active_graphql
233
251
  source_code_uri: https://github.com/samesystem/active_graphql
234
- changelog_uri: https://github.com/samesystem/active_graphql/blob/v0.2.4/CHANGELOG.md
235
- post_install_message:
252
+ changelog_uri: https://github.com/samesystem/active_graphql/blob/v0.3/CHANGELOG.md
253
+ post_install_message:
236
254
  rdoc_options: []
237
255
  require_paths:
238
256
  - lib
@@ -247,8 +265,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
247
265
  - !ruby/object:Gem::Version
248
266
  version: '0'
249
267
  requirements: []
250
- rubygems_version: 3.0.3
251
- signing_key:
268
+ rubygems_version: 3.0.9
269
+ signing_key:
252
270
  specification_version: 4
253
271
  summary: Graphql client
254
272
  test_files: []