graphql 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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)