Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added support for parameterized queries and mutations. #20

Merged
merged 1 commit into from
Oct 20, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2017-10-18 19:18:40 -0400 using RuboCop version 0.47.1.
# on 2017-10-19 10:07:56 -0400 using RuboCop version 0.47.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.

# Offense count: 7
# Offense count: 8
# Configuration parameters: CountComments, ExcludedMethods.
Metrics/BlockLength:
Max: 82
Max: 88

# Offense count: 13
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Metrics/LineLength:
Max: 177

# Offense count: 2
# Offense count: 3
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 12
Expand All @@ -33,13 +33,14 @@ Style/Documentation:
- 'lib/graphlient/extensions/query.rb'
- 'lib/graphlient/query.rb'

# Offense count: 1
# Offense count: 2
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: line_count_dependent, lambda, literal
Style/Lambda:
Exclude:
- 'spec/support/queries/root_query.rb'
- 'spec/support/mutations/create_invoice_mutation.rb'
- 'spec/support/queries/query.rb'

# Offense count: 2
Style/MethodMissing:
Expand Down
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* [#21](https://github.com/ashkan18/graphlient/pull/21): Added danger, PR linter - [@dblock](https://github.com/dblock).
* [#17](https://github.com/ashkan18/graphlient/pull/17): Enable customizing of Faraday middleware - [@dblock](https://github.com/dblock).
* [#19](https://github.com/ashkan18/graphlient/pull/19): Expose `client.schema` - [@dblock](https://github.com/dblock).

* [#20](https://github.com/ashkan18/graphlient/pull/20): Added support for parameterized queries and mutations - [@dblock](https://github.com/dblock).
* Your contribution here.

### 0.0.5 (10/5/2017)
Expand Down
51 changes: 50 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,58 @@ A successful response object always contains data which can be iterated upon. Th
response.data.invoice.line_items.first.price
```

You can also execute mutations the same way.

```ruby
response = client.query do
mutation do
createInvoice(input: { fee_in_cents: 12_345 }) do
id
fee_in_cents
end
end
end
```

The successful response contains data in `response.data`. The following example returns the newly created invoice's ID.

```ruby
response.data.create_invoice.first.id
```

### Executing Parameterized Queries and Mutations

Graphlient can execute parameterized queries and mutations by providing variables as query parameters.

The following query accepts an array of IDs.

```ruby
client.query(ids: [42]) do
query(:$ids => :'[Int]') do
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

oo interesting approach, I wonder if we can make this slightly more DSLy by doing things like:

client.query(ids: [42]) do
  query(:ids => [:int]) do
   ....
   end
end

This means on our end we need to detect query variables and add $ to them when generating query. What do you think?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, opened #23 for a future improvement.

invoices(ids: :$ids) do
id
fee_in_cents
end
end
end
```

The following mutation accepts a custom type that requires `fee_in_cents`.

```ruby
client.query(input: { fee_in_cents: 12_345 }) do
mutation(:$input => :createInvoiceInput!) do
createInvoice(input: :$input) do
id
fee_in_cents
end
end
end
```

### Generate Queries with Graphlient::Query

You can directly use `Graphlient::Query` to generate GraphQL queries.
You can directly use `Graphlient::Query` to generate raw GraphQL queries.

```ruby
query = Graphlient::Query.new do
Expand Down
7 changes: 5 additions & 2 deletions lib/graphlient/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ def initialize(url, options = {}, &_block)
yield self if block_given?
end

def query(&block)
def query(variables = nil, &block)
query_str = Graphlient::Query.new do
instance_eval(&block)
end
parsed_query = client.parse(query_str.to_s)
client.allow_dynamic_queries = true
client.query(parsed_query, context: @options)
query_params = {}
query_params[:context] = @options if @options
query_params[:variables] = variables if variables
client.query(parsed_query, query_params)
rescue GraphQL::Client::Error => e
raise Graphlient::Errors::Client.new(e.message, e)
end
Expand Down
14 changes: 1 addition & 13 deletions lib/graphlient/query.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,12 @@ module Graphlient
class Query
attr_accessor :query_str

ACTIONS = %w(query mutation subscription fragment).freeze

def initialize(&block)
@indents = 0
@query_str = ''
instance_eval(&block)
end

ACTIONS.each do |action|
define_method(action.to_sym) do |&block|
@query_str << "#{action}{"
@indents += 1
instance_eval(&block)
@indents -= 1
@query_str << '}'
end
end

def method_missing(m, *args, &block)
append(m, args, &block)
end
Expand All @@ -29,7 +17,7 @@ def respond_to_missing?(m, include_private = false)
end

def to_s
query_str
query_str.strip
end

private
Expand Down
70 changes: 67 additions & 3 deletions spec/graphlient/client_query_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@
end
end
end.to raise_error Graphlient::Errors::Client do |e|
expect(e.to_s).to eq "Field 'invoice' doesn't exist on type 'RootQuery'"
expect(e.to_s).to eq "Field 'invoice' doesn't exist on type 'Query'"
end
end

it 'returns expected response with block' do
it 'returns a response from a query' do
response = client.query do
query do
invoices(ids: [10]) do
Expand All @@ -31,9 +31,73 @@
end

invoices = response.data.invoices
expect(invoices.first.id).to eq '1231'
expect(invoices.first.id).to eq 10
expect(invoices.first.fee_in_cents).to eq 20_000
end

it 'returns a response from a mutation' do
response = client.query do
mutation do
createInvoice(input: { fee_in_cents: 12_345 }) do
id
fee_in_cents
end
end
end

invoice = response.data.create_invoice.first
expect(invoice.id).to eq 1231
expect(invoice.fee_in_cents).to eq 12_345
end
end

context 'parameterized query' do
it 'fails when missing input' do
response = client.query do
mutation('$input' => :createInvoiceInput!) do
createInvoice(input: :$input) do
id
fee_in_cents
end
end
end

expect(response.errors.messages['data']).to eq(
[
'Variable input of type createInvoiceInput! was provided invalid value'
]
)
end

it 'returns a response from a query' do
response = client.query(ids: [42]) do
query(:$ids => :'[Int]') do
invoices(ids: :$ids) do
id
fee_in_cents
end
end
end

invoices = response.data.invoices
expect(invoices.first.id).to eq 42
expect(invoices.first.fee_in_cents).to eq 20_000
end

it 'executes the mutation' do
response = client.query(input: { fee_in_cents: 12_345 }) do
mutation(:$input => :createInvoiceInput!) do
createInvoice(input: :$input) do
id
fee_in_cents
end
end
end

invoice = response.data.create_invoice.first
expect(invoice.id).to eq 1231
expect(invoice.fee_in_cents).to eq 12_345
end
end
end
end
20 changes: 13 additions & 7 deletions spec/support/dummy_app.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,17 @@
end

post '/graphql' do
headers['Content-Type'] = 'application/json'
DummySchema.execute(
params[:query],
variables: params[:variables] ? JSON.parse(params[:variables]) : {},
context: {},
operation_name: params[:operationName]
).to_json
begin
headers['Content-Type'] = 'application/json'
DummySchema.execute(
params[:query],
variables: params[:variables] ? JSON.parse(params[:variables]) : {},
context: {},
operation_name: params[:operationName]
).to_json
rescue StandardError => e
warn e
warn e.backtrace.join("\n")
raise e
end
end
8 changes: 5 additions & 3 deletions spec/support/dummy_schema.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
require_relative 'queries/root_query'
require_relative 'types/invoice_type'

require_relative 'queries/query'
require_relative 'mutations/mutation'

DummySchema = GraphQL::Schema.define do
query RootQuery
max_depth 5
query Query
mutation Mutation
end
16 changes: 16 additions & 0 deletions spec/support/mutations/create_invoice_mutation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
CreateInvoiceMutation = GraphQL::Relay::Mutation.define do
name 'createInvoice'

input_field :fee_in_cents, !types.Int

return_type types[InvoiceType]

resolve ->(_object, inputs, _ctx) {
[
OpenStruct.new(
id: 1231,
fee_in_cents: inputs[:fee_in_cents]
)
]
}
end
7 changes: 7 additions & 0 deletions spec/support/mutations/mutation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
require_relative './create_invoice_mutation'

Mutation = GraphQL::ObjectType.define do
name 'Mutation'

field :createInvoice, field: CreateInvoiceMutation.field
end
16 changes: 16 additions & 0 deletions spec/support/queries/query.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Query = GraphQL::ObjectType.define do
name 'Query'

field :invoices, types[InvoiceType] do
argument :ids, types[types.Int]
description 'Find Invoices'
resolve ->(_obj, args, _ctx) {
args[:ids].map do |id|
OpenStruct.new(
id: id,
fee_in_cents: 20_000
)
end
}
end
end
17 changes: 0 additions & 17 deletions spec/support/queries/root_query.rb

This file was deleted.

2 changes: 1 addition & 1 deletion spec/support/types/invoice_type.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
InvoiceType = GraphQL::ObjectType.define do
name 'Invoice'
description 'An Invoice'
field :id, !types.ID
field :id, !types.Int
field :fee_in_cents, types.Int
end