hq-graphql 0.0.2

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: be9de36cb5b0f12696e98d85156404d89acdc6d5
4
+ data.tar.gz: 0e887aabc0d14374ad192e464a8b1e3e4ac4ea8f
5
+ SHA512:
6
+ metadata.gz: 7ec2755f98cd6045e93da317edec985a3a148c4baef5a7e30d24c1568020327f455d4a7efb14b117d482c0b51106e69ad67f0db33c6051c8b42d750f951086c8
7
+ data.tar.gz: aae1f46e971c88db6a4e87d919dc354e0235931041bf485172df0109520892f1e0294a10b99c81943ca71132b92ed1bfd56f76fdda5d2cb0e3b9e9323bc56665
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2018 Danny Jones
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,55 @@
1
+ # HQ::GraphQL
2
+
3
+ OneHQ GraphQL interface to [Ruby Graphql](https://github.com/rmosolgo/graphql-ruby).
4
+
5
+ [![CircleCI](https://img.shields.io/circleci/project/github/OneHQ/hq-graphql.svg)](https://circleci.com/gh/OneHQ/hq-graphql/tree/master)
6
+ [![GitHub tag](https://img.shields.io/github/tag/OneHQ/hq-graphql.svg)](https://github.com/OneHQ/hq-graphql)
7
+
8
+ ## Configuration
9
+
10
+ You can pass configuration options as a block to `::HQ::GraphQL.configure`.
11
+
12
+ ```ruby
13
+ # The gem assumes that if your model is called `MyModel`, the corresponding type is `MyModelType`.
14
+ # You can override that convention.
15
+ # Default is: -> (model_class) { "#{model_class.name.demodulize}Type" }
16
+ ::HQ::GraphQL.config do |config|
17
+ config.model_to_graphql_type = -> (model_class) { "::CustomNameSpace::#{model_class.name}Type" }
18
+ end
19
+ ```
20
+
21
+ ## Usage
22
+
23
+ Create a new ::HQ::GraphQL::Object
24
+ ```ruby
25
+ class AdvisorType < ::HQ::GraphQL::Object
26
+ # Supports graphql-ruby functionality
27
+ field :id, Int, null: false
28
+
29
+ # Lazy Loading
30
+ # Useful for loading data from the database to generate a schema
31
+ lazy_load do
32
+ load_data_from_db.each do |name|
33
+ field name, String, null: false
34
+ end
35
+ end
36
+
37
+ # Attach the GraphQL object to an ActiveRecord Model
38
+ # First argument is the string form of your ActiveRecord model.
39
+ #
40
+ # attributes:
41
+ # Set it to false if you don't want to auto-include your model's attributes.
42
+ # Defaults to true.
43
+ #
44
+ # associations:
45
+ # Set it to false if you don't want to auto-include your model's associations.
46
+ # Defaults to true.
47
+ with_model "Advisor", attributes: true, associations: false
48
+
49
+ # Remove attributes that were included by `with_model`
50
+ remove_attrs :id, :created_at, :updated_at
51
+
52
+ # Remove associations that were included by `with_model`
53
+ remove_associations :organization, :created_by
54
+ end
55
+ ```
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'HQ::GraphQL'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.rdoc')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ require 'bundler/gem_tasks'
data/lib/hq-graphql.rb ADDED
@@ -0,0 +1 @@
1
+ require "hq/graphql"
data/lib/hq/graphql.rb ADDED
@@ -0,0 +1,32 @@
1
+ require "graphql"
2
+
3
+ module HQ
4
+ module GraphQL
5
+ def self.config
6
+ @config ||= ::ActiveSupport::OrderedOptions.new
7
+ end
8
+
9
+ def self.configure(&block)
10
+ config.instance_eval(&block)
11
+ end
12
+
13
+ # The gem assumes that if your model is called `MyModel`, the corresponding type is `MyModelType`.
14
+ # You can override that convention.
15
+ #
16
+ # ::HQ::GraphQL.config do |config|
17
+ # config.model_to_graphql_type = -> (model_class) { "::CustomNameSpace::#{model_class.name}Type" }
18
+ # end
19
+ def self.model_to_graphql_type
20
+ config.model_to_graphql_type ||
21
+ @model_to_graphql_type ||= -> (model_class) { "#{model_class.name.demodulize}Type" }
22
+ end
23
+
24
+ def self.graphql_type_from_model(model_class)
25
+ model_to_graphql_type.call(model_class)
26
+ end
27
+ end
28
+ end
29
+
30
+ require "hq/graphql/object"
31
+ require "hq/graphql/types"
32
+ require "hq/graphql/engine"
@@ -0,0 +1,6 @@
1
+ module HQ
2
+ module GraphQL
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,117 @@
1
+ module HQ
2
+ module GraphQL
3
+ class Object < ::GraphQL::Schema::Object
4
+ class Error < StandardError
5
+ MISSING_MODEL_MSG = "Can't perform %{action} without connecting to a model: `::HQ::GraphQL::Object.with_model 'User'`".freeze
6
+ MISSING_ATTR_MSG = "Can't find attr %{model}.%{attr}'`".freeze
7
+ MISSING_ASSOC_MSG = "Can't find association %{model}.%{assoc}'`".freeze
8
+ end
9
+
10
+ def self.lazy_load(&block)
11
+ @lazy_load ||= []
12
+ @lazy_load << block if block
13
+ @lazy_load
14
+ end
15
+
16
+ def self.with_model(model_name, attributes: true, associations: true)
17
+ self.model_name = model_name
18
+
19
+ lazy_load do
20
+ if attributes
21
+ model_klass.columns.reject { |c| removed_attrs.include?(c.name.to_sym) }.each do |column|
22
+ field_from_column column
23
+ end
24
+ end
25
+
26
+ if associations
27
+ model_klass.reflect_on_all_associations.reject { |a| removed_associations.include?(a.name.to_sym) }.each do |association|
28
+ field_from_association association
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ def self.add_attr(attr)
35
+ lazy_load do
36
+ validate_model!(:add_attr)
37
+ field_from_column model_column(attr)
38
+ end
39
+ end
40
+
41
+ def self.remove_attrs(*attrs)
42
+ removed_attrs.concat attrs.map(&:to_sym)
43
+ end
44
+
45
+ def self.remove_associations(*associations)
46
+ removed_associations.concat associations.map(&:to_sym)
47
+ end
48
+
49
+ def self.add_association(association)
50
+ lazy_load do
51
+ validate_model!(:add_association)
52
+ field_from_association model_association(association)
53
+ end
54
+ end
55
+
56
+ def self.to_graphql
57
+ lazy_load!
58
+ super
59
+ end
60
+
61
+ class << self
62
+ private
63
+
64
+ attr_accessor :model_name
65
+ attr_writer :removed_attrs, :removed_associations
66
+
67
+ def lazy_load!
68
+ lazy_load.map(&:call)
69
+ @lazy_load = []
70
+ end
71
+
72
+ def model_klass
73
+ @model_klass ||= model_name&.constantize
74
+ end
75
+
76
+ def model_column(attr)
77
+ model_klass.columns_hash[attr.to_s] || raise(Error, Error::MISSING_ATTR_MSG % { model: model_name, attr: attr })
78
+ end
79
+
80
+ def model_association(association)
81
+ model_klass.reflect_on_association(association) || raise(Error, Error::MISSING_ASSOC_MSG % { model: model_name, assoc: association })
82
+ end
83
+
84
+ def removed_attrs
85
+ @removed_attrs ||= []
86
+ end
87
+
88
+ def removed_associations
89
+ @removed_associations ||= []
90
+ end
91
+
92
+ def validate_model!(action)
93
+ unless model_name
94
+ raise Error, Error::MISSING_MODEL_MSG % { action: action }
95
+ end
96
+ end
97
+
98
+ def field_from_association(association)
99
+ name = association.name
100
+ type = ::HQ::GraphQL::Types[association.klass]
101
+ case association.macro
102
+ when :has_many
103
+ field name, [type], null: false
104
+ else
105
+ field name, type, null: true
106
+ end
107
+ end
108
+
109
+ def field_from_column(column)
110
+ field column.name, ::HQ::GraphQL::Types.type_from_column(column), null: column.null
111
+ end
112
+
113
+ end
114
+
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,55 @@
1
+ require "hq/graphql/types/uuid"
2
+
3
+ module HQ
4
+ module GraphQL
5
+ module Types
6
+
7
+ def self.[](key)
8
+ @schema_objects ||= Hash.new do |hash, klass|
9
+ hash[klass] = klass_for(klass)
10
+ end
11
+ @schema_objects[key]
12
+ end
13
+
14
+ def self.type_from_column(column)
15
+ case column&.cast_type&.type
16
+ when :uuid
17
+ ::HQ::GraphQL::Types::UUID
18
+ when :integer
19
+ ::GraphQL::INT_TYPE
20
+ when :decimal
21
+ ::GraphQL::FLOAT_TYPE
22
+ when :boolean
23
+ ::GraphQL::BOOLEAN_TYPE
24
+ else
25
+ ::GraphQL::STRING_TYPE
26
+ end
27
+ end
28
+
29
+ # Only being used in testing
30
+ def self.reset!
31
+ @schema_objects = nil
32
+ end
33
+
34
+ class << self
35
+ private
36
+
37
+ def klass_for(klass)
38
+ hql_klass_name = ::HQ::GraphQL.graphql_type_from_model(klass)
39
+ hql_klass = hql_klass_name.safe_constantize
40
+ return hql_klass if hql_klass
41
+
42
+ module_name = hql_klass_name.deconstantize.presence
43
+ hql_module = module_name ? (module_name.safe_constantize || ::Object.const_set(module_name, Module.new)) : ::Object
44
+
45
+ hql_klass = Class.new(::HQ::GraphQL::Object) do
46
+ with_model klass.name
47
+ end
48
+
49
+ hql_module.const_set(hql_klass_name.demodulize, hql_klass)
50
+ end
51
+ end
52
+
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,33 @@
1
+ module HQ
2
+ module GraphQL
3
+ module Types
4
+ class UUID < ::GraphQL::Schema::Scalar
5
+ description "UUID"
6
+
7
+ class << self
8
+ def self.coerce_input(value, context)
9
+ validate_and_return_uuid(value)
10
+ end
11
+
12
+ def self.coerce_result(value, context)
13
+ validate_and_return_uuid(value)
14
+ end
15
+
16
+ private
17
+
18
+ def validate_and_return_uuid(value)
19
+ if validate_uuid(value)
20
+ value
21
+ else
22
+ raise ::GraphQL::CoercionError, "#{value.inspect} is not a valid UUID"
23
+ end
24
+ end
25
+
26
+ def validate_uuid(value)
27
+ !!value.match(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,5 @@
1
+ module HQ
2
+ module GraphQL
3
+ VERSION = "0.0.2"
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ FactoryBot.define do
2
+ factory :advisor do
3
+ name { Faker::Name.name }
4
+ organization { FactoryBot.build(:organization) }
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ FactoryBot.define do
2
+ factory :organization do
3
+ name { Faker::Company.name }
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ FactoryBot.define do
2
+ factory :user do
3
+ name { Faker::Name.name }
4
+ organization { FactoryBot.build(:organization) }
5
+ end
6
+ end
@@ -0,0 +1,18 @@
1
+ class Query < GraphQL::Schema::Object
2
+ graphql_name "Query"
3
+
4
+ field :users, [HQ::GraphQL::Types[User]], null: false
5
+ field :advisors, [HQ::GraphQL::Types[Advisor]], null: false
6
+
7
+ def users
8
+ User.all
9
+ end
10
+
11
+ def users_custom
12
+ users
13
+ end
14
+
15
+ def advisors
16
+ Advisor.all
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ class Schema < GraphQL::Schema
2
+ query(Query)
3
+ end
@@ -0,0 +1,3 @@
1
+ class UserType < HQ::GraphQL::Object
2
+ with_model "User"
3
+ end
@@ -0,0 +1,3 @@
1
+ class Advisor < ActiveRecord::Base
2
+ belongs_to :organization
3
+ end
@@ -0,0 +1,3 @@
1
+ class Organization < ::ActiveRecord::Base
2
+ has_many :users, inverse_of: :organization
3
+ end
@@ -0,0 +1,3 @@
1
+ class User < ::ActiveRecord::Base
2
+ belongs_to :organization, inverse_of: :users
3
+ end
@@ -0,0 +1,24 @@
1
+ common: &common
2
+ adapter: postgresql
3
+ encoding: utf8
4
+ host: <%= ENV["DBHOST"] || "127.0.0.1" %>
5
+ port: <%= ENV["DBPORT"] || "5432" %>
6
+ database: <%= ENV["DBNAME"] || "hq-graphql" %>
7
+ username: <%= ENV["DBUSER"] || "postgres" %>
8
+ password: <%= ENV["DBPASSWORD"] || "" %>
9
+ pool: 5
10
+ reconnect: true
11
+ timeout: 5000
12
+
13
+ development:
14
+ <<: *common
15
+
16
+ test:
17
+ <<: *common
18
+
19
+ staging:
20
+ <<: *common
21
+
22
+ production:
23
+ <<: *common
24
+ pool: 10
@@ -0,0 +1,8 @@
1
+ test:
2
+ adapter: postgresql
3
+ database: <%= ENV['DB'] || 'hq-graphql_test' %>
4
+ username: <%= ENV['DB_USERNAME'] || %x[id -un] %> # `id -un` returns the active user in OSX (at least as of 10.10)
5
+ password:
6
+ timeout: 5000
7
+ encoding: utf8
8
+ pool: 25
@@ -0,0 +1,21 @@
1
+ ActiveRecord::Schema.define do
2
+ enable_extension "uuid-ossp"
3
+
4
+ create_table "organizations", force: true, id: :uuid do |t|
5
+ t.string :name, limit: 63, null: false
6
+ t.timestamps null: false
7
+ end
8
+
9
+ create_table "users", force: true do |t|
10
+ t.belongs_to :organization, null: false, index: true, foreign_key: true, type: :uuid
11
+ t.string :name, null: false
12
+ t.timestamps null: false
13
+ end
14
+
15
+ create_table "advisors", force: true do |t|
16
+ t.references :organization, null: false, index: true, foreign_key: true, type: :uuid
17
+ t.string :name, null: false
18
+ t.timestamps null: false
19
+ end
20
+
21
+ end
@@ -0,0 +1,198 @@
1
+ require 'rails_helper'
2
+
3
+ describe ::HQ::GraphQL::Object do
4
+
5
+ describe ".lazy_load" do
6
+ let(:lazy_load_class) do
7
+ Class.new(described_class) do
8
+ graphql_name "LazyLoadQuery"
9
+
10
+ @counter = 0
11
+
12
+ lazy_load do
13
+ @counter += 1
14
+ end
15
+
16
+ def self.counter
17
+ @counter
18
+ end
19
+ end
20
+ end
21
+
22
+ it "lazy loads once" do
23
+ # First time it works
24
+ expect { lazy_load_class.to_graphql }.to change { lazy_load_class.counter }.by(1)
25
+ # Second time it does nothing
26
+ expect { lazy_load_class.to_graphql }.to change { lazy_load_class.counter }.by(0)
27
+ end
28
+ end
29
+
30
+ describe ".with_model" do
31
+ let(:hql_object_klass) do
32
+ Class.new(described_class) do
33
+ graphql_name "TestQuery"
34
+ end
35
+ end
36
+
37
+ it "adds everything by default" do
38
+ hql_object_klass.class_eval do
39
+ with_model "Advisor"
40
+ end
41
+
42
+ expect(hql_object_klass.fields.keys).to be_empty
43
+ hql_object_klass.to_graphql
44
+ expected = ["createdAt", "id", "name", "organization", "organizationId", "updatedAt"]
45
+ expect(hql_object_klass.fields.keys).to contain_exactly(*expected)
46
+ end
47
+
48
+ it "removes an attribute" do
49
+ hql_object_klass.class_eval do
50
+ remove_attrs :created_at, :id, :organization_id
51
+ with_model "Advisor"
52
+ end
53
+
54
+ expect(hql_object_klass.fields.keys).to be_empty
55
+ hql_object_klass.to_graphql
56
+ expected = ["name", "organization", "updatedAt"]
57
+ expect(hql_object_klass.fields.keys).to contain_exactly(*expected)
58
+ end
59
+
60
+ it "removes an association" do
61
+ hql_object_klass.class_eval do
62
+ remove_associations :organization, :doesntexist
63
+ with_model "Advisor"
64
+ end
65
+
66
+ expect(hql_object_klass.fields.keys).to be_empty
67
+ hql_object_klass.to_graphql
68
+ expected = ["createdAt", "id", "name", "organizationId", "updatedAt"]
69
+ expect(hql_object_klass.fields.keys).to contain_exactly(*expected)
70
+ end
71
+
72
+ context "with attributes and associations turned off" do
73
+ it "doesn't have any fields by default" do
74
+ hql_object_klass.to_graphql
75
+ expect(hql_object_klass.fields.keys).to be_empty
76
+ end
77
+
78
+ it "doesn't have any fields when disabling model attrs/associations" do
79
+ hql_object_klass.class_eval do
80
+ with_model "Advisor", attributes: false, associations: false
81
+ end
82
+ hql_object_klass.to_graphql
83
+ expect(hql_object_klass.fields.keys).to be_empty
84
+ end
85
+
86
+ it "blows up when adding an attribute to an object without a model" do
87
+ hql_object_klass.class_eval do
88
+ add_attr :name
89
+ end
90
+
91
+ expect { hql_object_klass.to_graphql }.to raise_error(described_class::Error)
92
+ end
93
+
94
+ it "blows up when adding an attribute that doesn't exist" do
95
+ hql_object_klass.class_eval do
96
+ add_attr :doesnt_exist
97
+
98
+ with_model "Advisor", attributes: false, associations: false
99
+ end
100
+
101
+ expect { hql_object_klass.to_graphql }.to raise_error(described_class::Error)
102
+ end
103
+
104
+ it "adds attributes once connected to a model" do
105
+ hql_object_klass.class_eval do
106
+ # Order shouldn't matter....but let's test it anyway
107
+
108
+ # First
109
+ add_attr :name
110
+
111
+ # Second
112
+ with_model "Advisor", attributes: false, associations: false
113
+ end
114
+
115
+ expect(hql_object_klass.fields.keys).to be_empty
116
+ hql_object_klass.to_graphql
117
+ expect(hql_object_klass.fields.keys).to contain_exactly("name")
118
+ end
119
+
120
+ it "blows up when adding an association to an object without a model" do
121
+ hql_object_klass.class_eval do
122
+ add_association :organization
123
+ end
124
+
125
+ expect { hql_object_klass.to_graphql }.to raise_error(described_class::Error)
126
+ end
127
+
128
+ it "blows up when adding an association that doesn't exist" do
129
+ hql_object_klass.class_eval do
130
+ add_association :doesnt_exist
131
+
132
+ with_model "Advisor", attributes: false, associations: false
133
+ end
134
+
135
+ expect { hql_object_klass.to_graphql }.to raise_error(described_class::Error)
136
+ end
137
+
138
+ it "adds associations once connected to a model" do
139
+ hql_object_klass.class_eval do
140
+ # Order shouldn't matter....but let's test it anyway
141
+
142
+ # First
143
+ add_association :organization
144
+
145
+ # Second
146
+ with_model "Advisor", attributes: false, associations: false
147
+ end
148
+
149
+ expect(hql_object_klass.fields.keys).to be_empty
150
+ hql_object_klass.to_graphql
151
+ expect(hql_object_klass.fields.keys).to contain_exactly("organization")
152
+ end
153
+
154
+ end
155
+
156
+ context "with a schema" do
157
+ let(:user_1) { FactoryBot.create(:user) }
158
+ let(:user_2) { FactoryBot.create(:user, organization: user_1.organization) }
159
+
160
+ before(:each) do
161
+ user_1
162
+ user_2
163
+ end
164
+
165
+ it "executes graphql" do
166
+ query_str = <<-GRAPHQ
167
+ query {
168
+ users {
169
+ name
170
+ organizationId
171
+
172
+ organization {
173
+ name
174
+ }
175
+ }
176
+ }
177
+ GRAPHQ
178
+
179
+ result = ::Schema.execute(query_str)
180
+
181
+ aggregate_failures do
182
+ user_1_data, user_2_data = result["data"]["users"]
183
+ # User 1
184
+ expect(user_1_data["name"]).to eql(user_1.name)
185
+ expect(user_1_data["organizationId"]).to eql(user_1.organization_id)
186
+ expect(user_1_data["organization"]["name"]).to eql(user_1.organization.name)
187
+
188
+ # User 2
189
+ expect(user_2_data["name"]).to eql(user_2.name)
190
+ expect(user_2_data["organizationId"]).to eql(user_2.organization_id)
191
+ expect(user_2_data["organization"]["name"]).to eql(user_2.organization.name)
192
+ end
193
+ end
194
+ end
195
+
196
+ end
197
+
198
+ end
@@ -0,0 +1,60 @@
1
+ require 'rails_helper'
2
+
3
+ describe ::HQ::GraphQL::Types do
4
+
5
+ describe ".[]" do
6
+ before(:each) do
7
+ described_class.reset!
8
+ end
9
+
10
+ after(:each) do
11
+ described_class.reset!
12
+ end
13
+
14
+ it "generates a new class with columns & associations" do
15
+ # Unique Classes
16
+ allow(::HQ::GraphQL).to receive(:model_to_graphql_type) { -> (model_class) { "#{model_class.name.demodulize}TypeTest" } }
17
+
18
+ advisor_klass = described_class[Advisor]
19
+ user_klass = described_class[User]
20
+ advisor_user_fields = ["id", "organizationId", "name", "createdAt", "updatedAt", "organization"]
21
+
22
+ #### Advisor Schema ####
23
+ expect(advisor_klass.name).to eql(::HQ::GraphQL.graphql_type_from_model(Advisor))
24
+ expect(advisor_klass.fields.keys).to be_empty
25
+ ### Build GraphQL schema
26
+ advisor_klass.to_graphql
27
+ expect(advisor_klass.fields.keys).to contain_exactly(*advisor_user_fields)
28
+
29
+ #### User Schema ####
30
+ expect(user_klass.name).to eql(::HQ::GraphQL.graphql_type_from_model(User))
31
+ expect(user_klass.fields.keys).to be_empty
32
+ user_klass.to_graphql
33
+ expect(user_klass.fields.keys).to contain_exactly(*advisor_user_fields)
34
+
35
+ organization_klass = ::HQ::GraphQL.graphql_type_from_model(Organization).constantize
36
+ expect(organization_klass.fields.keys).to be_empty
37
+ organization_klass.to_graphql
38
+ organization_fields = ["id", "name", "createdAt", "updatedAt", "users"]
39
+ expect(organization_klass.fields.keys).to contain_exactly(*organization_fields)
40
+ end
41
+
42
+ it "users the existing class if one exists" do
43
+ allow(::HQ::GraphQL).to receive(:model_to_graphql_type) { -> (model_class) { "#{model_class.name.demodulize}TypeTestTwo" } }
44
+
45
+ klass = Class.new(::HQ::GraphQL::Object) do
46
+ graphql_name "TestQuery"
47
+
48
+ field :custom_field, String, null: false
49
+ end
50
+ advisor_klass = stub_const(::HQ::GraphQL.graphql_type_from_model(Advisor), klass)
51
+
52
+ expect(advisor_klass).to eql(described_class[Advisor])
53
+ expect(advisor_klass.fields.keys).to contain_exactly("customField")
54
+ advisor_klass.to_graphql
55
+ expect(advisor_klass.fields.keys).to contain_exactly("customField")
56
+ end
57
+
58
+ end
59
+
60
+ end
@@ -0,0 +1,41 @@
1
+ require "testhq/coverage" # Needs to be require at the top of this file!
2
+ # Coverage must also be enabled in the environment by setting
3
+ # environment variables, e.g. `COVERAGE=true` or `CODECLIMATE_REPO_TOKEN=...`.
4
+ # See https://github.com/OneHQ/testhq#code-coverage.
5
+
6
+ require "bundler/setup"
7
+ require "combustion"
8
+
9
+ silence_stream(STDOUT) do # Hides a lot of output from Combustion init such as schema loading.
10
+ Combustion.initialize! :all do
11
+ # Disable strong parameters
12
+ config.action_controller.permit_all_parameters = true
13
+ end
14
+ end
15
+
16
+ require "byebug"
17
+ require "rspec/rails"
18
+ require "testhq"
19
+
20
+ RSpec.configure do |config|
21
+ config.expect_with :rspec do |c|
22
+ c.syntax = [:expect]
23
+ end
24
+
25
+ config.use_transactional_fixtures = false
26
+
27
+ config.include FactoryBot::Syntax::Methods
28
+
29
+ config.before(:suite) do
30
+ DatabaseCleaner.strategy = :transaction
31
+ DatabaseCleaner.clean_with :truncation # Clear everything
32
+ end
33
+
34
+ config.before(:each) do
35
+ DatabaseCleaner.start
36
+ end
37
+
38
+ config.after(:each) do
39
+ DatabaseCleaner.clean
40
+ end
41
+ end
@@ -0,0 +1,22 @@
1
+ RSpec.configure do |config|
2
+ config.expect_with :rspec do |expectations|
3
+ # This option will default to `true` in RSpec 4. It makes the `description`
4
+ # and `failure_message` of custom matchers include text for helper methods
5
+ # defined using `chain`, e.g.:
6
+ # be_bigger_than(2).and_smaller_than(4).description
7
+ # # => "be bigger than 2 and smaller than 4"
8
+ # ...rather than:
9
+ # # => "be bigger than 2"
10
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
11
+ end
12
+
13
+ # rspec-mocks config goes here. You can use an alternate test double
14
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
15
+ config.mock_with :rspec do |mocks|
16
+ # Prevents you from mocking or stubbing a method that does not exist on
17
+ # a real object. This is generally recommended, and will default to
18
+ # `true` in RSpec 4.
19
+ mocks.verify_partial_doubles = true
20
+ end
21
+ config.order = :random
22
+ end
metadata ADDED
@@ -0,0 +1,160 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hq-graphql
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Danny Jones
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-08-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.2'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '4.2'
27
+ - !ruby/object:Gem::Dependency
28
+ name: graphql
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.0'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 1.8.7
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '1.0'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 1.8.7
47
+ - !ruby/object:Gem::Dependency
48
+ name: rspec_junit_formatter
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.3'
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 0.3.0
57
+ type: :development
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '0.3'
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 0.3.0
67
+ - !ruby/object:Gem::Dependency
68
+ name: testhq
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '1.0'
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: 1.0.0
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '1.0'
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: 1.0.0
87
+ description: OneHQ GraphQL Library
88
+ email:
89
+ - dpjones09@gmail.com
90
+ executables: []
91
+ extensions: []
92
+ extra_rdoc_files: []
93
+ files:
94
+ - MIT-LICENSE
95
+ - README.md
96
+ - Rakefile
97
+ - lib/hq-graphql.rb
98
+ - lib/hq/graphql.rb
99
+ - lib/hq/graphql/engine.rb
100
+ - lib/hq/graphql/object.rb
101
+ - lib/hq/graphql/types.rb
102
+ - lib/hq/graphql/types/uuid.rb
103
+ - lib/hq/graphql/version.rb
104
+ - spec/factories/advisors.rb
105
+ - spec/factories/organizations.rb
106
+ - spec/factories/users.rb
107
+ - spec/internal/app/graphql/query.rb
108
+ - spec/internal/app/graphql/schema.rb
109
+ - spec/internal/app/graphql/user_type.rb
110
+ - spec/internal/app/models/advisor.rb
111
+ - spec/internal/app/models/organization.rb
112
+ - spec/internal/app/models/user.rb
113
+ - spec/internal/config/database.circleci.yml
114
+ - spec/internal/config/database.yml
115
+ - spec/internal/db/schema.rb
116
+ - spec/lib/object_spec.rb
117
+ - spec/lib/types_spec.rb
118
+ - spec/rails_helper.rb
119
+ - spec/spec_helper.rb
120
+ homepage: https://github.com/OneHQ/hq-graphql
121
+ licenses:
122
+ - MIT
123
+ metadata: {}
124
+ post_install_message:
125
+ rdoc_options: []
126
+ require_paths:
127
+ - lib
128
+ required_ruby_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ requirements: []
139
+ rubyforge_project:
140
+ rubygems_version: 2.5.2.2
141
+ signing_key:
142
+ specification_version: 4
143
+ summary: OneHQ GraphQL Library
144
+ test_files:
145
+ - spec/spec_helper.rb
146
+ - spec/internal/app/models/advisor.rb
147
+ - spec/internal/app/models/organization.rb
148
+ - spec/internal/app/models/user.rb
149
+ - spec/internal/app/graphql/schema.rb
150
+ - spec/internal/app/graphql/query.rb
151
+ - spec/internal/app/graphql/user_type.rb
152
+ - spec/internal/config/database.circleci.yml
153
+ - spec/internal/config/database.yml
154
+ - spec/internal/db/schema.rb
155
+ - spec/factories/organizations.rb
156
+ - spec/factories/advisors.rb
157
+ - spec/factories/users.rb
158
+ - spec/lib/object_spec.rb
159
+ - spec/lib/types_spec.rb
160
+ - spec/rails_helper.rb