graphql 1.8.0.pre2 → 1.8.0.pre3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (83) hide show
  1. checksums.yaml +4 -4
  2. data/lib/graphql.rb +1 -1
  3. data/lib/graphql/deprecated_dsl.rb +2 -0
  4. data/lib/graphql/enum_type.rb +1 -1
  5. data/lib/graphql/field.rb +10 -1
  6. data/lib/graphql/input_object_type.rb +3 -1
  7. data/lib/graphql/introspection.rb +3 -10
  8. data/lib/graphql/introspection/base_object.rb +15 -0
  9. data/lib/graphql/introspection/directive_location_enum.rb +11 -7
  10. data/lib/graphql/introspection/directive_type.rb +23 -16
  11. data/lib/graphql/introspection/dynamic_fields.rb +11 -0
  12. data/lib/graphql/introspection/entry_points.rb +29 -0
  13. data/lib/graphql/introspection/enum_value_type.rb +16 -11
  14. data/lib/graphql/introspection/field_type.rb +21 -12
  15. data/lib/graphql/introspection/input_value_type.rb +26 -23
  16. data/lib/graphql/introspection/schema_field.rb +7 -2
  17. data/lib/graphql/introspection/schema_type.rb +36 -22
  18. data/lib/graphql/introspection/type_by_name_field.rb +10 -2
  19. data/lib/graphql/introspection/type_kind_enum.rb +10 -6
  20. data/lib/graphql/introspection/type_type.rb +85 -23
  21. data/lib/graphql/introspection/typename_field.rb +1 -0
  22. data/lib/graphql/language.rb +1 -0
  23. data/lib/graphql/language/document_from_schema_definition.rb +129 -37
  24. data/lib/graphql/language/generation.rb +3 -182
  25. data/lib/graphql/language/nodes.rb +12 -2
  26. data/lib/graphql/language/parser.rb +63 -55
  27. data/lib/graphql/language/parser.y +2 -1
  28. data/lib/graphql/language/printer.rb +351 -0
  29. data/lib/graphql/object_type.rb +1 -1
  30. data/lib/graphql/query.rb +1 -1
  31. data/lib/graphql/query/arguments.rb +24 -8
  32. data/lib/graphql/query/context.rb +3 -0
  33. data/lib/graphql/query/literal_input.rb +4 -1
  34. data/lib/graphql/railtie.rb +28 -6
  35. data/lib/graphql/schema.rb +42 -7
  36. data/lib/graphql/schema/enum.rb +1 -0
  37. data/lib/graphql/schema/field.rb +26 -5
  38. data/lib/graphql/schema/field/dynamic_resolve.rb +18 -9
  39. data/lib/graphql/schema/input_object.rb +2 -2
  40. data/lib/graphql/schema/introspection_system.rb +93 -0
  41. data/lib/graphql/schema/late_bound_type.rb +32 -0
  42. data/lib/graphql/schema/member.rb +21 -1
  43. data/lib/graphql/schema/member/build_type.rb +8 -6
  44. data/lib/graphql/schema/member/has_fields.rb +1 -1
  45. data/lib/graphql/schema/member/instrumentation.rb +3 -1
  46. data/lib/graphql/schema/member/list_type_proxy.rb +4 -0
  47. data/lib/graphql/schema/member/non_null_type_proxy.rb +4 -0
  48. data/lib/graphql/schema/object.rb +2 -1
  49. data/lib/graphql/schema/printer.rb +33 -266
  50. data/lib/graphql/schema/traversal.rb +74 -6
  51. data/lib/graphql/schema/validation.rb +3 -2
  52. data/lib/graphql/static_validation/rules/fields_are_defined_on_type.rb +6 -6
  53. data/lib/graphql/tracing/scout_tracing.rb +2 -2
  54. data/lib/graphql/upgrader/member.rb +463 -63
  55. data/lib/graphql/version.rb +1 -1
  56. data/spec/fixtures/upgrader/blame_range.original.rb +43 -0
  57. data/spec/fixtures/upgrader/blame_range.transformed.rb +31 -0
  58. data/spec/fixtures/upgrader/subscribable.original.rb +51 -0
  59. data/spec/fixtures/upgrader/subscribable.transformed.rb +46 -0
  60. data/spec/fixtures/upgrader/type_x.original.rb +35 -0
  61. data/spec/fixtures/upgrader/type_x.transformed.rb +35 -0
  62. data/spec/graphql/language/document_from_schema_definition_spec.rb +729 -296
  63. data/spec/graphql/language/generation_spec.rb +21 -186
  64. data/spec/graphql/language/nodes_spec.rb +21 -0
  65. data/spec/graphql/language/printer_spec.rb +203 -0
  66. data/spec/graphql/query/arguments_spec.rb +14 -4
  67. data/spec/graphql/query/context_spec.rb +17 -0
  68. data/spec/graphql/schema/build_from_definition_spec.rb +13 -4
  69. data/spec/graphql/schema/field_spec.rb +14 -0
  70. data/spec/graphql/schema/introspection_system_spec.rb +39 -0
  71. data/spec/graphql/schema/object_spec.rb +12 -1
  72. data/spec/graphql/schema/printer_spec.rb +14 -14
  73. data/spec/graphql/tracing/platform_tracing_spec.rb +2 -2
  74. data/spec/graphql/upgrader/member_spec.rb +274 -62
  75. data/spec/support/jazz.rb +75 -3
  76. metadata +38 -9
  77. data/lib/graphql/introspection/arguments_field.rb +0 -7
  78. data/lib/graphql/introspection/enum_values_field.rb +0 -18
  79. data/lib/graphql/introspection/fields_field.rb +0 -13
  80. data/lib/graphql/introspection/input_fields_field.rb +0 -12
  81. data/lib/graphql/introspection/interfaces_field.rb +0 -11
  82. data/lib/graphql/introspection/of_type_field.rb +0 -6
  83. data/lib/graphql/introspection/possible_types_field.rb +0 -11
@@ -19,7 +19,7 @@ describe GraphQL::Query::Arguments do
19
19
  GraphQL::Query::Arguments.construct_arguments_class(test_input_1)
20
20
  GraphQL::Query::Arguments.construct_arguments_class(test_input_2)
21
21
  arg_values = {a: 1, b: 2, c: { d: 3, e: 4 }}
22
- test_input_2.arguments_class.new(arg_values, context: nil)
22
+ test_input_2.arguments_class.new(arg_values, context: nil, defaults_used: Set.new)
23
23
  }
24
24
 
25
25
  it "returns keys as strings, with aliases" do
@@ -73,7 +73,7 @@ describe GraphQL::Query::Arguments do
73
73
  self.argument_definitions = types
74
74
  end
75
75
 
76
- new_arguments = args_class.new(transformed_args, context: nil)
76
+ new_arguments = args_class.new(transformed_args, context: nil, defaults_used: Set.new)
77
77
  expected_hash = {
78
78
  "A" => 1,
79
79
  "B" => 2,
@@ -96,7 +96,8 @@ describe GraphQL::Query::Arguments do
96
96
  it "wraps input objects, but not other hashes" do
97
97
  args = input_type.arguments_class.new(
98
98
  {a: 1, b: {a: 2}, c: {a: 3}},
99
- context: nil
99
+ defaults_used: Set.new,
100
+ context: nil,
100
101
  )
101
102
  assert_kind_of GraphQL::Query::Arguments, args["b"]
102
103
  assert_instance_of Hash, args["c"]
@@ -198,6 +199,15 @@ describe GraphQL::Query::Arguments do
198
199
  assert_equal({"a" => 1, "b" => 2}, last_args.to_h)
199
200
  end
200
201
 
202
+ it "indicates when default argument values were applied" do
203
+ schema.execute("{ argTest(a: 1) }")
204
+
205
+ last_args = arg_values.last
206
+
207
+ assert_equal false, last_args.default_used?('a')
208
+ assert_equal true, last_args.default_used?('b')
209
+ end
210
+
201
211
  it "works from variables" do
202
212
  variables = { "arg" => { "a" => 1, "d" => nil } }
203
213
  schema.execute("query ArgTest($arg: TestInput){ argTest(d: $arg) }", variables: variables)
@@ -306,7 +316,7 @@ describe GraphQL::Query::Arguments do
306
316
  assert_nil input_object.arguments_class
307
317
 
308
318
  GraphQL::Query::Arguments.construct_arguments_class(input_object)
309
- args = input_object.arguments_class.new({foo: 3, bar: -90}, context: nil)
319
+ args = input_object.arguments_class.new({foo: 3, bar: -90}, defaults_used: Set.new, context: nil)
310
320
 
311
321
  assert_equal 3, args.foo
312
322
  assert_equal -90, args.bar
@@ -243,4 +243,21 @@ TABLE
243
243
  assert_equal [expected_err], result["errors"]
244
244
  end
245
245
  end
246
+
247
+ describe "custom context class" do
248
+ it "can be specified" do
249
+ query_str = '{
250
+ inspectContext
251
+ find(id: "Musician/Herbie Hancock") {
252
+ ... on Musician {
253
+ inspectContext
254
+ }
255
+ }
256
+ }'
257
+ res = Jazz::Schema.execute(query_str, context: { magic_key: :ignored, normal_key: "normal_value" })
258
+ expected_values = ["custom_method", "magic_value", "normal_value"]
259
+ assert_equal expected_values, res["data"]["inspectContext"]
260
+ assert_equal expected_values, res["data"]["find"]["inspectContext"]
261
+ end
262
+ end
246
263
  end
@@ -308,7 +308,7 @@ schema {
308
308
  }
309
309
 
310
310
  type Hello {
311
- str(int: Int, bool: Boolean): String
311
+ str(bool: Boolean, int: Int): String
312
312
  }
313
313
  SCHEMA
314
314
 
@@ -505,7 +505,7 @@ type HelloScalars {
505
505
  }
506
506
 
507
507
  type Mutation {
508
- addHelloScalars(str: String, int: Int, bool: Boolean): HelloScalars
508
+ addHelloScalars(bool: Boolean, int: Int, str: String): HelloScalars
509
509
  }
510
510
  SCHEMA
511
511
 
@@ -520,7 +520,7 @@ enum Color {
520
520
  }
521
521
 
522
522
  type Mutation {
523
- hello(str: String, int: Int, color: Color = RED, nullDefault: Int = null): String
523
+ hello(color: Color = RED, int: Int, nullDefault: Int = null, str: String): String
524
524
  }
525
525
 
526
526
  type Query {
@@ -545,7 +545,7 @@ type HelloScalars {
545
545
  }
546
546
 
547
547
  type Subscription {
548
- subscribeHelloScalars(str: String, int: Int, bool: Boolean): HelloScalars
548
+ subscribeHelloScalars(bool: Boolean, int: Int, str: String): HelloScalars
549
549
  }
550
550
  SCHEMA
551
551
 
@@ -603,6 +603,15 @@ type Query {
603
603
 
604
604
  build_schema_and_compare_output(schema.chop)
605
605
  end
606
+
607
+ it 'supports empty types' do
608
+ schema = <<-SCHEMA
609
+ type Query {
610
+ }
611
+ SCHEMA
612
+
613
+ build_schema_and_compare_output(schema.chop)
614
+ end
606
615
  end
607
616
 
608
617
  describe 'Failures' do
@@ -14,5 +14,19 @@ describe GraphQL::Schema::Field do
14
14
  it "camelizes the field name" do
15
15
  assert_equal 'inspectInput', field.graphql_definition.name
16
16
  end
17
+
18
+ describe "description in block" do
19
+ it "will raise if description is defined both in the argument and in the block" do
20
+ assert_raises RuntimeError, "You're overriding the description of shouldRaise in the provided block!" do
21
+ Class.new(Jazz::BaseObject) do
22
+ graphql_name "JustAName"
23
+
24
+ field :should_raise, Jazz::Key, "this should not raise", null: true do
25
+ description "This should raise"
26
+ end
27
+ end.to_graphql
28
+ end
29
+ end
30
+ end
17
31
  end
18
32
  end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+ require "spec_helper"
3
+
4
+ describe GraphQL::Schema::IntrospectionSystem do
5
+ describe "custom introspection" do
6
+ it "serves custom fields on types" do
7
+ res = Jazz::Schema.execute("{ __schema { isJazzy } }")
8
+ assert_equal true, res["data"]["__schema"]["isJazzy"]
9
+ end
10
+
11
+ it "serves overridden fields on types" do
12
+ res = Jazz::Schema.execute(%|{ __type(name: "Ensemble") { name } }|)
13
+ assert_equal "ENSEMBLE", res["data"]["__type"]["name"]
14
+ end
15
+
16
+ it "serves custom entry points" do
17
+ res = Jazz::Schema.execute("{ __classname }", root_value: Set.new)
18
+ assert_equal "Set", res["data"]["__classname"]
19
+ end
20
+
21
+ it "serves custom dynamic fields" do
22
+ res = Jazz::Schema.execute("{ nowPlaying { __typename __typenameLength __astNodeClass } }")
23
+ assert_equal "Ensemble", res["data"]["nowPlaying"]["__typename"]
24
+ assert_equal 8, res["data"]["nowPlaying"]["__typenameLength"]
25
+ assert_equal "GraphQL::Language::Nodes::Field", res["data"]["nowPlaying"]["__astNodeClass"]
26
+ end
27
+
28
+ it "doesn't affect other schemas" do
29
+ res = Dummy::Schema.execute("{ __schema { isJazzy } }")
30
+ assert_equal 1, res["errors"].length
31
+
32
+ res = Dummy::Schema.execute("{ __classname }", root_value: Set.new)
33
+ assert_equal 1, res["errors"].length
34
+
35
+ res = Dummy::Schema.execute("{ ensembles { __typenameLength } }")
36
+ assert_equal 1, res["errors"].length
37
+ end
38
+ end
39
+ end
@@ -16,7 +16,7 @@ describe GraphQL::Schema::Object do
16
16
  it "inherits fields and interfaces" do
17
17
  new_object_class = Class.new(object_class) do
18
18
  field :newField, String, null: true
19
- field :name, String, "The new description", null: true
19
+ field :name, String, description: "The new description", null: true
20
20
  end
21
21
 
22
22
  # one more than the parent class
@@ -29,6 +29,17 @@ describe GraphQL::Schema::Object do
29
29
  name_field = new_object_class.fields.find { |f| f.name == "name" }
30
30
  assert_equal "The new description", name_field.description
31
31
  end
32
+
33
+ it "inherits name and description" do
34
+ # Manually assign a name since `.name` isn't populated for dynamic classes
35
+ new_subclass_1 = Class.new(object_class) do
36
+ graphql_name "NewSubclass"
37
+ end
38
+ new_subclass_2 = Class.new(new_subclass_1)
39
+ assert_equal "NewSubclass", new_subclass_1.graphql_name
40
+ assert_equal "NewSubclass", new_subclass_2.graphql_name
41
+ assert_equal object_class.description, new_subclass_2.description
42
+ end
32
43
  end
33
44
 
34
45
  describe ".to_graphql_type" do
@@ -136,6 +136,14 @@ schema {
136
136
  query: Root
137
137
  }
138
138
 
139
+ # Marks an element of a GraphQL schema as no longer supported.
140
+ directive @deprecated(
141
+ # Explains why this element was deprecated, usually also including a suggestion
142
+ # for how to access supported similar data. Formatted in
143
+ # [Markdown](https://daringfireball.net/projects/markdown/).
144
+ reason: String = "No longer supported"
145
+ ) on FIELD_DEFINITION | ENUM_VALUE
146
+
139
147
  # Directs the executor to include this field or fragment only when the `if` argument is true.
140
148
  directive @include(
141
149
  # Included when true.
@@ -148,14 +156,6 @@ directive @skip(
148
156
  if: Boolean!
149
157
  ) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
150
158
 
151
- # Marks an element of a GraphQL schema as no longer supported.
152
- directive @deprecated(
153
- # Explains why this element was deprecated, usually also including a suggestion
154
- # for how to access supported similar data. Formatted in
155
- # [Markdown](https://daringfireball.net/projects/markdown/).
156
- reason: String = "No longer supported"
157
- ) on FIELD_DEFINITION | ENUM_VALUE
158
-
159
159
  # A Directive provides a way to describe alternate runtime execution and type validation behavior in a GraphQL document.
160
160
  #
161
161
  # In some cases, you need to provide options to alter GraphQL's execution behavior
@@ -345,7 +345,6 @@ schema {
345
345
  subscription: Subscription
346
346
  }
347
347
  SCHEMA
348
-
349
348
  assert_match expected, GraphQL::Schema::Printer.print_schema(custom_schema)
350
349
  end
351
350
 
@@ -390,7 +389,7 @@ type Audio {
390
389
 
391
390
  enum Choice {
392
391
  BAR
393
- BAZ @deprecated(reason: "Use \\\"BAR\\\".")
392
+ BAZ @deprecated(reason: "Use "BAR".")
394
393
  FOO
395
394
  WOZ @deprecated
396
395
  }
@@ -439,7 +438,7 @@ interface Node {
439
438
  type Post {
440
439
  body: String!
441
440
  comments: [Comment!]
442
- comments_count: Int! @deprecated(reason: "Use \\\"comments\\\".")
441
+ comments_count: Int! @deprecated(reason: "Use "comments".")
443
442
  id: ID!
444
443
  title: String!
445
444
  }
@@ -489,7 +488,6 @@ enum Choice {
489
488
  }
490
489
 
491
490
  type Subscription {
492
-
493
491
  }
494
492
 
495
493
  input Varied {
@@ -509,7 +507,9 @@ SCHEMA
509
507
  when GraphQL::Argument
510
508
  member.name != "id"
511
509
  else
512
- member.deprecation_reason.nil?
510
+ if member.respond_to?(:deprecation_reason)
511
+ member.deprecation_reason.nil?
512
+ end
513
513
  end
514
514
  }
515
515
 
@@ -600,7 +600,7 @@ SCHEMA
600
600
  type Post {
601
601
  body: String!
602
602
  comments: [Comment!]
603
- comments_count: Int! @deprecated(reason: \"Use \\\"comments\\\".\")
603
+ comments_count: Int! @deprecated(reason: "Use "comments".")
604
604
  id: ID!
605
605
  title: String!
606
606
  }
@@ -69,10 +69,10 @@ describe GraphQL::Tracing::PlatformTracing do
69
69
  schema.execute(" { tracingScalar { traceNil traceFalse traceTrue } }")
70
70
  expected_trace = [
71
71
  "em",
72
- "am",
73
72
  "l",
74
73
  "p",
75
74
  "v",
75
+ "am",
76
76
  "aq",
77
77
  "eq",
78
78
  "Q.t",
@@ -98,10 +98,10 @@ describe GraphQL::Tracing::PlatformTracing do
98
98
  schema.execute(" { tracingScalar { traceNil traceFalse traceTrue } }")
99
99
  expected_trace = [
100
100
  "em",
101
- "am",
102
101
  "l",
103
102
  "p",
104
103
  "v",
104
+ "am",
105
105
  "aq",
106
106
  "eq",
107
107
  "Q.t",
@@ -11,17 +11,23 @@ describe GraphQL::Upgrader::Member do
11
11
  describe 'field arguments' do
12
12
  it 'upgrades' do
13
13
  old = %{argument :status, !TodoStatus, "Restrict items to this status"}
14
- new = %{argument :status, TodoStatus, "Restrict items to this status", null: false}
14
+ new = %{argument :status, TodoStatus, "Restrict items to this status", required: true}
15
15
 
16
- assert_equal upgrade(old), new
16
+ assert_equal new, upgrade(old)
17
17
  end
18
18
  end
19
19
 
20
20
  it 'upgrades the property definition to method' do
21
21
  old = %{field :name, String, property: :name}
22
- new = %{field :name, String, method: :name, null: false}
22
+ new = %{field :name, String, method: :name, null: true}
23
23
 
24
- assert_equal upgrade(old), new
24
+ assert_equal new, upgrade(old)
25
+ end
26
+
27
+ it 'upgrades the property definition in a block to method' do
28
+ old = %{field :name, String do\n property :name\nend}
29
+ new = %{field :name, String, method: :name, null: true}
30
+ assert_equal new, upgrade(old)
25
31
  end
26
32
 
27
33
  describe 'name' do
@@ -32,10 +38,23 @@ describe GraphQL::Upgrader::Member do
32
38
  end
33
39
  }
34
40
  new = %{
35
- class UserType < BaseObject
41
+ class UserType < Types::BaseObject
42
+ end
43
+ }
44
+ assert_equal new, upgrade(old)
45
+ end
46
+
47
+ it 'removes the name field if it can be inferred from the class and under a module' do
48
+ old = %{
49
+ Types::UserType = GraphQL::ObjectType.define do
50
+ name "User"
51
+ end
52
+ }
53
+ new = %{
54
+ class Types::UserType < Types::BaseObject
36
55
  end
37
56
  }
38
- assert_equal upgrade(old), new
57
+ assert_equal new, upgrade(old)
39
58
  end
40
59
 
41
60
  it 'upgrades the name into graphql_name if it can\'t be inferred from the class' do
@@ -45,11 +64,11 @@ describe GraphQL::Upgrader::Member do
45
64
  end
46
65
  }
47
66
  new = %{
48
- class TeamType < BaseObject
67
+ class TeamType < Types::BaseObject
49
68
  graphql_name "User"
50
69
  end
51
70
  }
52
- assert_equal upgrade(old), new
71
+ assert_equal new, upgrade(old)
53
72
 
54
73
  old = %{
55
74
  UserInterface = GraphQL::InterfaceType.define do
@@ -57,111 +76,211 @@ describe GraphQL::Upgrader::Member do
57
76
  end
58
77
  }
59
78
  new = %{
60
- class UserInterface < BaseInterface
79
+ class UserInterface < Types::BaseInterface
61
80
  graphql_name "User"
62
81
  end
63
82
  }
64
- assert_equal upgrade(old), new
83
+ assert_equal new, upgrade(old)
65
84
 
66
85
  old = %{
67
- UserInterface = GraphQL::InterfaceType.define do
86
+ UserEnum = GraphQL::EnumType.define do
68
87
  name "User"
69
88
  end
70
89
  }
71
90
  new = %{
72
- class UserInterface < BaseInterface
91
+ class UserEnum < Types::BaseEnum
73
92
  graphql_name "User"
74
93
  end
75
94
  }
76
- assert_equal upgrade(old), new
95
+ assert_equal new, upgrade(old)
77
96
  end
78
97
  end
79
98
 
80
99
  describe 'definition' do
81
100
  it 'upgrades the .define into class based definition' do
82
- old = %{UserType = GraphQL::ObjectType.define do}
83
- new = %{class UserType < BaseObject}
84
- assert_equal upgrade(old), new
101
+ old = %{UserType = GraphQL::ObjectType.define do
102
+ end}
103
+ new = %{class UserType < Types::BaseObject
104
+ end}
105
+ assert_equal new, upgrade(old)
106
+
107
+ old = %{UserInterface = GraphQL::InterfaceType.define do
108
+ end}
109
+ new = %{class UserInterface < Types::BaseInterface
110
+ end}
111
+ assert_equal new, upgrade(old)
112
+
113
+ old = %{UserUnion = GraphQL::UnionType.define do
114
+ end}
115
+ new = %{class UserUnion < Types::BaseUnion
116
+ end}
117
+ assert_equal new, upgrade(old)
85
118
 
86
- old = %{UserInterface = GraphQL::InterfaceType.define do}
87
- new = %{class UserInterface < BaseInterface}
88
- assert_equal upgrade(old), new
119
+ old = %{UserEnum = GraphQL::EnumType.define do
120
+ end}
121
+ new = %{class UserEnum < Types::BaseEnum
122
+ end}
123
+ assert_equal new, upgrade(old)
89
124
 
90
- old = %{UserUnion = GraphQL::UnionType.define do}
91
- new = %{class UserUnion < BaseUnion}
92
- assert_equal upgrade(old), new
125
+ old = %{UserInput = GraphQL::InputObjectType.define do
126
+ end}
127
+ new = %{class UserInput < Types::BaseInputObject
128
+ end}
129
+ assert_equal new, upgrade(old)
93
130
 
94
- old = %{UserEnum = GraphQL::EnumType.define do}
95
- new = %{class UserEnum < BaseEnum}
96
- assert_equal upgrade(old), new
131
+ old = %{UserScalar = GraphQL::ScalarType.define do
132
+ end}
133
+ new = %{class UserScalar < Types::BaseScalar
134
+ end}
135
+ assert_equal new, upgrade(old)
97
136
  end
98
137
 
99
138
  it 'upgrades including the module' do
100
- old = %{Module::UserType = GraphQL::ObjectType.define do}
101
- new = %{class Module::UserType < BaseObject}
102
- assert_equal upgrade(old), new
139
+ old = %{Module::UserType = GraphQL::ObjectType.define do
140
+ end}
141
+ new = %{class Module::UserType < Types::BaseObject
142
+ end}
143
+ assert_equal new, upgrade(old)
103
144
  end
104
145
  end
105
146
 
106
147
  describe 'fields' do
148
+ it 'underscorizes field name' do
149
+ old = %{field :firstName, !types.String}
150
+ new = %{field :first_name, String, null: false}
151
+ assert_equal new, upgrade(old)
152
+ end
153
+
154
+ describe "resolve proc to method" do
155
+ it "converts @object and @context" do
156
+ old = %{
157
+ field :firstName, !types.String do
158
+ resolve ->(obj, arg, ctx) {
159
+ ctx.something
160
+ other_ctx # test combined identifiers
161
+
162
+ obj[ctx] + obj
163
+ obj.given_name
164
+ }
165
+ end
166
+ }
167
+ new = %{
168
+ field :first_name, String, null: false
169
+
170
+ def first_name
171
+ @context.something
172
+ other_ctx # test combined identifiers
173
+
174
+ @object[@context] + @object
175
+ @object.given_name
176
+ end
177
+ }
178
+ assert_equal new, upgrade(old)
179
+ end
180
+
181
+ it "handles `_` var names" do
182
+ old = %{
183
+ field :firstName, !types.String do
184
+ resolve ->(obj, _, _) {
185
+ obj.given_name
186
+ }
187
+ end
188
+ }
189
+ new = %{
190
+ field :first_name, String, null: false
191
+
192
+ def first_name
193
+ @object.given_name
194
+ end
195
+ }
196
+ assert_equal new, upgrade(old)
197
+ end
198
+
199
+ it "creates **arguments if necessary" do
200
+ old = %{
201
+ field :firstName, !types.String do
202
+ argument :ctx, types.String, default_value: "abc"
203
+ resolve ->(obj, args, ctx) {
204
+ args[:ctx]
205
+ }
206
+ end
207
+ }
208
+ new = %{
209
+ field :first_name, String, null: false do
210
+ argument :ctx, String, default_value: "abc", required: false
211
+ end
212
+
213
+ def first_name(**args)
214
+ args[:ctx]
215
+ end
216
+ }
217
+ assert_equal new, upgrade(old)
218
+ end
219
+ end
220
+
221
+
107
222
  it 'upgrades to the new definition' do
108
223
  old = %{field :name, !types.String}
109
224
  new = %{field :name, String, null: false}
110
- assert_equal upgrade(old), new
225
+ assert_equal new, upgrade(old)
111
226
 
112
227
  old = %{field :name, !types.String, "description", method: :name}
113
228
  new = %{field :name, String, "description", method: :name, null: false}
114
- assert_equal upgrade(old), new
229
+ assert_equal new, upgrade(old)
115
230
 
116
231
  old = %{field :name, -> { !types.String }}
117
- new = %{field :name, -> { String }, null: false}
118
- assert_equal upgrade(old), new
232
+ new = %{field :name, String, null: false}
233
+ assert_equal new, upgrade(old)
119
234
 
120
235
  old = %{connection :name, Name.connection_type, "names"}
121
- new = %{field :name, Name.connection_type, "names", null: false, connection: true}
122
- assert_equal upgrade(old), new
236
+ new = %{field :name, Name.connection_type, "names", null: true, connection: true}
237
+ assert_equal new, upgrade(old)
123
238
 
124
239
  old = %{connection :name, !Name.connection_type, "names"}
125
240
  new = %{field :name, Name.connection_type, "names", null: false, connection: true}
126
- assert_equal upgrade(old), new
241
+ assert_equal new, upgrade(old)
127
242
 
128
243
  old = %{field :names, types[types.String]}
129
- new = %{field :names, [String], null: false}
130
- assert_equal upgrade(old), new
244
+ new = %{field :names, [String], null: true}
245
+ assert_equal new, upgrade(old)
131
246
 
132
247
  old = %{field :names, !types[types.String]}
133
248
  new = %{field :names, [String], null: false}
134
- assert_equal upgrade(old), new
249
+ assert_equal new, upgrade(old)
135
250
 
136
251
  old = %{
137
252
  field :name, types.String do
138
253
  end
139
254
  }
140
255
  new = %{
141
- field :name, String, null: false do
142
- end
256
+ field :name, String, null: true
143
257
  }
144
- assert_equal upgrade(old), new
258
+ assert_equal new, upgrade(old)
145
259
 
146
260
  old = %{
147
261
  field :name, !types.String do
262
+ description "abc"
263
+ end
264
+
265
+ field :name2, !types.Int do
266
+ description "def"
148
267
  end
149
268
  }
150
269
  new = %{
151
- field :name, String, null: false do
152
- end
270
+ field :name, String, description: "abc", null: false
271
+
272
+ field :name2, Integer, description: "def", null: false
153
273
  }
154
- assert_equal upgrade(old), new
274
+ assert_equal new, upgrade(old)
155
275
 
156
276
  old = %{
157
277
  field :name, -> { !types.String } do
158
278
  end
159
279
  }
160
280
  new = %{
161
- field :name, -> { String }, null: false do
162
- end
281
+ field :name, String, null: false
163
282
  }
164
- assert_equal upgrade(old), new
283
+ assert_equal new, upgrade(old)
165
284
 
166
285
  old = %{
167
286
  field :name do
@@ -169,21 +288,25 @@ describe GraphQL::Upgrader::Member do
169
288
  end
170
289
  }
171
290
  new = %{
172
- field :name, -> { String }, null: false do
173
- end
291
+ field :name, String, null: true
174
292
  }
175
- assert_equal upgrade(old), new
293
+ assert_equal new, upgrade(old)
176
294
 
177
295
  old = %{
178
296
  field :name do
179
297
  type !String
180
298
  end
299
+
300
+ field :name2 do
301
+ type !String
302
+ end
181
303
  }
182
304
  new = %{
183
- field :name, String, null: false do
184
- end
305
+ field :name, String, null: false
306
+
307
+ field :name2, String, null: false
185
308
  }
186
- assert_equal upgrade(old), new
309
+ assert_equal new, upgrade(old)
187
310
 
188
311
  old = %{
189
312
  field :name, -> { types.String },
@@ -191,10 +314,9 @@ describe GraphQL::Upgrader::Member do
191
314
  end
192
315
  }
193
316
  new = %{
194
- field :name, -> { String }, "newline description", null: false do
195
- end
317
+ field :name, String, "newline description", null: true
196
318
  }
197
- assert_equal upgrade(old), new
319
+ assert_equal new, upgrade(old)
198
320
 
199
321
  old = %{
200
322
  field :name, -> { !types.String },
@@ -202,10 +324,9 @@ describe GraphQL::Upgrader::Member do
202
324
  end
203
325
  }
204
326
  new = %{
205
- field :name, -> { String }, "newline description", null: false do
206
- end
327
+ field :name, String, "newline description", null: false
207
328
  }
208
- assert_equal upgrade(old), new
329
+ assert_equal new, upgrade(old)
209
330
 
210
331
  old = %{
211
332
  field :name, String,
@@ -213,10 +334,101 @@ describe GraphQL::Upgrader::Member do
213
334
  end
214
335
  }
215
336
  new = %{
216
- field :name, String, field: SomeField, null: false do
217
- end
337
+ field :name, String, field: SomeField, null: true
338
+ }
339
+ assert_equal new, upgrade(old)
340
+ end
341
+ end
342
+
343
+ describe 'multi-line field with property/method' do
344
+ it 'upgrades without breaking syntax' do
345
+ old = %{
346
+ field :is_example_field, types.Boolean,
347
+ property: :example_field?
348
+ }
349
+ new = %{
350
+ field :is_example_field, Boolean, null: true
351
+ method: :example_field?
352
+ }
353
+
354
+ assert_equal new, upgrade(old)
355
+ end
356
+ end
357
+
358
+ describe 'multi-line connection with property/method' do
359
+ it 'upgrades without breaking syntax' do
360
+ old = %{
361
+ connection :example_connection, -> { ExampleConnectionType },
362
+ property: :example_connections
363
+ }
364
+ new = %{
365
+ field :example_connection, ExampleConnectionType, null: true, connection: true
366
+ method: :example_connections
367
+ }
368
+
369
+ assert_equal new, upgrade(old)
370
+ end
371
+ end
372
+
373
+ describe 'input_field' do
374
+ it 'upgrades to argument' do
375
+ old = %{input_field :id, !types.ID}
376
+ new = %{argument :id, ID, required: true}
377
+ assert_equal new, upgrade(old)
378
+ end
379
+ end
380
+
381
+ describe 'implements' do
382
+ it 'upgrades interfaces to implements' do
383
+ old = %{
384
+ interfaces [Types::SearchableType, Types::CommentableType]
385
+ interfaces [Types::ShareableType]
386
+ }
387
+ new = %{
388
+ implements Types::SearchableType
389
+ implements Types::CommentableType
390
+ implements Types::ShareableType
391
+ }
392
+ assert_equal new, upgrade(old)
393
+ end
394
+ end
395
+
396
+ describe "fixtures" do
397
+ class ActiveRecordTypeToClassTransform < GraphQL::Upgrader::Transform
398
+ def initialize
399
+ @find_pattern = /^( +)([a-zA-Z_0-9:]*) = define_active_record_type\(-> ?\{ ?:{0,2}([a-zA-Z_0-9:]*) ?\} ?\) do/
400
+ @replace_pattern = "\\1class \\2 < Platform::Objects::Base\n\\1 model_name \"\\3\""
401
+ end
402
+
403
+ def apply(input_text)
404
+ input_text.sub(@find_pattern, @replace_pattern)
405
+ end
406
+ end
407
+
408
+ def custom_upgrade(original_text)
409
+ # Replace the default one with a custom one:
410
+ type_transforms = GraphQL::Upgrader::Member::DEFAULT_TYPE_TRANSFORMS.map { |t|
411
+ if t == GraphQL::Upgrader::TypeDefineToClassTransform
412
+ GraphQL::Upgrader::TypeDefineToClassTransform.new(base_class_pattern: "Platform::\\2s::Base")
413
+ else
414
+ t
415
+ end
218
416
  }
219
- assert_equal upgrade(old), new
417
+
418
+ type_transforms.unshift(ActiveRecordTypeToClassTransform)
419
+ upgrader = GraphQL::Upgrader::Member.new(original_text, type_transforms: type_transforms)
420
+ upgrader.upgrade
421
+ end
422
+
423
+ original_files = Dir.glob("spec/fixtures/upgrader/*.original.rb")
424
+ original_files.each do |original_file|
425
+ transformed_file = original_file.sub(".original.", ".transformed.")
426
+ it "transforms #{original_file} -> #{transformed_file}" do
427
+ original_text = File.read(original_file)
428
+ expected_text = File.read(transformed_file)
429
+ transformed_text = custom_upgrade(original_text)
430
+ assert_equal(expected_text, transformed_text)
431
+ end
220
432
  end
221
433
  end
222
434
  end