You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
@@ -9,45 +9,74 @@ desc: Creating a set of modifications to release in an API version
9
9
index: 2
10
10
---
11
11
12
-
After {% internal_link "installing Changeset integrations", "/changesets/installation" %} in your schema, you can create Changesets which modify parts of the schema. Changesets extend `GraphQL::Enterprise::Changeset` and include a `release`and some `modifies ...` configurations.
12
+
After {% internal_link "installing Changeset integrations", "/changesets/installation" %} in your schema, you can create Changesets which modify parts of the schema. Changesets extend `GraphQL::Enterprise::Changeset` and include a `release`string. Once a Changeset class is defined, it can be referenced with `added_in: ...`or `removed_in: ...`configurations in the schema.
13
13
14
-
For example, this Changeset marks the old `Recipe.flag` field as deprecated:
14
+
__Note:__ Before GraphQL-Enterprise 1.3.0, Changesets were configured with `modifies ...` blocks. These blocks are still supported and you can find the documentation for that API [on GitHub](https://github.com/rmosolgo/graphql-ruby/blob/v2.0.22/guides/changesets/definition.md).
15
+
16
+
17
+
## Changeset Classes
18
+
19
+
This Changeset will be available to any client whose `context[:changeset_version]` is on or after `2020-12-01`:
Additionally, the Changesets must be added to the schema (see the {% internal_link "Releases guide", "/changesets/releases" %}):
41
+
You can also provide a _replacement_ implementation by using `added_in:`. When a new definition has the same name as an existing definition, it implicitly replaces the previous definition in new versions of the API. For example:
39
42
40
43
```ruby
41
-
classMyAppSchema < GraphQL::Schema
42
-
# ...
43
-
use GraphQL::Enterprise::Changeset::Release, changesets: [
44
-
Changesets::DeprecateRecipeFlag,
45
-
Changesets::RemoveRecipeFlag,
46
-
]
44
+
field :rating, Integer, "A 1-5 score for this recipe"# This definition will be superseded by the following one
45
+
field :rating, Float, "A 1.0-5.0 score for this recipe", added_in:Changesets::FloatingPointRatings
46
+
```
47
+
48
+
Here, a new implementation for `rating` will be used when clients requests an API version that includes `Changesets::FloatingPointRatings`. (If the client requests a version _before_ that changeset, then the preceding implementation would be used instead.)
49
+
50
+
## Removing with `removed_in:`
51
+
52
+
A `removed_in:` configuration removes something in the named changeset. For example, these enum values are replaced with more clearly-named ones:
53
+
54
+
```ruby
55
+
classTypes::RecipeTag < Types::BaseEnum
56
+
# These are replaced by *_HEAT below:
57
+
value :SPICY, removed_in:Changesets::ClarifyHeatTags
58
+
value :MEDIUM, removed_in:Changesets::ClarifyHeatTags
59
+
value :MILD, removed_in:Changesets::ClarifyHeatTags
60
+
# These new tags are more clear:
61
+
value :SPICY_HEAT, added_in:Changesets::ClarifyHeatTags
62
+
value :MEDIUM_HEAT, added_in:Changesets::ClarifyHeatTags
63
+
value :MILD_HEAT, added_in:Changesets::ClarifyHeatTags
64
+
end
65
+
```
66
+
67
+
If something has been defined several times, a `removed_in:` configuration removes _all_ definitions:
Although the changesets above have one modification each, a changeset may have any number of modifications in it.
79
+
## Examples
51
80
52
81
See below for the different kind of modifications you can make in a changeset:
53
82
@@ -59,114 +88,126 @@ See below for the different kind of modifications you can make in a changeset:
59
88
-[Types](#types): changing one type definition for another
60
89
-[Runtime](#runtime): choosing a behavior at runtime based on the current request and changeset
61
90
62
-
## Fields
91
+
###Fields
63
92
64
-
In a Changeset, you can add, redefine, or remove fields that belong to object types, interface types, or resolvers. First, use `modifies ... do ... end`, naming the owner of the field:
93
+
To add or redefine a field, use `field(..., added_in: ...)`, including all configuration values for the new implementation (see {{ "GraphQL::Schema::Field#initialize" | api_doc }}). The definition given here will override the previous definition (if there was one) whenever this Changeset applies.
# This new field is available when `context[:changeset_version]`
98
+
# is on or after the release date of `AddRecipeTags`
99
+
field :tags, [Types::RecipeTag], added_in:Changeset::AddRecipeTags
71
100
end
72
101
```
73
102
74
-
Then...
103
+
To remove a field, add a `removed_in: ...` configuration to the last definition of the field:
75
104
76
-
- To add or redefine a field, use `field(...)`, including the same configurations you'd use in a type definition (see {{ "GraphQL::Schema::Field#initialize" | api_doc }}). The definition given here will override the previous definition (if there was one) whenever this Changeset applies.
77
-
- To remove a field, use `remove_field(field_name)`, where `field_name` is the name given to `field(...)` (usually an underscore-cased symbol)
105
+
```ruby
106
+
classTypes::Recipe < Types::BaseObject
107
+
# Even after migrating to floating point values,
108
+
# the "rating" feature never took off,
109
+
# so we removed it entirely eventually.
110
+
field :rating, Integer
111
+
field :rating, Float, added_in:Changeset::FloatingPointRatings,
112
+
removed_in:Changeset::RemoveRatings
113
+
end
114
+
```
78
115
79
116
When a field is removed, queries that request that field will be invalid, unless the client has requested a previous API version where the field is still available.
80
117
81
-
## Arguments
118
+
###Arguments
82
119
83
-
In a Changeset, you can add, redefine, or remove arguments that belong to fields, input objects, or resolvers. Use `modifies` to select the argument owner, for example:
120
+
You can add, redefine, or remove arguments that belong to fields, input objects, or resolvers. Use `added_in: ...` to provide a new (or updated) definition for an argument, for example:
When versioning field arguments, use a second `modifies(field_name) { ... }` call to select the field to modify:
131
+
To remove an argument entirely, add a `removed_in: ...` configuration to the last definition. It will remove _all_ implementations for that argument. For example:
94
132
95
133
```ruby
96
-
# ...
97
-
modifies Types::Querydo
98
-
modifies :ingredientsdo
99
-
# modify the arguments of `Query.ingredients(...)` here
- To add or redefine an argument, use `argument(...)`, passing the same configurations you'd usually pass to `argument(...)` (see {{ "GraphQL::Schema::Argument#initialize" | api_doc }}). The redefined argument will override any previous definitions whenever this Changeset is active.
108
-
- To remove an argument, use `remove_argument(argument_name)`, where `argument_name` is the name given to `field(...)` (usually an underscore-cased symbol)
109
-
110
140
When arguments are removed, the schema will reject any queries which use them unless the client has requested a previous API version where the argument is still allowed.
111
141
112
-
## Enum Values
142
+
###Enum Values
113
143
114
-
In a Changeset, you can add, redefine, or remove enum values. First, use `modifies ... do ... end`, naming the enum type:
144
+
With Changesets, you can add, redefine, or remove enum values. To add a new value (or provide a new implementation for a value), include `added_in:` in the `value(...)` configuration:
# This enum will accept and return `KETO` only when the client's API version
149
+
# includes `AddKetoDietSupport`'s release date.
150
+
value :KETO, added_in:Changesets::AddKetoDietSupport
121
151
end
122
152
```
123
153
124
-
Then...
154
+
Values can be removed with `removed_in:`, for example:
125
155
126
-
- To add a value, use `value(...)`, passing the same configurations you'd usually pass to `value(...)` in an enum type (see {{ "GraphQL::Schema::Enum.value" | api_doc }}). The configuration given here will override previous configurations whenever this Changeset applies.
127
-
- To remove a value, use `remove_value(name)`, where `name` is the name given to `value(...)` (an all-caps string)
156
+
```ruby
157
+
classTypes::RecipeTag < Types::BaseEnum
158
+
# Old API versions will serve this value;
159
+
# new versions won't accept it or return it.
160
+
value :GRAPEFRUIT_DIET, removed_in:Changesets::RemoveLegacyDiets
161
+
end
162
+
```
128
163
129
164
When enum values are removed, they won't be accepted as input and they won't be allowed as return values from fields unless the client has requested a previous API version where those values are still allowed.
130
165
131
-
## Unions
166
+
###Unions
132
167
133
-
In a Changeset, you can add to or remove from a union's possible types. First, use `modifies ...`, naming the union type:
168
+
You can add to or remove from a union's possible types. To release a new union member, include `added_in:` in the `possible_types` configuration:
To remove a member from a union, move it to a `possible_types` call with `removed_in: ...`:
144
178
145
-
- To add one or more possible types, use `possible_types(*object_types)`, passing one or more object type classes. The given types will be _added_ to the union's set of possible types whenever this Changeset is active.
146
-
- To remove one or more more possible types, use `remove_possible_types(*object_types)`, passing one or more object type classes
179
+
```ruby
180
+
# Stop including this in the union in new API versions:
When a possible type is removed, it will not be associated with the union type in introspection queries or schema dumps.
149
185
150
-
## Interfaces
186
+
###Interfaces
151
187
152
-
In a Changeset, you can add to or remove from an object type's interface definitions. First, use `modifies ...`, naming the object type:
188
+
You can add to or remove from an object type's interface definitions. To add one or more interface implementations, use `implements(..., added_in:)`. This will add the interface and its fields to the object whenever this Changeset is active, for example:
- To add one or more interface implementations, use `implements(*interface_types)`, passing one or more interface type modules. This will add the interface and its fields to the object whenever this Changeset is active.
165
-
- To remove one or more more interface implementations, use `remove_implements(*interface_types)`, passing one or more interface type modules
198
+
To remove one or more more interface implementations, add `removed_in:` to the `implements ...` configuration, for example:
199
+
200
+
```ruby
201
+
implements Types::RssSubject,
202
+
added_in:Changesets::AddRssSupport,
203
+
# Sadly, nobody seems to want to use this,
204
+
# so we removed it all:
205
+
removed_in:Changesets::RemoveRssSupport
206
+
```
166
207
167
208
When an interface implementation is removed, then the interface will not be associated with the object in introspection queries or schema dumps. Also, any fields inherited from the interface will be hidden from clients. (If the object defines the field itself, it will still be visible.)
168
209
169
-
## Types
210
+
###Types
170
211
171
212
Using Changesets, it's possible to define a new type using the same name as an old type. (Only one type per name is allowed for each query, but different queries can use different types for the same name.)
172
213
@@ -175,20 +216,20 @@ First, to define two types with the same name, make two different type definitio
175
216
```ruby
176
217
# app/graphql/types/legacy_recipe_flag.rb
177
218
178
-
# In the old version of the schema, "recipe flags" were limited to defined set of values.
179
-
# This enum was renamed from `Types::RecipeFlag`, then `graphql_name("RecipeFlag")`
219
+
# In the old version of the schema, "recipe tags" were limited to defined set of values.
220
+
# This enum was renamed from `Types::RecipeTag`, then `graphql_name("RecipeTag")`
180
221
# was added for GraphQL.
181
-
classTypes::LegacyRecipeFlag < Types::BaseEnum
182
-
graphql_name "RecipeFlag"
222
+
classTypes::LegacyRecipeTag < Types::BaseEnum
223
+
graphql_name "RecipeTag"
183
224
# ...
184
225
end
185
226
```
186
227
187
228
```ruby
188
229
# app/graphql/types/recipe_flag.rb
189
230
190
-
# But in the new schema, each flag is a full-fledge object with fields of its own
191
-
classTypes::RecipeFlag < Types::BaseObject
231
+
# But in the new schema, each tag is a full-fledged object with fields of its own
232
+
classTypes::RecipeTag < Types::BaseObject
192
233
field :name, String, null:false
193
234
field :is_vegetarian, Boolean, null:false
194
235
# ...
@@ -197,28 +238,34 @@ end
197
238
198
239
Then, add or update fields or arguments to use the _new_ type instead of the old one. For example:
# in types/recipe.rb, this is defined with `field :flags, [Types::LegacyRecipeFlag]`
204
-
# Here, update the field to use the _object_ instead:
205
-
update_field :flags, [Types::RecipeFlag]
241
+
```diff
242
+
class Types::Recipe < Types::BaseObject
243
+
244
+
# Change this definition to point at the newly-renamed _legacy_ type
245
+
# (It's the same type definition, but the Ruby class has a new name)
246
+
- field :tags, [Types::RecipeTag]
247
+
+ field :tags, [Types::LegacyRecipeTag]
248
+
249
+
# And add a new field for the new type:
250
+
+ field :tags, [Types::RecipeTag], added_in: Changesets::MigrateRecipeTagToObject
206
251
end
207
-
end
208
252
```
209
253
210
-
With that Changeset, `Recipe.flags` will return an object type instead of an enum type. Clients requesting older versions will still receive enum values from that field.
254
+
With that Changeset, `Recipe.tags` will return an object type instead of an enum type. Clients requesting older versions will still receive enum values from that field.
211
255
212
256
The resolver will probably need an update, too, for example:
213
257
214
258
```ruby
215
259
classTypes::Recipe < Types::BaseObject
216
-
# Here's the original definition, which is modified by `MigrateRecipeFlagToObject`:
217
-
field :flags, [Types::LegacyRecipeFlag], null:false
260
+
# Here's the original definition which returns enum values:
261
+
field :tags, [Types::LegacyRecipeTag], null:false
262
+
# Here's the new definition which replaces the previous one on new API versions:
263
+
field :tags, [Types::RecipeTag], null:false, added_in:Changesets::MigrateRecipeTagToObject
0 commit comments