+7
CHANGELOG.md
+7
CHANGELOG.md
···
7
7
- Support for variable default values (`$name: Type = defaultValue`)
8
8
- Default values are applied during execution when variables are not provided
9
9
- Provided variables override default values
10
+
- New `schema.named_type_name` function to get the base type name without List/NonNull wrappers
11
+
12
+
### Fixed
13
+
14
+
- Fragment spread type condition matching now works correctly when parent type is wrapped in NonNull or List
15
+
- Inline fragment type condition matching now works correctly with wrapped types
16
+
- `__typename` introspection now returns the concrete type name without modifiers
10
17
11
18
### Breaking Changes
12
19
+7
birdie_snapshots/execute_fragment_spread_on_non_null_type.accepted
+7
birdie_snapshots/execute_fragment_spread_on_non_null_type.accepted
+8
-5
src/swell/executor.gleam
+8
-5
src/swell/executor.gleam
···
322
322
type_condition,
323
323
fragment_selection_set,
324
324
)) -> {
325
-
// Check type condition
326
-
let current_type_name = schema.type_name(parent_type)
325
+
// Check type condition - use named_type_name to get the base type
326
+
// without NonNull/List wrappers, since fragments are defined on named types
327
+
let current_type_name = schema.named_type_name(parent_type)
327
328
case type_condition == current_type_name {
328
329
False -> {
329
330
// Type condition doesn't match, skip this fragment
···
357
358
}
358
359
}
359
360
parser.InlineFragment(type_condition_opt, inline_selections) -> {
360
-
// Check type condition if present
361
-
let current_type_name = schema.type_name(parent_type)
361
+
// Check type condition if present - use named_type_name to get the base type
362
+
// without NonNull/List wrappers, since fragments are defined on named types
363
+
let current_type_name = schema.named_type_name(parent_type)
362
364
let should_execute = case type_condition_opt {
363
365
None -> True
364
366
Some(type_condition) -> type_condition == current_type_name
···
396
398
// Handle introspection meta-fields
397
399
case name {
398
400
"__typename" -> {
399
-
let type_name = schema.type_name(parent_type)
401
+
// Use named_type_name to return the concrete type without modifiers
402
+
let type_name = schema.named_type_name(parent_type)
400
403
Ok(#(key, value.String(type_name), []))
401
404
}
402
405
"__schema" -> {
+15
src/swell/schema.gleam
+15
src/swell/schema.gleam
···
239
239
}
240
240
}
241
241
242
+
/// Get the named (base) type name, unwrapping List and NonNull wrappers.
243
+
/// This is used for fragment type condition matching and __typename where
244
+
/// we need the concrete type name without modifiers.
245
+
pub fn named_type_name(t: Type) -> String {
246
+
case t {
247
+
ScalarType(name) -> name
248
+
ObjectType(name, _, _) -> name
249
+
InputObjectType(name, _, _) -> name
250
+
EnumType(name, _, _) -> name
251
+
UnionType(name, _, _, _) -> name
252
+
ListType(inner) -> named_type_name(inner)
253
+
NonNullType(inner) -> named_type_name(inner)
254
+
}
255
+
}
256
+
242
257
pub fn field_name(f: Field) -> String {
243
258
case f {
244
259
Field(name, _, _, _, _) -> name
+52
test/executor_test.gleam
+52
test/executor_test.gleam
···
206
206
)
207
207
}
208
208
209
+
// Test for fragment spread on NonNull wrapped type
210
+
pub fn execute_fragment_spread_on_non_null_type_test() {
211
+
// Create a schema where the user field returns a NonNull type
212
+
let user_type =
213
+
schema.object_type("User", "A user", [
214
+
schema.field("id", schema.id_type(), "User ID", fn(_ctx) {
215
+
Ok(value.String("123"))
216
+
}),
217
+
schema.field("name", schema.string_type(), "User name", fn(_ctx) {
218
+
Ok(value.String("Alice"))
219
+
}),
220
+
])
221
+
222
+
let query_type =
223
+
schema.object_type("Query", "Root query type", [
224
+
// Wrap user_type in NonNull to test fragment type condition matching
225
+
schema.field("user", schema.non_null(user_type), "Get user", fn(_ctx) {
226
+
Ok(
227
+
value.Object([
228
+
#("id", value.String("123")),
229
+
#("name", value.String("Alice")),
230
+
]),
231
+
)
232
+
}),
233
+
])
234
+
235
+
let test_schema = schema.schema(query_type, None)
236
+
237
+
// Fragment is defined on "User" (not "User!") - this should still work
238
+
let query =
239
+
"
240
+
fragment UserFields on User {
241
+
id
242
+
name
243
+
}
244
+
245
+
{ user { ...UserFields } }
246
+
"
247
+
248
+
let result = executor.execute(query, test_schema, schema.context(None))
249
+
250
+
let response = case result {
251
+
Ok(r) -> r
252
+
Error(_) -> panic as "Execution failed"
253
+
}
254
+
255
+
birdie.snap(
256
+
title: "Execute fragment spread on NonNull type",
257
+
content: format_response(response),
258
+
)
259
+
}
260
+
209
261
// Test for list fields with nested selections
210
262
pub fn execute_list_with_nested_selections_test() {
211
263
// Create a schema with a list field