Skip to content

Commit 6c49772

Browse files
authored
Merge pull request #216 from movio/comma-chameleon
Handle empty fragment spread being first in query
2 parents 2f9e0bf + 77261c0 commit 6c49772

2 files changed

Lines changed: 168 additions & 2 deletions

File tree

execution_result.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,8 @@ func formatResponseDataRec(schema *ast.Schema, selectionSet ast.SelectionSet, re
320320
objectTypename := extractAndCastTypenameField(result)
321321
filteredSelectionSet := unionAndTrimSelectionSet(objectTypename, schema, selectionSet)
322322

323-
for i, selection := range filteredSelectionSet {
323+
itemWritten := false
324+
for _, selection := range filteredSelectionSet {
324325
var innerBody []byte
325326
switch selection := selection.(type) {
326327
case *ast.InlineFragment:
@@ -349,10 +350,11 @@ func formatResponseDataRec(schema *ast.Schema, selectionSet ast.SelectionSet, re
349350
innerBody = innerBuf.Bytes()
350351
}
351352
if len(innerBody) > 0 {
352-
if i > 0 {
353+
if itemWritten {
353354
buf.WriteString(",")
354355
}
355356
buf.Write(innerBody)
357+
itemWritten = true
356358
}
357359
}
358360
if !insideFragment {

execution_result_test.go

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1637,6 +1637,170 @@ func TestFormatResponseBody(t *testing.T) {
16371637
require.JSONEq(t, expectedJSON, string(bodyJSON))
16381638
})
16391639

1640+
t.Run("multiple implementations with empty fragment spreads before other fields", func(t *testing.T) {
1641+
ddl := `
1642+
interface Gizmo {
1643+
id: ID!
1644+
name: String!
1645+
}
1646+
1647+
type Gadget implements Gizmo {
1648+
id: ID!
1649+
name: String!
1650+
owner: String!
1651+
}
1652+
1653+
type Tool implements Gizmo {
1654+
id: ID!
1655+
name: String!
1656+
category: String!
1657+
}
1658+
1659+
type Query {
1660+
gizmos: [Gizmo!]!
1661+
}
1662+
`
1663+
1664+
result := jsonToInterfaceMap(`{
1665+
"gizmos": [
1666+
{
1667+
"id": "GADGET1",
1668+
"name": "Gadget #1",
1669+
"owner": "Bob",
1670+
"__typename": "Gadget",
1671+
"_bramble__typename": "Gadget"
1672+
},
1673+
{
1674+
"id": "GADGET2",
1675+
"name": "Gadget #2",
1676+
"category": "Plastic",
1677+
"__typename": "Tool",
1678+
"_bramble__typename": "Tool"
1679+
}
1680+
]
1681+
}`)
1682+
1683+
schema := gqlparser.MustLoadSchema(&ast.Source{Name: "fixture", Input: ddl})
1684+
1685+
query := `
1686+
query Gizmo {
1687+
gizmos {
1688+
...GizmoDetails
1689+
id
1690+
}
1691+
}
1692+
1693+
fragment GizmoDetails on Gizmo {
1694+
... on Gadget {
1695+
name
1696+
owner
1697+
}
1698+
}`
1699+
1700+
expectedJSON := `
1701+
{
1702+
"gizmos": [
1703+
{
1704+
"id": "GADGET1",
1705+
"name": "Gadget #1",
1706+
"owner": "Bob"
1707+
},
1708+
{
1709+
"id": "GADGET2"
1710+
}
1711+
]
1712+
}`
1713+
1714+
document := gqlparser.MustLoadQuery(schema, query)
1715+
bodyJSON := formatResponseData(schema, document.Operations[0].SelectionSet, result)
1716+
require.JSONEq(t, expectedJSON, string(bodyJSON))
1717+
})
1718+
1719+
t.Run("multiple implementations with multiple empty fragment spreads around other fields", func(t *testing.T) {
1720+
ddl := `
1721+
interface Gizmo {
1722+
id: ID!
1723+
name: String!
1724+
}
1725+
1726+
type Gadget implements Gizmo {
1727+
id: ID!
1728+
name: String!
1729+
owner: String!
1730+
}
1731+
1732+
type Tool implements Gizmo {
1733+
id: ID!
1734+
name: String!
1735+
category: String!
1736+
}
1737+
1738+
type Query {
1739+
gizmos: [Gizmo!]!
1740+
}
1741+
`
1742+
1743+
result := jsonToInterfaceMap(`{
1744+
"gizmos": [
1745+
{
1746+
"id": "GADGET1",
1747+
"name": "Gadget #1",
1748+
"owner": "Bob",
1749+
"__typename": "Gadget",
1750+
"_bramble__typename": "Gadget"
1751+
},
1752+
{
1753+
"id": "GADGET2",
1754+
"name": "Gadget #2",
1755+
"category": "Plastic",
1756+
"__typename": "Tool",
1757+
"_bramble__typename": "Tool"
1758+
}
1759+
]
1760+
}`)
1761+
1762+
schema := gqlparser.MustLoadSchema(&ast.Source{Name: "fixture", Input: ddl})
1763+
1764+
query := `
1765+
query Gizmo {
1766+
gizmos {
1767+
...GizmoName
1768+
id
1769+
...GizmoDetails
1770+
}
1771+
}
1772+
1773+
fragment GizmoName on Gizmo {
1774+
... on Gadget {
1775+
name
1776+
}
1777+
}
1778+
1779+
fragment GizmoDetails on Gizmo {
1780+
... on Gadget {
1781+
owner
1782+
}
1783+
}`
1784+
1785+
expectedJSON := `
1786+
{
1787+
"gizmos": [
1788+
{
1789+
"id": "GADGET1",
1790+
"name": "Gadget #1",
1791+
"owner": "Bob"
1792+
},
1793+
{
1794+
"id": "GADGET2"
1795+
}
1796+
]
1797+
}`
1798+
1799+
document := gqlparser.MustLoadQuery(schema, query)
1800+
bodyJSON := formatResponseData(schema, document.Operations[0].SelectionSet, result)
1801+
require.JSONEq(t, expectedJSON, string(bodyJSON))
1802+
})
1803+
16401804
t.Run("multiple implementation fragment spreads (bottom fragment matches)", func(t *testing.T) {
16411805
ddl := `
16421806
interface Gizmo {

0 commit comments

Comments
 (0)