graphql 0.0.4 → 0.1.0

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.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graph_ql/directive.rb +36 -0
  3. data/lib/graph_ql/directives/directive_chain.rb +33 -0
  4. data/lib/graph_ql/directives/include_directive.rb +15 -0
  5. data/lib/graph_ql/directives/skip_directive.rb +15 -0
  6. data/lib/graph_ql/enum.rb +34 -0
  7. data/lib/graph_ql/fields/abstract_field.rb +37 -0
  8. data/lib/graph_ql/fields/access_field.rb +24 -0
  9. data/lib/graph_ql/fields/field.rb +34 -0
  10. data/lib/graph_ql/interface.rb +14 -0
  11. data/lib/graph_ql/introspection/arguments_field.rb +5 -0
  12. data/lib/graph_ql/introspection/directive_type.rb +12 -0
  13. data/lib/graph_ql/introspection/enum_value_type.rb +10 -0
  14. data/lib/graph_ql/introspection/enum_values_field.rb +15 -0
  15. data/lib/graph_ql/introspection/field_type.rb +11 -0
  16. data/lib/graph_ql/introspection/fields_field.rb +14 -0
  17. data/lib/graph_ql/introspection/input_fields_field.rb +12 -0
  18. data/lib/graph_ql/introspection/input_value_type.rb +10 -0
  19. data/lib/graph_ql/introspection/of_type_field.rb +12 -0
  20. data/lib/graph_ql/introspection/possible_types_field.rb +12 -0
  21. data/lib/graph_ql/introspection/schema_type.rb +32 -0
  22. data/lib/graph_ql/introspection/type_kind_enum.rb +7 -0
  23. data/lib/graph_ql/introspection/type_type.rb +22 -0
  24. data/lib/graph_ql/parser/nodes.rb +72 -0
  25. data/lib/graph_ql/parser/parser.rb +108 -0
  26. data/lib/graph_ql/parser/transform.rb +86 -0
  27. data/lib/graph_ql/parser/visitor.rb +47 -0
  28. data/lib/graph_ql/query.rb +50 -0
  29. data/lib/graph_ql/query/arguments.rb +25 -0
  30. data/lib/graph_ql/query/field_resolution_strategy.rb +83 -0
  31. data/lib/graph_ql/query/fragment_spread_resolution_strategy.rb +16 -0
  32. data/lib/graph_ql/query/inline_fragment_resolution_strategy.rb +14 -0
  33. data/lib/graph_ql/query/operation_resolver.rb +28 -0
  34. data/lib/graph_ql/query/selection_resolver.rb +20 -0
  35. data/lib/graph_ql/query/type_resolver.rb +19 -0
  36. data/lib/graph_ql/repl.rb +27 -0
  37. data/lib/graph_ql/schema.rb +30 -0
  38. data/lib/graph_ql/schema/type_reducer.rb +44 -0
  39. data/lib/graph_ql/type_kinds.rb +15 -0
  40. data/lib/graph_ql/types/abstract_type.rb +14 -0
  41. data/lib/graph_ql/types/boolean_type.rb +6 -0
  42. data/lib/graph_ql/types/float_type.rb +6 -0
  43. data/lib/graph_ql/types/input_object_type.rb +17 -0
  44. data/lib/graph_ql/types/input_value.rb +10 -0
  45. data/lib/graph_ql/types/int_type.rb +6 -0
  46. data/lib/graph_ql/types/list_type.rb +10 -0
  47. data/lib/graph_ql/types/non_null_type.rb +18 -0
  48. data/lib/graph_ql/types/non_null_with_bang.rb +5 -0
  49. data/lib/graph_ql/types/object_type.rb +62 -0
  50. data/lib/graph_ql/types/scalar_type.rb +5 -0
  51. data/lib/graph_ql/types/string_type.rb +6 -0
  52. data/lib/graph_ql/types/type_definer.rb +16 -0
  53. data/lib/graph_ql/union.rb +35 -0
  54. data/lib/graph_ql/validations/fields_are_defined_on_type.rb +44 -0
  55. data/lib/graph_ql/validations/fields_will_merge.rb +80 -0
  56. data/lib/graph_ql/validations/fragments_are_used.rb +24 -0
  57. data/lib/graph_ql/validator.rb +29 -0
  58. data/lib/graph_ql/version.rb +3 -0
  59. data/lib/graphql.rb +92 -99
  60. data/readme.md +17 -177
  61. data/spec/graph_ql/directive_spec.rb +81 -0
  62. data/spec/graph_ql/enum_spec.rb +5 -0
  63. data/spec/graph_ql/fields/field_spec.rb +10 -0
  64. data/spec/graph_ql/interface_spec.rb +13 -0
  65. data/spec/graph_ql/introspection/directive_type_spec.rb +40 -0
  66. data/spec/graph_ql/introspection/schema_type_spec.rb +39 -0
  67. data/spec/graph_ql/introspection/type_type_spec.rb +104 -0
  68. data/spec/graph_ql/parser/parser_spec.rb +120 -0
  69. data/spec/graph_ql/parser/transform_spec.rb +109 -0
  70. data/spec/graph_ql/parser/visitor_spec.rb +31 -0
  71. data/spec/graph_ql/query/operation_resolver_spec.rb +14 -0
  72. data/spec/graph_ql/query_spec.rb +82 -0
  73. data/spec/graph_ql/schema/type_reducer_spec.rb +24 -0
  74. data/spec/graph_ql/types/input_object_type_spec.rb +12 -0
  75. data/spec/graph_ql/types/object_type_spec.rb +35 -0
  76. data/spec/graph_ql/union_spec.rb +27 -0
  77. data/spec/graph_ql/validations/fields_are_defined_on_type_spec.rb +28 -0
  78. data/spec/graph_ql/validations/fields_will_merge_spec.rb +40 -0
  79. data/spec/graph_ql/validations/fragments_are_used_spec.rb +28 -0
  80. data/spec/graph_ql/validator_spec.rb +24 -0
  81. data/spec/spec_helper.rb +2 -2
  82. data/spec/support/dummy_app.rb +123 -63
  83. data/spec/support/dummy_data.rb +11 -0
  84. metadata +107 -59
  85. data/lib/graphql/call.rb +0 -8
  86. data/lib/graphql/connection.rb +0 -65
  87. data/lib/graphql/field.rb +0 -12
  88. data/lib/graphql/field_definer.rb +0 -25
  89. data/lib/graphql/introspection/call_type.rb +0 -13
  90. data/lib/graphql/introspection/connection.rb +0 -9
  91. data/lib/graphql/introspection/field_type.rb +0 -10
  92. data/lib/graphql/introspection/root_call_argument_node.rb +0 -5
  93. data/lib/graphql/introspection/root_call_type.rb +0 -20
  94. data/lib/graphql/introspection/schema_call.rb +0 -8
  95. data/lib/graphql/introspection/schema_type.rb +0 -17
  96. data/lib/graphql/introspection/type_call.rb +0 -8
  97. data/lib/graphql/introspection/type_type.rb +0 -18
  98. data/lib/graphql/node.rb +0 -244
  99. data/lib/graphql/parser/parser.rb +0 -39
  100. data/lib/graphql/parser/transform.rb +0 -22
  101. data/lib/graphql/query.rb +0 -109
  102. data/lib/graphql/root_call.rb +0 -202
  103. data/lib/graphql/root_call_argument.rb +0 -11
  104. data/lib/graphql/root_call_argument_definer.rb +0 -17
  105. data/lib/graphql/schema/all.rb +0 -46
  106. data/lib/graphql/schema/schema.rb +0 -87
  107. data/lib/graphql/schema/schema_validation.rb +0 -32
  108. data/lib/graphql/syntax/call.rb +0 -8
  109. data/lib/graphql/syntax/field.rb +0 -9
  110. data/lib/graphql/syntax/fragment.rb +0 -7
  111. data/lib/graphql/syntax/node.rb +0 -8
  112. data/lib/graphql/syntax/query.rb +0 -8
  113. data/lib/graphql/syntax/variable.rb +0 -7
  114. data/lib/graphql/types/boolean_type.rb +0 -3
  115. data/lib/graphql/types/number_type.rb +0 -3
  116. data/lib/graphql/types/object_type.rb +0 -6
  117. data/lib/graphql/types/string_type.rb +0 -3
  118. data/lib/graphql/version.rb +0 -3
  119. data/spec/graphql/node_spec.rb +0 -69
  120. data/spec/graphql/parser/parser_spec.rb +0 -168
  121. data/spec/graphql/parser/transform_spec.rb +0 -157
  122. data/spec/graphql/query_spec.rb +0 -274
  123. data/spec/graphql/root_call_spec.rb +0 -69
  124. data/spec/graphql/schema/schema_spec.rb +0 -93
  125. data/spec/graphql/schema/schema_validation_spec.rb +0 -48
  126. data/spec/support/nodes.rb +0 -175
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Visitor do
4
+ let(:document) { GraphQL.parse("
5
+ query cheese { cheese(id: 1) { flavor, source, producers(first: 3) { name } } }
6
+ fragment cheeseFields on Cheese { flavor }
7
+ ")}
8
+ let(:counts) { {fields_entered: 0, arguments_entered: 0, arguments_left: 0, argument_names: []} }
9
+
10
+ let(:visitor) do
11
+ v = GraphQL::Visitor.new
12
+ v[GraphQL::Nodes::Field] << -> (node) { counts[:fields_entered] += 1 }
13
+ # two ways to set up enter hooks:
14
+ v[GraphQL::Nodes::Argument] << -> (node) { counts[:argument_names] << node.name }
15
+ v[GraphQL::Nodes::Argument].enter << -> (node) { counts[:arguments_entered] += 1}
16
+ v[GraphQL::Nodes::Argument].leave << -> (node) { counts[:arguments_left] += 1 }
17
+
18
+ v[GraphQL::Nodes::Document].leave << -> (node) { counts[:finished] = true }
19
+ v
20
+ end
21
+
22
+ it 'calls hooks during a depth-first tree traversal' do
23
+ assert_equal(2, visitor[GraphQL::Nodes::Argument].enter.length)
24
+ visitor.visit(document)
25
+ assert_equal(6, counts[:fields_entered])
26
+ assert_equal(2, counts[:arguments_entered])
27
+ assert_equal(2, counts[:arguments_left])
28
+ assert_equal(["id", "first"], counts[:argument_names])
29
+ assert(counts[:finished])
30
+ end
31
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Query::OperationResolver do
4
+ let(:operation) { GraphQL.parse("query getCheese($cheeseId: Int!) { cheese(id: $cheeseId) { name }}", as: :operation_definition) }
5
+ let(:params) { {"cheeseId" => 1}}
6
+ let(:query) { q = Minitest::Mock.new; q.expect(:params, params); q}
7
+ let(:resolver) { GraphQL::Query::OperationResolver.new(operation, query)}
8
+
9
+ describe "variables" do
10
+ it 'returns variables by name' do
11
+ assert_equal(1, resolver.variables["cheeseId"])
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Query do
4
+ describe '#execute' do
5
+ let(:query_string) { %|
6
+ query getFlavor($cheeseId: Int!) {
7
+ brie: cheese(id: 1) { ...cheeseFields, ... meatFields, taste: flavor },
8
+ cheese(id: $cheeseId) {
9
+ __typename,
10
+ id,
11
+ ...cheeseFields,
12
+ ... edibleFields,
13
+ ... on Cheese { cheeseKind: flavor },
14
+ ... on Meat { cut }
15
+ }
16
+ fromSource(source: COW) { id }
17
+ firstSheep: searchDairy(product: {source: SHEEP}) { ... dairyFields }
18
+ favoriteEdible { __typename, fatContent }
19
+ }
20
+ fragment cheeseFields on Cheese { flavor }
21
+ fragment edibleFields on Edible { fatContent }
22
+ fragment meatFields on Meat { cut }
23
+ fragment dairyFields on DairyProduct {
24
+ ... on Cheese { flavor }
25
+ ... on Milk { source }
26
+ }
27
+ |}
28
+ let(:query) { GraphQL::Query.new(DummySchema, query_string, context: {}, params: {"cheeseId" => 2})}
29
+ let(:result) { query.result }
30
+ it 'returns fields on objects' do
31
+ expected = {"data"=> { "getFlavor" => {
32
+ "brie" => { "flavor" => "Brie", "taste" => "Brie" },
33
+ "cheese" => {
34
+ "__typename" => "Cheese",
35
+ "id" => 2,
36
+ "fatContent" => 0.3,
37
+ "flavor" => "Gouda",
38
+ "cheeseKind" => "Gouda",
39
+ },
40
+ "fromSource" => [{ "id" => 1 }, {"id" => 2}],
41
+ "firstSheep" => { "flavor" => "Manchego" },
42
+ "favoriteEdible"=>{"__typename"=>"Edible", "fatContent"=>0.04},
43
+ }}}
44
+ assert_equal(expected, result)
45
+ end
46
+
47
+ it 'exposes fragments' do
48
+ assert_equal(GraphQL::Nodes::FragmentDefinition, query.fragments['cheeseFields'].class)
49
+ end
50
+
51
+ describe 'runtime errors' do
52
+ let(:query_string) {%| query noMilk { milk(id: 1000) { name } }|}
53
+ it 'turns into error messages' do
54
+ expected = {"errors"=>[
55
+ {"message"=>"Something went wrong during query execution: No field found on Query 'Query' for 'milk'"}
56
+ ]}
57
+ assert_equal(expected, result)
58
+ end
59
+ end
60
+
61
+
62
+ describe 'execution order' do
63
+ let(:query_string) {%|
64
+ mutation setInOrder {
65
+ first: pushValue(value: 1)
66
+ second: pushValue(value: 5)
67
+ third: pushValue(value: 2)
68
+ }
69
+ |}
70
+ it 'executes mutations in order' do
71
+ expected = {"data"=>{
72
+ "setInOrder"=>{
73
+ "first"=> [1],
74
+ "second"=>[1, 5],
75
+ "third"=> [1, 5, 2],
76
+ }
77
+ }}
78
+ assert_equal(expected, result)
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Schema::TypeReducer do
4
+ it 'finds types from a single type and its fields' do
5
+ reducer = GraphQL::Schema::TypeReducer.new(CheeseType, {})
6
+ expected = {
7
+ "Cheese" => CheeseType,
8
+ "Int" => GraphQL::INT_TYPE,
9
+ "String" => GraphQL::STRING_TYPE,
10
+ "DairyAnimal" => DairyAnimalEnum,
11
+ "Float" => GraphQL::FLOAT_TYPE,
12
+ "Edible" => Edible,
13
+ "Milk" => MilkType,
14
+ "AnimalProduct" => AnimalProduct,
15
+ }
16
+ assert_equal(expected.keys, reducer.result.keys)
17
+ assert_equal(expected, reducer.result)
18
+ end
19
+
20
+ it 'finds type from arguments' do
21
+ reducer = GraphQL::Schema::TypeReducer.new(QueryType, {})
22
+ assert_equal(DairyProductInputType, reducer.result["DairyProductInput"])
23
+ end
24
+ end
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::InputObjectType do
4
+ let(:input_object) { DairyProductInputType }
5
+ it 'has a description' do
6
+ assert(input_object.description)
7
+ end
8
+
9
+ it 'has input fields' do
10
+ assert(DairyProductInputType.input_fields["fatContent"])
11
+ end
12
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::ObjectType do
4
+ let(:type) { CheeseType }
5
+
6
+ it 'has a name' do
7
+ assert_equal("Cheese", type.name)
8
+ type.name("Fromage")
9
+ assert_equal("Fromage", type.name)
10
+ type.name("Cheese")
11
+ end
12
+
13
+ it 'has a description' do
14
+ assert_equal(22, type.description.length)
15
+ end
16
+
17
+ it 'may have interfaces' do
18
+ assert_equal([Edible, AnimalProduct], type.interfaces)
19
+ end
20
+
21
+ it 'becomes non-null with !' do
22
+ non_null_type = !type
23
+ assert_equal(GraphQL::TypeKinds::NON_NULL, non_null_type.kind)
24
+ assert_equal(type, non_null_type.of_type)
25
+ assert_equal(GraphQL::TypeKinds::NON_NULL, (!GraphQL::STRING_TYPE).kind)
26
+ end
27
+
28
+ describe '.fields ' do
29
+ it 'exposes fields' do
30
+ field = type.fields["id"]
31
+ assert_equal(GraphQL::TypeKinds::NON_NULL, field.type.kind)
32
+ assert_equal(GraphQL::TypeKinds::SCALAR, field.type.of_type.kind)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Union do
4
+ let(:type_1) { OpenStruct.new(kind: GraphQL::TypeKinds::OBJECT)}
5
+ let(:type_2) { OpenStruct.new(kind: GraphQL::TypeKinds::OBJECT)}
6
+ let(:union) { GraphQL::Union.new("MyUnion", [type_1, type_2]) }
7
+ it 'has a name' do
8
+ assert_equal("MyUnion", union.name)
9
+ end
10
+
11
+ it 'identifies members' do
12
+ assert(union.include?(type_1))
13
+ assert(!union.include?(:type_3))
14
+ end
15
+
16
+ it 'must be 2+ types' do
17
+ assert_raises(ArgumentError) { GraphQL::Union.new("Something", [type_1])}
18
+ end
19
+
20
+ it 'must be all object types' do
21
+ assert_raises(ArgumentError) { GraphQL::Union.new("Something", [type_1, type_2, union])}
22
+ end
23
+
24
+ it 'infers type from an object' do
25
+ assert_equal(CheeseType, DairyProductUnion.resolve_type(CHEESES[1]))
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Validations::FieldsAreDefinedOnType do
4
+ let(:document) { GraphQL.parse("
5
+ query getCheese($sourceVar: DairyAnimal!) {
6
+ notDefinedField { name }
7
+ cheese(id: 1) { nonsenseField, flavor }
8
+ fromSource(source: COW) { bogusField }
9
+ }
10
+
11
+ fragment cheeseFields on Cheese { fatContent, hogwashField }
12
+ ")}
13
+
14
+ let(:validator) { GraphQL::Validator.new(schema: DummySchema, validators: [GraphQL::Validations::FieldsAreDefinedOnType]) }
15
+ let(:errors) { validator.validate(document) }
16
+ it "finds fields that are requested on types that don't have that field" do
17
+ expected_errors = [
18
+ "Field 'notDefinedField' doesn't exist on type 'Query'", # from query root
19
+ "Field 'nonsenseField' doesn't exist on type 'Cheese'", # from another field
20
+ "Field 'bogusField' doesn't exist on type 'Cheese'", # from a list
21
+ "Field 'hogwashField' doesn't exist on type 'Cheese'", # from a fragment
22
+ ]
23
+ assert_equal(expected_errors, errors)
24
+ end
25
+
26
+ it 'finds invalid fields on interfaces'
27
+ it 'finds invalid fields on unions'
28
+ end
@@ -0,0 +1,40 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Validations::FieldsWillMerge do
4
+ let(:document) { GraphQL.parse("
5
+ query getCheese($sourceVar: DairyAnimal!) {
6
+ id
7
+ nickname: name,
8
+ nickname: fatContent,
9
+ fatContent
10
+
11
+ similarCheeses(source: $sourceVar)
12
+
13
+ origin { originName: id },
14
+ ...cheeseFields
15
+ ... on Cheese {
16
+ fatContent: name
17
+ similarCheeses(source: SHEEP)
18
+ }
19
+ }
20
+ fragment cheeseFields on Cheese {
21
+ fatContent,
22
+ origin { originName: name }
23
+ id @someFlag
24
+ }
25
+ ")}
26
+
27
+ let(:validator) { GraphQL::Validator.new(schema: nil, validators: [GraphQL::Validations::FieldsWillMerge]) }
28
+ let(:errors) { validator.validate(document) }
29
+ it 'finds field naming conflicts' do
30
+ expected_errors = [
31
+ "Field 'nickname' has a field conflict: name or fatContent?", # alias conflict in query
32
+ "Field 'originName' has a field conflict: id or name?", # nested conflict
33
+ "Field 'id' has a directive conflict: [] or [someFlag]?", # different directives
34
+ "Field 'id' has a directive argument conflict: [] or [{}]?", # not sure this is a great way to handle it but here we are!
35
+ "Field 'fatContent' has a field conflict: fatContent or name?", # alias/name conflict in query and fragment
36
+ "Field 'similarCheeses' has an argument conflict: {\"source\":\"sourceVar\"} or {\"source\":\"SHEEP\"}?", # different arguments
37
+ ]
38
+ assert_equal(expected_errors, errors)
39
+ end
40
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe GraphQL::Validations::FragmentsAreUsed do
4
+ let(:document) { GraphQL.parse("
5
+ query getCheese {
6
+ name,
7
+ ...cheeseFields,
8
+ origin {
9
+ ...originFields
10
+ ...undefinedFields
11
+ }
12
+ }
13
+ fragment cheeseFields on Cheese { fatContent }
14
+ fragment originFields on Country { name, continent { ...continentFields }}
15
+ fragment continentFields on Continent { name }
16
+ fragment unusedFields on Cheese { is, not, used }
17
+ ")}
18
+
19
+ let(:validator) { GraphQL::Validator.new(schema: nil, validators: [GraphQL::Validations::FragmentsAreUsed]) }
20
+ let(:errors) { validator.validate(document) }
21
+
22
+ it 'adds errors for unused fragment definitions' do
23
+ assert_includes(errors, 'Some fragments were defined but not used: unusedFields')
24
+ end
25
+ it 'adds errors for undefined fragment spreads' do
26
+ assert_includes(errors, 'Some fragments were used but not defined: undefinedFields')
27
+ end
28
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ class SchemaErrorValidator
4
+ def validate(context)
5
+ context.errors << "Something is wrong: #{context.schema}"
6
+ end
7
+ end
8
+
9
+ class DocumentErrorValidator
10
+ def validate(context)
11
+ context.errors << "Something is wrong: #{context.document.name}"
12
+ end
13
+ end
14
+
15
+ describe GraphQL::Validator do
16
+ let(:document) { OpenStruct.new(name: "This is not a document", children: []) }
17
+ let(:validator) { GraphQL::Validator.new(schema: "This is not a schema", validators: [SchemaErrorValidator, DocumentErrorValidator]) }
18
+
19
+ it 'uses validators' do
20
+ errors = validator.validate(document)
21
+ expected_errors = ["Something is wrong: This is not a schema", "Something is wrong: This is not a document"]
22
+ assert_equal(expected_errors, errors)
23
+ end
24
+ end
@@ -1,6 +1,6 @@
1
1
  require "codeclimate-test-reporter"
2
2
  CodeClimate::TestReporter.start
3
-
3
+ require 'graphql'
4
4
  require 'date'
5
5
  require "minitest/autorun"
6
6
  require "minitest/focus"
@@ -16,4 +16,4 @@ Minitest.backtrace_filter = Minitest::BacktraceFilter.new
16
16
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
17
17
 
18
18
  require 'parslet'
19
- require 'parslet/convenience'
19
+ require 'parslet/convenience'
@@ -1,81 +1,141 @@
1
- class InadequateRecordBase
2
- def initialize(attributes={})
3
- attributes.each do |key, value|
4
- self.send("#{key}=", value)
5
- end
6
- end
1
+ require_relative './dummy_data'
7
2
 
8
- def destroy
9
- self.class.all.delete(self)
10
- end
3
+ Edible = GraphQL::Interface.new do
4
+ name "Edible"
5
+ description "Something you can eat, yum"
6
+ fields({
7
+ fatContent: field(type: !type.Float, desc: "Percentage which is fat"),
8
+ })
9
+ end
11
10
 
12
- class << self
13
- attr_accessor :_objects
14
- def all
15
- @_objects ||= []
16
- end
17
-
18
- def find(id)
19
- all.find { |object| object.id.to_s == id.to_s }
20
- end
21
-
22
- def where(query={})
23
- result = []
24
- all.each do |object|
25
- match = true
26
-
27
- query.each do |key, value|
28
- if object.send(key) != value
29
- match = false
30
- end
31
- end
11
+ AnimalProduct = GraphQL::Interface.new do
12
+ name "AnimalProduct"
13
+ description "Comes from an animal, no joke"
14
+ fields({
15
+ source: field(type: !type.String, desc: "Animal which produced this product"),
16
+ })
17
+ end
32
18
 
33
- result << object if match
34
- end
35
- result
36
- end
37
-
38
- def create(attributes)
39
- @next_id ||= 0
40
- attributes[:id] ||= @next_id += 1
41
- instance = self.new(attributes)
42
- all << instance
43
- instance
44
- end
45
- end
19
+ DairyAnimalEnum = GraphQL::Enum.new do |e|
20
+ e.name "DairyAnimal"
21
+ e.description "An animal which can yield milk"
22
+ e.value("COW", "Animal with black and white spots")
23
+ e.value("GOAT", "Animal with horns")
24
+ e.value("SHEEP", "Animal with wool")
25
+ e.value("YAK", "Animal with long hair", deprecation_reason: "Out of fashion")
26
+ end
27
+
28
+ CheeseType = GraphQL::ObjectType.new do
29
+ name "Cheese"
30
+ description "Cultured dairy product"
31
+ interfaces [Edible, AnimalProduct]
32
+ self.fields = {
33
+ id: field(type: !type.Int, desc: "Unique identifier"),
34
+ flavor: field(type: !type.String, desc: "Kind of cheese"),
35
+ source: field(type: !DairyAnimalEnum, desc: "Animal which produced the milk for this cheese"),
36
+ fatContent: field(type: !type.Float, desc: "Percentage which is milkfat", deprecation_reason: "Diet fashion has changed"),
37
+ }
38
+ end
39
+
40
+ MilkType = GraphQL::ObjectType.new do
41
+ name 'Milk'
42
+ description "Dairy beverage"
43
+ interfaces [Edible, AnimalProduct]
44
+ self.fields = {
45
+ id: field(type: !type.Int, desc: "Unique identifier"),
46
+ source: field(type: DairyAnimalEnum, desc: "Animal which produced this milk"),
47
+ fatContent: field(type: !type.Float, desc: "Percentage which is milkfat"),
48
+ flavors: field(
49
+ type: type[type.String],
50
+ desc: "Chocolate, Strawberry, etc",
51
+ args: {limit: {type: type.Int}}
52
+ ),
53
+ }
46
54
  end
47
55
 
48
- class Post < InadequateRecordBase
49
- attr_accessor :id, :title, :content, :published_at
56
+ DairyProductUnion = GraphQL::Union.new("DairyProduct", [MilkType, CheeseType])
50
57
 
51
- def comments
52
- Comment.where(post_id: id)
58
+ DairyProductInputType = GraphQL::InputObjectType.new {
59
+ name "DairyProductInput"
60
+ description "Properties for finding a dairy product"
61
+ input_fields({
62
+ source: arg({type: DairyAnimalEnum}),
63
+ fatContent: arg({type: type.Float}),
64
+ })
65
+ }
66
+
67
+
68
+ class FetchField < GraphQL::AbstractField
69
+ attr_reader :type
70
+ attr_accessor :name
71
+ def initialize(type:, data:)
72
+ @type = type
73
+ @data = data
53
74
  end
54
75
 
55
- def likes
56
- Like.where(post_id: id)
76
+ def description
77
+ "Find a #{@type.name} by id"
78
+ end
79
+
80
+ def resolve(target, arguments, context)
81
+ @data[arguments["id"]]
57
82
  end
58
83
  end
59
84
 
60
- class Comment < InadequateRecordBase
61
- attr_accessor :id, :post_id, :content, :rating
85
+ SourceField = GraphQL::Field.new do |f|
86
+ f.type GraphQL::ListType.new(of_type: CheeseType)
87
+ f.description "Cheese from source"
88
+ f.resolve -> (target, arguments, context) {
89
+ CHEESES.values.select{ |c| c.source == arguments["source"] }
90
+ }
91
+ end
62
92
 
63
- def post
64
- Post.find(post_id)
65
- end
93
+ FavoriteField = GraphQL::Field.new do |f|
94
+ f.description "My favorite food"
95
+ f.type Edible
96
+ f.resolve -> (t, a, c) { MILKS[1] }
66
97
  end
67
98
 
68
- class Like < InadequateRecordBase
69
- attr_accessor :id, :post_id
70
99
 
71
- def post
72
- Post.find(post_id)
73
- end
100
+ QueryType = GraphQL::ObjectType.new do
101
+ name "Query"
102
+ description "Query root of the system"
103
+ fields({
104
+ cheese: FetchField.new(type: CheeseType, data: CHEESES),
105
+ fromSource: SourceField,
106
+ favoriteEdible: FavoriteField,
107
+ searchDairy: GraphQL::Field.new { |f|
108
+ f.name "searchDairy"
109
+ f.description "Find dairy products matching a description"
110
+ f.type DairyProductUnion
111
+ f.arguments({product: {type: DairyProductInputType}})
112
+ f.resolve -> (t, a, c) {
113
+ products = CHEESES.values + MILKS.values
114
+ source = a["product"]["source"]
115
+ if !source.nil?
116
+ products = products.select { |p| p.source == source }
117
+ end
118
+ products.first
119
+ }
120
+ }
121
+ })
74
122
  end
75
123
 
76
- class Context
77
- attr_reader :person_name
78
- def initialize(person_name:)
79
- @person_name = person_name
80
- end
124
+ GLOBAL_VALUES = []
125
+
126
+ MutationType = GraphQL::ObjectType.new do
127
+ name "Mutation"
128
+ description "The root for mutations in this schema"
129
+ fields({
130
+ pushValue: GraphQL::Field.new { |f|
131
+ f.description("Push a value onto a global array :D")
132
+ f.type(!type[!type.Int])
133
+ f.arguments(value: arg(type: !type.Int))
134
+ f.resolve -> (o, args, ctx) {
135
+ GLOBAL_VALUES << args["value"]
136
+ GLOBAL_VALUES
137
+ }
138
+ }
139
+ })
81
140
  end
141
+ DummySchema = GraphQL::Schema.new(query: QueryType, mutation: MutationType)