active_graphql 0.2.4 → 0.3

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
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: []