@distilled.cloud/core 1.0.0-rc.12 → 1.0.0-rc.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/api.d.ts +4 -4
- package/lib/api.d.ts.map +1 -1
- package/lib/api.js +1 -1
- package/lib/api.js.map +1 -1
- package/lib/category.js +1 -1
- package/lib/category.js.map +1 -1
- package/lib/codegen/boolean-string-enums.d.ts +8 -8
- package/lib/codegen/boolean-string-enums.d.ts.map +1 -1
- package/lib/codegen/boolean-string-enums.js +84 -27
- package/lib/codegen/boolean-string-enums.js.map +1 -1
- package/lib/codegen/boolean-string-enums.test.js +130 -0
- package/lib/codegen/boolean-string-enums.test.js.map +1 -1
- package/lib/codegen/cli.js +2 -2
- package/lib/codegen/cli.js.map +1 -1
- package/lib/codegen/generator.d.ts +20 -3
- package/lib/codegen/generator.d.ts.map +1 -1
- package/lib/codegen/generator.js +36 -9
- package/lib/codegen/generator.js.map +1 -1
- package/lib/codegen/generator.test.d.ts +2 -0
- package/lib/codegen/generator.test.d.ts.map +1 -0
- package/lib/codegen/generator.test.js +126 -0
- package/lib/codegen/generator.test.js.map +1 -0
- package/lib/codegen/graphql-client.d.ts +7 -1
- package/lib/codegen/graphql-client.d.ts.map +1 -1
- package/lib/codegen/graphql-client.js +262 -125
- package/lib/codegen/graphql-client.js.map +1 -1
- package/lib/codegen/graphql-client.test.js +104 -236
- package/lib/codegen/graphql-client.test.js.map +1 -1
- package/lib/codegen/openapi-binary.test.d.ts +2 -0
- package/lib/codegen/openapi-binary.test.d.ts.map +1 -0
- package/lib/codegen/openapi-binary.test.js +67 -0
- package/lib/codegen/openapi-binary.test.js.map +1 -0
- package/lib/codegen/openapi.d.ts +7 -1
- package/lib/codegen/openapi.d.ts.map +1 -1
- package/lib/codegen/openapi.js +15 -4
- package/lib/codegen/openapi.js.map +1 -1
- package/lib/codegen/patches.d.ts +16 -1
- package/lib/codegen/patches.d.ts.map +1 -1
- package/lib/codegen/patches.js +70 -4
- package/lib/codegen/patches.js.map +1 -1
- package/lib/codegen/patches.test.js +48 -2
- package/lib/codegen/patches.test.js.map +1 -1
- package/lib/codegen/untagged-unions.test.d.ts +2 -0
- package/lib/codegen/untagged-unions.test.d.ts.map +1 -0
- package/lib/codegen/untagged-unions.test.js +80 -0
- package/lib/codegen/untagged-unions.test.js.map +1 -0
- package/lib/errors.d.ts +2 -1
- package/lib/errors.d.ts.map +1 -1
- package/lib/errors.js +5 -2
- package/lib/errors.js.map +1 -1
- package/lib/fixtures/xml/upstream-validator.json +397 -0
- package/lib/graphql.d.ts +296 -205
- package/lib/graphql.d.ts.map +1 -1
- package/lib/graphql.js +968 -631
- package/lib/graphql.js.map +1 -1
- package/lib/graphql.test.js +134 -763
- package/lib/graphql.test.js.map +1 -1
- package/lib/graphql.types.d.ts +10 -0
- package/lib/graphql.types.d.ts.map +1 -1
- package/lib/graphql.types.js +4 -44
- package/lib/graphql.types.js.map +1 -1
- package/lib/index.d.ts +27 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +27 -0
- package/lib/index.js.map +1 -0
- package/lib/pagination.d.ts +2 -1
- package/lib/pagination.d.ts.map +1 -1
- package/lib/pagination.js +24 -16
- package/lib/pagination.js.map +1 -1
- package/lib/pagination.test.d.ts +2 -0
- package/lib/pagination.test.d.ts.map +1 -0
- package/lib/pagination.test.js +134 -0
- package/lib/pagination.test.js.map +1 -0
- package/lib/protocol-http.d.ts +12 -17
- package/lib/protocol-http.d.ts.map +1 -1
- package/lib/protocol-http.js +100 -45
- package/lib/protocol-http.js.map +1 -1
- package/lib/protocol-http.test.js +130 -0
- package/lib/protocol-http.test.js.map +1 -1
- package/lib/protocol-rest.d.ts +18 -1
- package/lib/protocol-rest.d.ts.map +1 -1
- package/lib/protocol-rest.js +24 -4
- package/lib/protocol-rest.js.map +1 -1
- package/lib/query.d.ts +29 -0
- package/lib/query.d.ts.map +1 -0
- package/lib/query.js +27 -0
- package/lib/query.js.map +1 -0
- package/lib/response-validation.d.ts +67 -0
- package/lib/response-validation.d.ts.map +1 -0
- package/lib/response-validation.js +76 -0
- package/lib/response-validation.js.map +1 -0
- package/lib/response-validation.test.d.ts +2 -0
- package/lib/response-validation.test.d.ts.map +1 -0
- package/lib/response-validation.test.js +67 -0
- package/lib/response-validation.test.js.map +1 -0
- package/lib/testing.d.ts +39 -0
- package/lib/testing.d.ts.map +1 -0
- package/lib/testing.js +41 -0
- package/lib/testing.js.map +1 -0
- package/lib/trait.d.ts +29 -9
- package/lib/trait.d.ts.map +1 -1
- package/lib/trait.js +8 -0
- package/lib/trait.js.map +1 -1
- package/lib/xml.d.ts +44 -0
- package/lib/xml.d.ts.map +1 -0
- package/lib/xml.js +315 -0
- package/lib/xml.js.map +1 -0
- package/lib/xml.test.d.ts +2 -0
- package/lib/xml.test.d.ts.map +1 -0
- package/lib/xml.test.js +211 -0
- package/lib/xml.test.js.map +1 -0
- package/package.json +4 -4
- package/src/api.ts +4 -4
- package/src/category.ts +1 -1
- package/src/codegen/boolean-string-enums.test.ts +147 -0
- package/src/codegen/boolean-string-enums.ts +84 -27
- package/src/codegen/cli.ts +2 -2
- package/src/codegen/generator.test.ts +151 -0
- package/src/codegen/generator.ts +70 -12
- package/src/codegen/graphql-client.test.ts +116 -283
- package/src/codegen/graphql-client.ts +326 -169
- package/src/codegen/openapi-binary.test.ts +98 -0
- package/src/codegen/openapi.ts +26 -4
- package/src/codegen/patches.test.ts +66 -1
- package/src/codegen/patches.ts +84 -4
- package/src/codegen/untagged-unions.test.ts +89 -0
- package/src/errors.ts +5 -2
- package/src/fixtures/xml/upstream-validator.json +397 -0
- package/src/graphql.test.ts +174 -948
- package/src/graphql.ts +1592 -1167
- package/src/graphql.types.ts +92 -163
- package/src/index.ts +26 -0
- package/src/pagination.test.ts +191 -0
- package/src/pagination.ts +35 -25
- package/src/protocol-http.test.ts +163 -0
- package/src/protocol-http.ts +122 -46
- package/src/protocol-rest.ts +62 -13
- package/src/query.ts +39 -0
- package/src/response-validation.test.ts +95 -0
- package/src/response-validation.ts +116 -0
- package/src/testing.ts +78 -0
- package/src/trait.ts +39 -8
- package/src/xml.test.ts +269 -0
- package/src/xml.ts +339 -0
- package/lib/graphql.fixture.d.ts +0 -249
- package/lib/graphql.fixture.d.ts.map +0 -1
- package/lib/graphql.fixture.js +0 -240
- package/lib/graphql.fixture.js.map +0 -1
- package/src/graphql.fixture.ts +0 -371
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
|
-
import { buildSchema, introspectionFromSchema
|
|
3
|
-
import { compile } from "../graphql.ts";
|
|
4
|
-
import { fixtureSDL } from "../graphql.fixture.ts";
|
|
2
|
+
import { buildSchema, introspectionFromSchema } from "graphql";
|
|
5
3
|
import { applyOperation } from "../json-patch.ts";
|
|
6
4
|
import {
|
|
7
5
|
convertGraphQLClient,
|
|
@@ -12,35 +10,18 @@ import {
|
|
|
12
10
|
|
|
13
11
|
const named = (name: string, kind = "OBJECT") => ({ kind, name });
|
|
14
12
|
const required = (ofType: object) => ({ kind: "NON_NULL", ofType });
|
|
15
|
-
const list = (ofType: object) => ({ kind: "LIST", ofType });
|
|
16
13
|
const field = (name: string, type: object, args: object[] = []) => ({
|
|
17
14
|
name,
|
|
18
15
|
type,
|
|
19
16
|
args,
|
|
20
17
|
});
|
|
18
|
+
|
|
21
19
|
const fixture = {
|
|
22
20
|
queryType: { name: "Query" },
|
|
23
21
|
mutationType: { name: "Mutation" },
|
|
24
22
|
types: [
|
|
25
23
|
{ kind: "SCALAR", name: "String" },
|
|
26
24
|
{ kind: "SCALAR", name: "Int" },
|
|
27
|
-
{ kind: "SCALAR", name: "JSON" },
|
|
28
|
-
{
|
|
29
|
-
kind: "ENUM",
|
|
30
|
-
name: "Status",
|
|
31
|
-
enumValues: [{ name: "ACTIVE" }, { name: "OLD", isDeprecated: true }],
|
|
32
|
-
},
|
|
33
|
-
{
|
|
34
|
-
kind: "INPUT_OBJECT",
|
|
35
|
-
name: "Filter",
|
|
36
|
-
inputFields: [
|
|
37
|
-
{
|
|
38
|
-
name: "status",
|
|
39
|
-
type: named("Status", "ENUM"),
|
|
40
|
-
defaultValue: "ACTIVE",
|
|
41
|
-
},
|
|
42
|
-
],
|
|
43
|
-
},
|
|
44
25
|
{
|
|
45
26
|
kind: "OBJECT",
|
|
46
27
|
name: "Query",
|
|
@@ -48,7 +29,13 @@ const fixture = {
|
|
|
48
29
|
field("project", required(named("Project")), [
|
|
49
30
|
{ name: "id", type: required(named("String", "SCALAR")) },
|
|
50
31
|
]),
|
|
51
|
-
field("
|
|
32
|
+
field("me", required(named("User"))),
|
|
33
|
+
field("projects", required(named("ProjectConnection")), [
|
|
34
|
+
{
|
|
35
|
+
name: "first",
|
|
36
|
+
type: named("Int", "SCALAR"),
|
|
37
|
+
},
|
|
38
|
+
]),
|
|
52
39
|
],
|
|
53
40
|
},
|
|
54
41
|
{
|
|
@@ -56,47 +43,57 @@ const fixture = {
|
|
|
56
43
|
name: "Mutation",
|
|
57
44
|
fields: [
|
|
58
45
|
field("projectCreate", named("Project"), [
|
|
59
|
-
{ name: "
|
|
46
|
+
{ name: "name", type: required(named("String", "SCALAR")) },
|
|
60
47
|
]),
|
|
61
48
|
],
|
|
62
49
|
},
|
|
63
50
|
{
|
|
64
51
|
kind: "OBJECT",
|
|
65
52
|
name: "Project",
|
|
66
|
-
interfaces: [named("Node", "INTERFACE")],
|
|
67
53
|
fields: [
|
|
68
54
|
field("id", required(named("String", "SCALAR"))),
|
|
69
|
-
field("
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
55
|
+
field("name", required(named("String", "SCALAR"))),
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
kind: "OBJECT",
|
|
60
|
+
name: "User",
|
|
61
|
+
fields: [
|
|
62
|
+
field("email", required(named("String", "SCALAR"))),
|
|
63
|
+
field("name", named("String", "SCALAR")),
|
|
64
|
+
field("projects", required(named("ProjectConnection")), [
|
|
65
|
+
{ name: "first", type: named("Int", "SCALAR") },
|
|
76
66
|
]),
|
|
77
|
-
{
|
|
78
|
-
...field("legacy", named("String", "SCALAR")),
|
|
79
|
-
isDeprecated: true,
|
|
80
|
-
deprecationReason: "Use id",
|
|
81
|
-
},
|
|
82
67
|
],
|
|
83
68
|
},
|
|
84
69
|
{
|
|
85
70
|
kind: "OBJECT",
|
|
86
|
-
name: "
|
|
87
|
-
fields: [
|
|
71
|
+
name: "ProjectConnection",
|
|
72
|
+
fields: [
|
|
73
|
+
field(
|
|
74
|
+
"edges",
|
|
75
|
+
required({ kind: "LIST", ofType: named("ProjectEdge") }),
|
|
76
|
+
),
|
|
77
|
+
field("pageInfo", required(named("PageInfo"))),
|
|
78
|
+
],
|
|
88
79
|
},
|
|
89
80
|
{
|
|
90
|
-
kind: "
|
|
91
|
-
name: "
|
|
92
|
-
fields: [
|
|
93
|
-
|
|
81
|
+
kind: "OBJECT",
|
|
82
|
+
name: "ProjectEdge",
|
|
83
|
+
fields: [
|
|
84
|
+
field("cursor", required(named("String", "SCALAR"))),
|
|
85
|
+
field("node", required(named("Project"))),
|
|
86
|
+
],
|
|
94
87
|
},
|
|
95
88
|
{
|
|
96
|
-
kind: "
|
|
97
|
-
name: "
|
|
98
|
-
|
|
89
|
+
kind: "OBJECT",
|
|
90
|
+
name: "PageInfo",
|
|
91
|
+
fields: [
|
|
92
|
+
field("endCursor", named("String", "SCALAR")),
|
|
93
|
+
field("hasNextPage", required(named("Boolean", "SCALAR"))),
|
|
94
|
+
],
|
|
99
95
|
},
|
|
96
|
+
{ kind: "SCALAR", name: "Boolean" },
|
|
100
97
|
],
|
|
101
98
|
};
|
|
102
99
|
|
|
@@ -104,283 +101,119 @@ const options = {
|
|
|
104
101
|
transportImport: "./transport.ts",
|
|
105
102
|
requirementsImport: "./transport.ts",
|
|
106
103
|
requirementsType: "Requirements",
|
|
107
|
-
|
|
104
|
+
sdkName: "Railway",
|
|
108
105
|
};
|
|
109
106
|
|
|
110
|
-
describe("GraphQL
|
|
111
|
-
test("
|
|
112
|
-
const
|
|
113
|
-
type Query {
|
|
114
|
-
query: String report: String schema: String transport: String G: String
|
|
115
|
-
catchTags: String delete: String Scalars: String Selection: String
|
|
116
|
-
same: String mutationSame: String
|
|
117
|
-
}
|
|
118
|
-
type Mutation { same: String }
|
|
119
|
-
`);
|
|
120
|
-
const model = convertGraphQLClient(introspectionFromSchema(source));
|
|
121
|
-
const output = generateGraphQLClient(model, {
|
|
122
|
-
...options,
|
|
123
|
-
operationAliases: {
|
|
124
|
-
default: "delete",
|
|
125
|
-
deleteField: "delete",
|
|
126
|
-
transport: "report",
|
|
127
|
-
readReport: "report",
|
|
128
|
-
},
|
|
129
|
-
});
|
|
130
|
-
expect(output).toContain(
|
|
131
|
-
'export const queryQuery = client.operation("query", "query");',
|
|
132
|
-
);
|
|
133
|
-
expect(output).toContain(
|
|
134
|
-
'export const queryReport = client.operation("query", "report");',
|
|
135
|
-
);
|
|
136
|
-
expect(output).toContain(
|
|
137
|
-
'export const queryTransport = client.operation("query", "transport");',
|
|
138
|
-
);
|
|
139
|
-
expect(output).toContain(
|
|
140
|
-
'export const queryDelete = client.operation("query", "delete");',
|
|
141
|
-
);
|
|
142
|
-
expect(output).toContain(
|
|
143
|
-
'export const queryScalars = client.operation("query", "Scalars");',
|
|
144
|
-
);
|
|
145
|
-
expect(output).toContain(
|
|
146
|
-
'export const mutationSame2 = client.operation("mutation", "same");',
|
|
147
|
-
);
|
|
148
|
-
expect(output).toContain("export const deleteField = queryDelete;");
|
|
149
|
-
expect(output).toContain("export const readReport = queryReport;");
|
|
150
|
-
expect(output).not.toContain("export const default =");
|
|
151
|
-
expect(
|
|
152
|
-
[...output.matchAll(/export const ([A-Za-z_$][\w$]*) =/g)].map(
|
|
153
|
-
(match) => match[1],
|
|
154
|
-
),
|
|
155
|
-
).toEqual(expect.arrayContaining(["query", "report", "queryCatchTags"]));
|
|
156
|
-
const declarations = [
|
|
157
|
-
...output.matchAll(/export (?:const|class|type) ([A-Za-z_$][\w$]*)/g),
|
|
158
|
-
].map((match) => match[1]);
|
|
159
|
-
expect(new Set(declarations).size).toBe(declarations.length);
|
|
160
|
-
expect(output.match(/client\.operation\(/g)).toHaveLength(12);
|
|
161
|
-
expect(() =>
|
|
162
|
-
new Bun.Transpiler({ loader: "ts" }).transformSync(output),
|
|
163
|
-
).not.toThrow();
|
|
164
|
-
expect(
|
|
165
|
-
generateGraphQLClient(model, {
|
|
166
|
-
...options,
|
|
167
|
-
operationAliases: {
|
|
168
|
-
default: "delete",
|
|
169
|
-
deleteField: "delete",
|
|
170
|
-
transport: "report",
|
|
171
|
-
readReport: "report",
|
|
172
|
-
},
|
|
173
|
-
}),
|
|
174
|
-
).toBe(output);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
test("schema type names and patched error tags cannot shadow generated support symbols", () => {
|
|
178
|
-
const model = convertGraphQLClient(
|
|
179
|
-
introspectionFromSchema(
|
|
180
|
-
buildSchema(`
|
|
181
|
-
type Query { data: Schema }
|
|
182
|
-
type Schema { selection: Selection }
|
|
183
|
-
type Selection { id: ID! }
|
|
184
|
-
`),
|
|
185
|
-
),
|
|
186
|
-
);
|
|
187
|
-
for (const tag of ["Schema", "catchTags", "class"]) {
|
|
188
|
-
applyOperation(model, {
|
|
189
|
-
op: "add",
|
|
190
|
-
path: `/errors/${tag}`,
|
|
191
|
-
value: { matchers: [{ code: tag }] },
|
|
192
|
-
});
|
|
193
|
-
applyOperation(model, {
|
|
194
|
-
op: "add",
|
|
195
|
-
path: "/types/Query/fields/data/errors/-",
|
|
196
|
-
value: tag,
|
|
197
|
-
});
|
|
198
|
-
}
|
|
107
|
+
describe("GraphQL Query SDK generator", () => {
|
|
108
|
+
test("emits TypeMeta, interfaces, and named roots on the SDK object", () => {
|
|
109
|
+
const model = convertGraphQLClient(fixture);
|
|
199
110
|
const output = generateGraphQLClient(model, options);
|
|
111
|
+
expect(output).toContain("export interface Project");
|
|
112
|
+
expect(output).toContain("export interface User");
|
|
200
113
|
expect(output).toContain(
|
|
201
|
-
'export
|
|
114
|
+
'export const Project: TypeMeta = { name: "Project", fields: {} };',
|
|
202
115
|
);
|
|
116
|
+
expect(output).toContain("export const Railway = {");
|
|
203
117
|
expect(output).toContain(
|
|
204
|
-
'
|
|
118
|
+
'root("query", "me", User, undefined, undefined, globalErrors)',
|
|
205
119
|
);
|
|
120
|
+
expect(output).toContain('root("query", "project", Project');
|
|
121
|
+
expect(output).toContain('rootConnection("query", "projects", Project');
|
|
206
122
|
expect(output).toContain(
|
|
207
|
-
|
|
123
|
+
"Query<ReadonlyArray<Project>, RailwayGlobalError>",
|
|
208
124
|
);
|
|
209
|
-
expect(output).toContain(
|
|
210
|
-
|
|
211
|
-
|
|
125
|
+
expect(output).toContain(
|
|
126
|
+
'connectionField("projects", Project, { first: "Int" })',
|
|
127
|
+
);
|
|
128
|
+
expect(output).toContain('root("mutation", "projectCreate", Project');
|
|
129
|
+
// Nullable GraphQL types include null; non-null ones do not.
|
|
130
|
+
expect(output).toContain("readonly name: string | null;");
|
|
131
|
+
expect(output).toContain("readonly email: string;");
|
|
132
|
+
expect(output).toContain("Query<Project | null, RailwayGlobalError>");
|
|
133
|
+
expect(output).toContain("args?: { readonly first?: number | null }");
|
|
134
|
+
expect(output).toContain('from "@distilled.cloud/core/graphql"');
|
|
212
135
|
expect(() =>
|
|
213
136
|
new Bun.Transpiler({ loader: "ts" }).transformSync(output),
|
|
214
137
|
).not.toThrow();
|
|
215
138
|
});
|
|
216
139
|
|
|
217
|
-
test("
|
|
218
|
-
const
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
__typename: true,
|
|
228
|
-
__on: {
|
|
229
|
-
Project: {
|
|
230
|
-
services: { select: { edges: { node: { id: true } } } },
|
|
231
|
-
},
|
|
232
|
-
Service: { status: true, project: { parent: { name: true } } },
|
|
233
|
-
},
|
|
140
|
+
test("emits error classes and scopes them to roots", () => {
|
|
141
|
+
const model = convertGraphQLClient(fixture);
|
|
142
|
+
for (const operation of [
|
|
143
|
+
{
|
|
144
|
+
op: "add" as const,
|
|
145
|
+
path: "/errors/RailwayNotFound",
|
|
146
|
+
value: {
|
|
147
|
+
description: "Missing.",
|
|
148
|
+
category: "notFound",
|
|
149
|
+
matchers: [{ code: "NOT_FOUND" }],
|
|
234
150
|
},
|
|
235
151
|
},
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
152
|
+
{
|
|
153
|
+
op: "add" as const,
|
|
154
|
+
path: "/errors/RailwayRateLimited",
|
|
155
|
+
value: {
|
|
156
|
+
category: "throttling",
|
|
157
|
+
retryable: true,
|
|
158
|
+
matchers: [{ code: "RATE_LIMITED" }],
|
|
241
159
|
},
|
|
242
160
|
},
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
type: "Int!",
|
|
248
|
-
defaultValue: "20",
|
|
249
|
-
});
|
|
250
|
-
expect(model.types.ProjectFilter!.inputFields!.statuses).toEqual({
|
|
251
|
-
type: "[Status!]",
|
|
252
|
-
defaultValue: "[ACTIVE]",
|
|
253
|
-
});
|
|
254
|
-
const output = generateGraphQLClient(model, options);
|
|
255
|
-
expect(output).toContain('"first"?: Scalars["Int"]');
|
|
256
|
-
expect(output).toContain('"Node": "Project" | "Service"');
|
|
257
|
-
expect(output).toContain('"SearchResult": "Project" | "Service"');
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
test("required input fields with defaults are omittable but other required fields stay required", () => {
|
|
261
|
-
const schema = buildSchema(`
|
|
262
|
-
input Paging { limit: Int! = 20, owner: ID!, statuses: [String!]! = [] }
|
|
263
|
-
type Query { names(paging: Paging!): [String!]! }
|
|
264
|
-
`);
|
|
265
|
-
const model = convertGraphQLClient(introspectionFromSchema(schema));
|
|
266
|
-
const output = generateGraphQLClient(model, options);
|
|
267
|
-
expect(output).toContain('"limit"?: Scalars["Int"]');
|
|
268
|
-
expect(output).toContain('"owner": Scalars["ID"]');
|
|
269
|
-
expect(output).toContain('"statuses"?: ReadonlyArray<Scalars["String"]>');
|
|
270
|
-
const compiled = compile(model, "query", {
|
|
271
|
-
names: { where: { paging: { owner: "account" } } },
|
|
272
|
-
});
|
|
273
|
-
expect(Object.values(compiled.variables)).toEqual([{ owner: "account" }]);
|
|
274
|
-
expect(validate(schema, parse(compiled.query))).toEqual([]);
|
|
275
|
-
expect(() =>
|
|
276
|
-
compile(model, "query", { names: { where: { paging: {} } } }),
|
|
277
|
-
).toThrow("owner is required");
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
test("provider scalar overrides stay aligned in runtime metadata and generated scalar types", () => {
|
|
281
|
-
const schema = buildSchema(
|
|
282
|
-
`scalar DateTime scalar JSON type Query { date: DateTime! blob: JSON! }`,
|
|
283
|
-
);
|
|
284
|
-
const model = convertGraphQLClient(introspectionFromSchema(schema), {
|
|
285
|
-
scalars: { DateTime: "string" },
|
|
286
|
-
});
|
|
287
|
-
const output = generateGraphQLClient(model, options);
|
|
288
|
-
expect(model.types.DateTime!.scalar).toBe("string");
|
|
289
|
-
expect(model.types.JSON!.scalar).toBe("unknown");
|
|
290
|
-
expect(output).toContain('"DateTime": string;');
|
|
291
|
-
expect(output).toContain('"JSON": unknown;');
|
|
292
|
-
expect(
|
|
293
|
-
validate(
|
|
294
|
-
schema,
|
|
295
|
-
parse(compile(model, "query", { date: true, blob: true }).query),
|
|
296
|
-
),
|
|
297
|
-
).toEqual([]);
|
|
298
|
-
});
|
|
299
|
-
|
|
300
|
-
test("preserves nested required arguments, defaults, recursion, union/interface members and deprecated fields", () => {
|
|
301
|
-
const model = convertGraphQLClient({ data: { __schema: fixture } });
|
|
302
|
-
expect(model.types.Project!.fields!.children).toEqual({
|
|
303
|
-
type: "[Project!]!",
|
|
304
|
-
args: {
|
|
305
|
-
first: { type: "Int!", defaultValue: "20" },
|
|
306
|
-
filter: { type: "Filter" },
|
|
161
|
+
{
|
|
162
|
+
op: "add" as const,
|
|
163
|
+
path: "/globalErrors/-",
|
|
164
|
+
value: "RailwayRateLimited",
|
|
307
165
|
},
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
expect(model.types.Search!.possibleTypes).toEqual(["Project", "Service"]);
|
|
313
|
-
expect(model.types.Project!.fields!.legacy!.deprecated).toBe("Use id");
|
|
314
|
-
expect(model.types.Status!.enumValues).toEqual(["ACTIVE", "OLD"]);
|
|
315
|
-
expect(model.types.Filter!.inputFields!.status!.defaultValue).toBe(
|
|
316
|
-
"ACTIVE",
|
|
317
|
-
);
|
|
318
|
-
expect(model.types.JSON!.scalar).toBe("unknown");
|
|
319
|
-
validateGraphQLModel(model);
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
test("field errors patch the one model used by emitted type unions and runtime classification", () => {
|
|
323
|
-
const model = convertGraphQLClient(fixture);
|
|
324
|
-
applyOperation(model, {
|
|
325
|
-
op: "add",
|
|
326
|
-
path: "/errors/ChildrenUnavailable",
|
|
327
|
-
value: {
|
|
328
|
-
category: "server",
|
|
329
|
-
matchers: [{ code: "CHILDREN_UNAVAILABLE" }],
|
|
166
|
+
{
|
|
167
|
+
op: "add" as const,
|
|
168
|
+
path: "/types/Query/fields/project/errors/-",
|
|
169
|
+
value: "RailwayNotFound",
|
|
330
170
|
},
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
op: "add",
|
|
334
|
-
path: "/types/Project/fields/children/errors/-",
|
|
335
|
-
value: "ChildrenUnavailable",
|
|
336
|
-
});
|
|
171
|
+
])
|
|
172
|
+
applyOperation(model, operation);
|
|
337
173
|
const output = generateGraphQLClient(model, options);
|
|
338
174
|
expect(output).toContain(
|
|
339
|
-
'
|
|
175
|
+
'export class RailwayNotFound extends S.TaggedError<RailwayNotFound>()("RailwayNotFound", errorFields).pipe(Category.withNotFoundError) {}',
|
|
340
176
|
);
|
|
341
177
|
expect(output).toContain(
|
|
342
|
-
|
|
178
|
+
"Category.withThrottlingError, Category.withRetryable({ throttling: true })",
|
|
343
179
|
);
|
|
344
|
-
expect(output).toContain('"errors":["ChildrenUnavailable"]');
|
|
345
|
-
expect(output).toContain('"matchers":[{"code":"CHILDREN_UNAVAILABLE"}]');
|
|
346
180
|
expect(output).toContain(
|
|
347
|
-
'
|
|
181
|
+
'errorSpec(RailwayRateLimited, "RailwayRateLimited", [{"code":"RATE_LIMITED"}], { retryable: true, global: true })',
|
|
348
182
|
);
|
|
349
|
-
expect(output).toContain(
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
convertGraphQLClient(fixture),
|
|
355
|
-
options,
|
|
183
|
+
expect(output).toContain(
|
|
184
|
+
"export type RailwayGlobalError = RailwayRateLimited;",
|
|
185
|
+
);
|
|
186
|
+
expect(output).toContain(
|
|
187
|
+
"Query<Project, RailwayNotFound | RailwayGlobalError>",
|
|
356
188
|
);
|
|
357
|
-
expect(output).toContain(
|
|
358
|
-
expect(output).toContain(
|
|
359
|
-
expect(
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
expect(output).not.toContain("maxDepth");
|
|
189
|
+
expect(output).toContain("[RailwayNotFoundSpec, ...globalErrors]");
|
|
190
|
+
expect(output).toContain("Query<User, RailwayGlobalError>");
|
|
191
|
+
expect(() =>
|
|
192
|
+
new Bun.Transpiler({ loader: "ts" }).transformSync(output),
|
|
193
|
+
).not.toThrow();
|
|
363
194
|
});
|
|
364
195
|
|
|
365
|
-
test("unknown coordinates
|
|
196
|
+
test("unknown coordinates fail validateGraphQLModel", () => {
|
|
366
197
|
const model = convertGraphQLClient(fixture);
|
|
367
198
|
applyOperation(model, {
|
|
368
199
|
op: "add",
|
|
369
|
-
path: "/types/Project/fields/
|
|
200
|
+
path: "/types/Project/fields/id/errors/-",
|
|
370
201
|
value: "MissingError",
|
|
371
202
|
});
|
|
372
203
|
expect(() => validateGraphQLModel(model)).toThrow(
|
|
373
|
-
"Project.
|
|
204
|
+
"Project.id: unknown GraphQL error MissingError",
|
|
374
205
|
);
|
|
375
206
|
expect(() => graphqlTypeString({ kind: "NON_NULL" })).toThrow(
|
|
376
207
|
"Incomplete GraphQL NON_NULL",
|
|
377
208
|
);
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
test("SDL round-trip through convert still produces a model", () => {
|
|
212
|
+
const source = buildSchema(`
|
|
213
|
+
type Query { ping: String }
|
|
214
|
+
`);
|
|
215
|
+
const model = convertGraphQLClient(introspectionFromSchema(source));
|
|
216
|
+
expect(model.queryType).toBe("Query");
|
|
217
|
+
expect(model.types.Query?.fields?.ping?.type).toBe("String");
|
|
385
218
|
});
|
|
386
219
|
});
|