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

Parse and execute #25

Merged
merged 3 commits 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
20 changes: 14 additions & 6 deletions .rubocop_todo.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# 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 15:16:01 -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: 11
# Configuration parameters: CountComments, ExcludedMethods.
Metrics/BlockLength:
Max: 82
Max: 136

# Offense count: 13
# Offense count: 17
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Metrics/LineLength:
Expand All @@ -22,6 +22,13 @@ Metrics/LineLength:
Metrics/MethodLength:
Max: 12

# Offense count: 1
# Configuration parameters: EnforcedStyle, SupportedStyles.
# SupportedStyles: nested, compact
Style/ClassAndModuleChildren:
Exclude:
- 'spec/graphlient/static_client_query_spec.rb'

# Offense count: 5
Style/Documentation:
Exclude:
Expand All @@ -33,13 +40,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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@
* [#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).
* [#25](https://github.com/ashkan18/graphlient/pull/25): Added `client.parse` and `client.execute` to parse and execute queries separately - [@dblock](https://github.com/dblock).
* Your contribution here.

### 0.0.5 (10/5/2017)
Expand Down
122 changes: 119 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
[![Gem Version](https://badge.fury.io/rb/graphlient.svg)](https://badge.fury.io/rb/graphlient)
[![Build Status](https://travis-ci.org/ashkan18/graphlient.svg?branch=master)](https://travis-ci.org/ashkan18/graphlient)

A Ruby Client for consuming GraphQL-based APIs without all the messy strings.
A much friendlier Ruby client for consuming GraphQL-based APIs, without all the messy strings. Built on top of your usual [graphql-client](https://github.com/github/graphql-client).

## Installation

Expand Down Expand Up @@ -31,7 +31,7 @@ The schema is available automatically via `.schema`.
client.schema # GraphQL::Schema
```

Make queries with `query`, which gets a block for the query definition.
Make queries with `query`, which takes a block for the query definition.

```ruby
response = client.query do
Expand Down Expand Up @@ -71,9 +71,124 @@ 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
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
```

### Parse and Execute Queries Separately

You can `parse` and `execute` queries separately with optional variables. This is highly recommended as parsing a query and validating a query on every request adds performance overhead. Parsing queries early allows validation errors to be discovered before request time and avoids many potential security issues.


```ruby
# parse a query, returns a GraphQL::Client::OperationDefinition
query = client.parse do
query(:$ids => :'[Int]') do
invoices(ids: :$ids) do
id
fee_in_cents
end
end
end

# execute a query, returns a GraphQL::Client::Response
client.execute query, ids: [42]
```

### Dynamic vs. Static Queries

Graphlient uses [graphql-client](https://github.com/github/graphql-client), which [recommends](https://github.com/github/graphql-client/blob/master/guides/dynamic-query-error.md) building queries as static module members along with dynamic variables during execution. This can be accomplished with graphlient the same way.

Create a new instance of `Graphlient::Client` with a URL and optional headers.

```ruby
module SWAPI
Client = Graphlient::Client.new('https://test-graphql.biz/graphql',
headers: {
'Authorization' => 'Bearer 123'
},
allow_dynamic_queries: false
)
end
```

The schema is available automatically via `.schema`.

```ruby
SWAPI::Client.schema # GraphQL::Schema
```

Define a query.

```ruby
module SWAPI
InvoiceQuery = Client.parse do
query(:$id => :Int) do
invoice(id: :$id) do
id
fee_in_cents
end
end
end
end
```

Execute the query.

```ruby
response = SWAPI::Client.execute(SWAPI::InvoiceQuery, id: 42)
```

Note that in the example above the client is created with `allow_dynamic_queries: false` (only allow static queries), while graphlient defaults to `allow_dynamic_queries: true` (allow dynamic queries). This option is marked deprecated, but we're proposing to remove it and default it to `true` in [graphql-client#128](https://github.com/github/graphql-client/issues/128).

### 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 Expand Up @@ -150,3 +265,4 @@ end
## License

MIT License, see [LICENSE](LICENSE)

23 changes: 18 additions & 5 deletions lib/graphlient/client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,24 @@ def initialize(url, options = {}, &_block)
yield self if block_given?
end

def query(&block)
def parse(&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)
client.parse(query_str.to_s)
end

def execute(query, variables = nil)
query_params = {}
query_params[:context] = @options if @options
query_params[:variables] = variables if variables
client.query(query, query_params)
rescue GraphQL::Client::Error => e
raise Graphlient::Errors::Client.new(e.message, e)
end

def query(variables = nil, &block)
execute(parse(&block), variables)
rescue GraphQL::Client::Error => e
raise Graphlient::Errors::Client.new(e.message, e)
end
Expand All @@ -33,7 +44,9 @@ def schema
private

def client
@client ||= GraphQL::Client.new(schema: schema, execute: http)
@client ||= GraphQL::Client.new(schema: schema, execute: http).tap do |client|
client.allow_dynamic_queries = @options.key?(:allow_dynamic_queries) ? options[:allow_dynamic_queries] : true
end
end
end
end
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
Loading