Skip to content

Commit

Permalink
Add client level keyword support
Browse files Browse the repository at this point in the history
  • Loading branch information
povilasjurcys committed Dec 10, 2024
1 parent e6827cf commit a5bd491
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 40 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

* Added/Changed/Deprecated/Removed/Fixed/Security: YOUR CHANGE HERE
* Added: automatic conversion from symbols to keywords setting
* Added: relation.empty?, relation.blank?, relation.present? methods
* Added: fields now can be marked as "keywords"
* Added: it's possible to set order for relation
Expand Down
3 changes: 2 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ GEM
byebug (~> 11.0)
pry (>= 0.13, < 0.15)
public_suffix (5.0.0)
racc (1.8.1)
rainbow (3.1.1)
rake (13.0.6)
regexp_parser (2.6.1)
Expand Down Expand Up @@ -126,8 +127,8 @@ DEPENDENCIES
activerecord
bundler (~> 2.0)
graphql_rails (>= 2.2.0)
pry
pry-byebug
racc
rake (~> 13.0)
rspec (~> 3.4)
rubocop (= 1.39.0)
Expand Down
3 changes: 2 additions & 1 deletion active_graphql.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ Gem::Specification.new do |spec|

spec.add_development_dependency "bundler", "~> 2.0"
spec.add_development_dependency "webmock", "~> 3"
spec.add_development_dependency "pry"
spec.add_development_dependency "pry-byebug"
spec.add_development_dependency "racc"
spec.add_development_dependency "rake", "~> 13.0"
spec.add_development_dependency "rspec", "~> 3.4"
spec.add_development_dependency "rubocop", "1.39.0"
Expand Down
26 changes: 25 additions & 1 deletion docs/client.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,36 @@ to initialize graphql client, simply create new client instance with url:
client = ActiveGraphql::Client.new(url: 'http://example.com/graphql')
```

you can also provide extra options which will be accepted by addapter, like this:
you can also provide extra options which will be accepted by adapter, like this:

```ruby
client = ActiveGraphql::Client.new(url: 'http://example.com/graphql', headers: {}, schema_path: '...')
```

### `treat_symbol_as_keyword` option

By default, ActiveGraphql converts all String/Symbol values to GraphQL strings. This creates a challenge when working with GraphQL Enums. To bypass this issue, you can pass `treat_symbol_as_keyword` option so symbol values will be converted to GraphQL keywords (enum values).

```ruby
default_client = ActiveGraphql::Client.new(url: 'http://example.com/graphql')
default_client.query(:users).select(:name).(status: :ACTIVE).to_graphql
# =>
# query {
# users(status: "ACTIVE") {
# status
# }
# }

client = ActiveGraphql::Client.new(url: 'http://example.com/graphql', treat_symbol_as_keyword: true)
client.query(:users).select(:name).(status: :ACTIVE).to_graphql
# =>
# query {
# users(status: ACTIVE) {
# status
# }
# }


## query and mutation actions

```ruby
Expand Down
6 changes: 2 additions & 4 deletions lib/active_graphql/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ class Client
require 'active_graphql/client/adapters'
require 'active_graphql/client/response'

attr_reader :config

def initialize(config)
@config = config.dup
@adapter_class = @config.delete(:adapter)
Expand All @@ -30,9 +32,5 @@ def adapter
adapter_builder.new(config)
end
end

private

attr_reader :config
end
end
2 changes: 1 addition & 1 deletion lib/active_graphql/client/actions/action.rb
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def join_array_and_hash(*array, **hash)
end

def formatted_inputs
FormatInputs.new(input_attributes).call
FormatInputs.new(input_attributes, client:).call
end

def formatted_outputs
Expand Down
64 changes: 39 additions & 25 deletions lib/active_graphql/client/actions/action/format_inputs.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,13 @@ module ActiveGraphql
class Client
module Actions
class Action
# converts ruby object in to grapqhl input string
# converts ruby object in to graphql input string
class FormatInputs
include VariableDetectable

def initialize(inputs)
def initialize(inputs, client:)
@inputs = inputs
@client = client
end

def call
Expand All @@ -20,7 +21,11 @@ def call

private

attr_reader :inputs
attr_reader :inputs, :client

def treat_symbol_as_keyword?
client.config[:treat_symbol_as_keyword]
end

def formatted(attributes, parent_keys: [])
if attributes.is_a?(Hash)
Expand All @@ -34,25 +39,25 @@ def formatted(attributes, parent_keys: [])
end

def formatted_attributes(attributes, parent_keys: [])
attributes = attributes.dup
keyword_fields = (attributes.delete(:__keyword_attributes) || []).map(&:to_s)

formatted_attributes = attributes.map do |key, val|
if keyword_fields.include?(key.to_s)
formatted_key_and_keyword(key, val, parent_keys:)
else
formatted_key_and_value(key, val, parent_keys:)
end
dup_attributes = attributes.dup
keyword_fields = (dup_attributes.delete(:__keyword_attributes) || []).map(&:to_s)

global_keyword_classes = treat_symbol_as_keyword? ? [Symbol] : []

formatted_attributes = dup_attributes.map do |key, val|
keyword_classes = keyword_fields.include?(key.to_s) ? [Symbol, String] : global_keyword_classes
formatted_key_and_value(key, val, parent_keys:, keyword_classes:)
end

formatted_attributes.join(', ')
end

def formatted_key_and_value(key, value, parent_keys:)
def formatted_key_and_value(key, value, parent_keys:, keyword_classes:)
if variable_value?(value)
"#{key}: $#{[*parent_keys, key].compact.join('_')}"
else
"#{key}: #{formatted_value(value, parent_keys: [*parent_keys, key])}"
formatted_value = formatted_value_for(value, parent_keys: [*parent_keys, key], keyword_classes:)
"#{key}: #{formatted_value}"
end
end

Expand All @@ -64,23 +69,32 @@ def formatted_key_and_keyword(key, value, parent_keys:)
end
end

def formatted_value(value, parent_keys:) # rubocop:disable Metrics/MethodLength
case value
when Hash
"{ #{formatted(value, parent_keys:)} }"
when Array
formatted_values = value.map.with_index do |it, idx|
formatted_value(it, parent_keys: [*parent_keys, idx])
end
"[#{formatted_values.join(', ')}]"
when nil
def formatted_value_for(value, parent_keys:, keyword_classes:) # rubocop:disable Metrics/MethodLength
if value.is_a?(Hash)
formatted_hash_value_for(value, parent_keys:)
elsif value.is_a?(Array)
formatted_array_value_for(value, parent_keys:, keyword_classes:)
elsif value.nil?
'null'
when Symbol
elsif keyword_classes.any? { |klass| value.is_a?(klass) }
value.to_s
elsif value.is_a?(Symbol)
value.to_s.inspect
else
value.inspect
end
end

def formatted_array_value_for(value, parent_keys:, keyword_classes:)
formatted_values = value.map.with_index do |it, idx|
formatted_value_for(it, parent_keys: [*parent_keys, idx], keyword_classes:)
end
"[#{formatted_values.join(', ')}]"
end

def formatted_hash_value_for(value, parent_keys:)
"{ #{formatted(value, parent_keys:)} }"
end
end
end
end
Expand Down
4 changes: 3 additions & 1 deletion lib/active_graphql/client/adapters/graphlient_adapter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ class GraphlientAdapter
require_relative './graphql_client_patch'
require_relative './graphlient_multipart_adapter'

attr_reader :config

def initialize(config)
@url = config[:url]
@config = config
Expand All @@ -29,7 +31,7 @@ def adapter_config

private

attr_reader :url, :config
attr_reader :url

def graphql_client
@graphql_client ||= Graphlient::Client.new(url, **adapter_config)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
require 'spec_helper'

RSpec.describe ActiveGraphql::Client::Actions::Action::FormatInputs do
subject(:format_inputs) { described_class.new(inputs) }
subject(:format_inputs) { described_class.new(inputs, client:) }

let(:inputs) { {} }
let(:client) { ActiveGraphql::Client::Adapters::GraphlientAdapter.new(client_config)}
let(:client_config) { { url: 'http://example.com/graphql' } }

describe '#call' do
subject(:call) { format_inputs.call }
Expand All @@ -17,8 +19,18 @@
context 'when value is symbol' do
let(:inputs) { { val: :yes } }

it 'convers Symbol values to strings' do
expect(call).to eq 'val: "yes"'
context 'when treat_symbol_as_keyword is false or not set' do
it 'converts Symbol values to strings' do
expect(call).to eq 'val: "yes"'
end
end

context 'when treat_symbol_as_keyword is true' do
let(:client_config) { super().merge(treat_symbol_as_keyword: true) }

it 'converts Symbol values to keywords' do
expect(call).to eq 'val: yes'
end
end
end

Expand Down Expand Up @@ -85,7 +97,7 @@
context 'when value is symbol' do
let(:inputs) { { val: :YES, __keyword_attributes: [:val] } }

it 'convers Symbol values to strings' do
it 'converts Symbol values to strings' do
expect(call).to eq 'val: YES'
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def to_h(*)
end

let(:action_name) { 'createAccessToken' }
let(:action_client) { instance_double(Adapters::GraphlientAdapter, post: response) }
let(:action_client) { instance_double(Adapters::GraphlientAdapter, post: response, config: {}) }
let(:response) { Response.new(result, error) }
let(:email) { '[email protected]' }
let(:error) { nil }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
let(:initial_action) { described_class.new(name:, client: authenticator_client) }
let(:name) { :findUser }
let(:authenticator_client) do
instance_double(ActiveGraphql::Client::Adapters::GraphlientAdapter, post: response_mock)
instance_double(ActiveGraphql::Client::Adapters::GraphlientAdapter, post: response_mock, config: {})
end
let(:response_mock) { instance_double(ActiveGraphql::Client::Response, result: nil) }

Expand Down

0 comments on commit a5bd491

Please sign in to comment.