@kubun/graphql 0.4.4 → 0.5.0
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/context.d.ts +10 -2
- package/lib/context.js +1 -223
- package/lib/identifiers.d.ts +0 -1
- package/lib/identifiers.js +1 -42
- package/lib/index.d.ts +0 -1
- package/lib/index.js +1 -3
- package/lib/schema.d.ts +17 -3
- package/lib/schema.js +1 -1202
- package/lib/types.d.ts +17 -1
- package/lib/types.js +1 -1
- package/lib/utils.d.ts +0 -1
- package/lib/utils.js +1 -4
- package/package.json +9 -6
- package/lib/context.d.ts.map +0 -1
- package/lib/identifiers.d.ts.map +0 -1
- package/lib/index.d.ts.map +0 -1
- package/lib/schema.d.ts.map +0 -1
- package/lib/types.d.ts.map +0 -1
- package/lib/utils.d.ts.map +0 -1
package/lib/schema.js
CHANGED
|
@@ -1,1202 +1 @@
|
|
|
1
|
-
import { AttachmentID, DocumentID, DocumentModelID, decodeID } from '@kubun/id';
|
|
2
|
-
import { REFERENCE_PREFIX } from '@kubun/protocol';
|
|
3
|
-
import { pascalCase } from 'change-case';
|
|
4
|
-
import { GraphQLBoolean, GraphQLEnumType, GraphQLFloat, GraphQLID, GraphQLInputObjectType, GraphQLInt, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLSchema, GraphQLString, isEnumType, isListType, isNonNullType } from 'graphql';
|
|
5
|
-
import { connectionArgs, connectionDefinitions, mutationWithClientMutationId, nodeDefinitions } from 'graphql-relay';
|
|
6
|
-
import { GraphQLDateTimeISO, GraphQLDID, GraphQLDuration, GraphQLEmailAddress, GraphQLJSON, GraphQLJSONObject, GraphQLURL } from 'graphql-scalars';
|
|
7
|
-
import { GraphQLAttachmentID, GraphQLDocID } from './identifiers.js';
|
|
8
|
-
import { getUniqueDocValue } from './utils.js';
|
|
9
|
-
const STRING_FORMATS = {
|
|
10
|
-
date: GraphQLString,
|
|
11
|
-
time: GraphQLString,
|
|
12
|
-
'date-time': GraphQLString,
|
|
13
|
-
duration: GraphQLDuration,
|
|
14
|
-
email: GraphQLEmailAddress,
|
|
15
|
-
uri: GraphQLURL
|
|
16
|
-
};
|
|
17
|
-
const STRING_TITLES = {
|
|
18
|
-
attachmentid: GraphQLAttachmentID,
|
|
19
|
-
did: GraphQLDID,
|
|
20
|
-
docid: GraphQLDocID
|
|
21
|
-
};
|
|
22
|
-
function getReferenceID(path) {
|
|
23
|
-
return path.slice(REFERENCE_PREFIX.length);
|
|
24
|
-
}
|
|
25
|
-
function isList(type) {
|
|
26
|
-
return isListType(type) || isNonNullType(type) && isListType(type.ofType);
|
|
27
|
-
}
|
|
28
|
-
function collectAllModelIDs(collected, record, interfaceID) {
|
|
29
|
-
const implementedBy = record[interfaceID];
|
|
30
|
-
if (implementedBy == null) {
|
|
31
|
-
throw new Error(`Interface ${interfaceID} not found in record`);
|
|
32
|
-
}
|
|
33
|
-
for (const id of implementedBy){
|
|
34
|
-
// Check if this id is itself an interface (exists in record with implementers)
|
|
35
|
-
if (record[id] != null) {
|
|
36
|
-
// The id is an interface that has implementers, resolve recursively
|
|
37
|
-
collectAllModelIDs(collected, record, id);
|
|
38
|
-
} else {
|
|
39
|
-
// The id is a concrete model
|
|
40
|
-
collected.add(id);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
export function toDocumentModelID(id) {
|
|
45
|
-
return id instanceof DocumentModelID ? id : DocumentModelID.fromString(id);
|
|
46
|
-
}
|
|
47
|
-
export function getGlobalID(id, globalID) {
|
|
48
|
-
const docModelID = toDocumentModelID(id);
|
|
49
|
-
return docModelID.isLocal ? docModelID.toGlobal(toDocumentModelID(globalID)) : docModelID;
|
|
50
|
-
}
|
|
51
|
-
export function toList(type) {
|
|
52
|
-
return new GraphQLList(new GraphQLNonNull(type));
|
|
53
|
-
}
|
|
54
|
-
export function toOuputList(type) {
|
|
55
|
-
return new GraphQLList(new GraphQLNonNull(type));
|
|
56
|
-
}
|
|
57
|
-
function createEnumValueFilterInput(type) {
|
|
58
|
-
return new GraphQLInputObjectType({
|
|
59
|
-
name: `${type.name}ValueFilter`,
|
|
60
|
-
isOneOf: true,
|
|
61
|
-
fields: {
|
|
62
|
-
isNull: {
|
|
63
|
-
type: GraphQLBoolean
|
|
64
|
-
},
|
|
65
|
-
equalTo: {
|
|
66
|
-
type
|
|
67
|
-
},
|
|
68
|
-
notEqualTo: {
|
|
69
|
-
type
|
|
70
|
-
},
|
|
71
|
-
in: {
|
|
72
|
-
type: toList(type)
|
|
73
|
-
},
|
|
74
|
-
notIn: {
|
|
75
|
-
type: toList(type)
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
function createScalarValueFilterInput(type) {
|
|
81
|
-
return new GraphQLInputObjectType({
|
|
82
|
-
name: `${type.name}ValueFilter`,
|
|
83
|
-
isOneOf: true,
|
|
84
|
-
fields: {
|
|
85
|
-
isNull: {
|
|
86
|
-
type: GraphQLBoolean
|
|
87
|
-
},
|
|
88
|
-
equalTo: {
|
|
89
|
-
type
|
|
90
|
-
},
|
|
91
|
-
notEqualTo: {
|
|
92
|
-
type
|
|
93
|
-
},
|
|
94
|
-
in: {
|
|
95
|
-
type: toList(type)
|
|
96
|
-
},
|
|
97
|
-
notIn: {
|
|
98
|
-
type: toList(type)
|
|
99
|
-
},
|
|
100
|
-
lessThan: {
|
|
101
|
-
type
|
|
102
|
-
},
|
|
103
|
-
lessThanOrEqualTo: {
|
|
104
|
-
type
|
|
105
|
-
},
|
|
106
|
-
greaterThan: {
|
|
107
|
-
type
|
|
108
|
-
},
|
|
109
|
-
greaterThanOrEqualTo: {
|
|
110
|
-
type
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
const VALUE_FILTER_INPUTS = {
|
|
116
|
-
AccountValueFilter: new GraphQLInputObjectType({
|
|
117
|
-
name: 'AccountValueFilter',
|
|
118
|
-
isOneOf: true,
|
|
119
|
-
fields: {
|
|
120
|
-
equalTo: {
|
|
121
|
-
type: GraphQLDID
|
|
122
|
-
},
|
|
123
|
-
notEqualTo: {
|
|
124
|
-
type: GraphQLDID
|
|
125
|
-
},
|
|
126
|
-
in: {
|
|
127
|
-
type: toList(GraphQLDID)
|
|
128
|
-
},
|
|
129
|
-
notIn: {
|
|
130
|
-
type: toList(GraphQLDID)
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
}),
|
|
134
|
-
BooleanValueFilter: new GraphQLInputObjectType({
|
|
135
|
-
name: 'BooleanValueFilter',
|
|
136
|
-
isOneOf: true,
|
|
137
|
-
fields: {
|
|
138
|
-
isNull: {
|
|
139
|
-
type: GraphQLBoolean
|
|
140
|
-
},
|
|
141
|
-
equalTo: {
|
|
142
|
-
type: GraphQLBoolean
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
}),
|
|
146
|
-
FloatValueFilter: createScalarValueFilterInput(GraphQLFloat),
|
|
147
|
-
IntValueFilter: createScalarValueFilterInput(GraphQLInt),
|
|
148
|
-
StringValueFilter: createScalarValueFilterInput(GraphQLString)
|
|
149
|
-
};
|
|
150
|
-
const VALUE_FILTER_INPUT_TYPES = {
|
|
151
|
-
GraphQLBoolean: 'BooleanValueFilter',
|
|
152
|
-
GraphQLDID: 'AccountValueFilter',
|
|
153
|
-
GraphQLFloat: 'FloatValueFilter',
|
|
154
|
-
GraphQLInt: 'IntValueFilter',
|
|
155
|
-
GraphQLString: 'StringValueFilter'
|
|
156
|
-
};
|
|
157
|
-
const PatchSetOperation = new GraphQLInputObjectType({
|
|
158
|
-
name: 'PatchSetOperation',
|
|
159
|
-
fields: {
|
|
160
|
-
path: {
|
|
161
|
-
type: new GraphQLNonNull(GraphQLString)
|
|
162
|
-
},
|
|
163
|
-
value: {
|
|
164
|
-
type: GraphQLJSON
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
const PatchRemoveOperation = new GraphQLInputObjectType({
|
|
169
|
-
name: 'PatchRemoveOperation',
|
|
170
|
-
fields: {
|
|
171
|
-
path: {
|
|
172
|
-
type: new GraphQLNonNull(GraphQLString)
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
const PatchDestinationOperation = new GraphQLInputObjectType({
|
|
177
|
-
name: 'PatchDestinationOperation',
|
|
178
|
-
fields: {
|
|
179
|
-
from: {
|
|
180
|
-
type: new GraphQLNonNull(GraphQLString)
|
|
181
|
-
},
|
|
182
|
-
path: {
|
|
183
|
-
type: new GraphQLNonNull(GraphQLString)
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
});
|
|
187
|
-
export const PatchOperation = new GraphQLInputObjectType({
|
|
188
|
-
name: 'PatchOperation',
|
|
189
|
-
isOneOf: true,
|
|
190
|
-
fields: {
|
|
191
|
-
add: {
|
|
192
|
-
type: PatchSetOperation
|
|
193
|
-
},
|
|
194
|
-
set: {
|
|
195
|
-
type: PatchSetOperation
|
|
196
|
-
},
|
|
197
|
-
remove: {
|
|
198
|
-
type: PatchRemoveOperation
|
|
199
|
-
},
|
|
200
|
-
replace: {
|
|
201
|
-
type: PatchSetOperation
|
|
202
|
-
},
|
|
203
|
-
copy: {
|
|
204
|
-
type: PatchDestinationOperation
|
|
205
|
-
},
|
|
206
|
-
move: {
|
|
207
|
-
type: PatchDestinationOperation
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
});
|
|
211
|
-
const OrderByDirection = new GraphQLEnumType({
|
|
212
|
-
name: 'OrderByDirection',
|
|
213
|
-
values: {
|
|
214
|
-
ASC: {
|
|
215
|
-
value: 'asc'
|
|
216
|
-
},
|
|
217
|
-
DESC: {
|
|
218
|
-
value: 'desc'
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
export class SchemaBuilder {
|
|
223
|
-
// Mapping of model or references IDs to their GraphQL object names
|
|
224
|
-
_aliases;
|
|
225
|
-
_definitions;
|
|
226
|
-
_implemented = {};
|
|
227
|
-
_inputObjects = {
|
|
228
|
-
...VALUE_FILTER_INPUTS,
|
|
229
|
-
PatchOperation
|
|
230
|
-
};
|
|
231
|
-
_mutations = {};
|
|
232
|
-
_nodeModelIDs;
|
|
233
|
-
_record;
|
|
234
|
-
_references = {};
|
|
235
|
-
_relationsTo = {};
|
|
236
|
-
_subscriptions = {};
|
|
237
|
-
_types = {
|
|
238
|
-
OrderByDirection
|
|
239
|
-
};
|
|
240
|
-
constructor(params){
|
|
241
|
-
this._aliases = params.aliases ?? {};
|
|
242
|
-
this._record = params.record;
|
|
243
|
-
const referencedInterfaces = {};
|
|
244
|
-
for (const [modelIDstring, model] of Object.entries(params.record)){
|
|
245
|
-
const modelID = DocumentModelID.fromString(modelIDstring);
|
|
246
|
-
Object.assign(this._references, model.schema.$defs ?? {});
|
|
247
|
-
// Extract interfaces
|
|
248
|
-
for (const iid of model.interfaces){
|
|
249
|
-
const interfaceID = getGlobalID(iid, modelID).toString();
|
|
250
|
-
referencedInterfaces[interfaceID] ??= new Set();
|
|
251
|
-
referencedInterfaces[interfaceID].add(modelIDstring);
|
|
252
|
-
}
|
|
253
|
-
// Extract inverse relations
|
|
254
|
-
for (const [fieldName, meta] of Object.entries(model.fieldsMeta)){
|
|
255
|
-
if (meta.type === 'document' && meta.model != null) {
|
|
256
|
-
const relationRefID = getReferenceID(model.schema.properties[fieldName]?.$ref);
|
|
257
|
-
const isList = this._getReferenceSchema(relationRefID).type === 'array';
|
|
258
|
-
const relationID = getGlobalID(meta.model, modelID).toString();
|
|
259
|
-
this._relationsTo[relationID] ??= {};
|
|
260
|
-
this._relationsTo[relationID][fieldName] ??= [];
|
|
261
|
-
this._relationsTo[relationID][fieldName].push({
|
|
262
|
-
model: modelIDstring,
|
|
263
|
-
isList
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
// Map interface IDs to all the models implementing them
|
|
269
|
-
const nodeModelIDs = new Set();
|
|
270
|
-
for (const id of Object.keys(referencedInterfaces)){
|
|
271
|
-
const implementedBy = new Set();
|
|
272
|
-
collectAllModelIDs(implementedBy, referencedInterfaces, id);
|
|
273
|
-
if (implementedBy.size !== 0) {
|
|
274
|
-
this._implemented[id] = Array.from(implementedBy);
|
|
275
|
-
}
|
|
276
|
-
for (const modelID of implementedBy){
|
|
277
|
-
nodeModelIDs.add(modelID);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
this._nodeModelIDs = Array.from(nodeModelIDs);
|
|
281
|
-
}
|
|
282
|
-
getModelIDs(id) {
|
|
283
|
-
return id === null ? this._nodeModelIDs : this._implemented[id] ?? [
|
|
284
|
-
id
|
|
285
|
-
];
|
|
286
|
-
}
|
|
287
|
-
build() {
|
|
288
|
-
const definitions = this._buildSharedDefinitions();
|
|
289
|
-
this._definitions = definitions;
|
|
290
|
-
const queryFields = {
|
|
291
|
-
nodes: definitions.nodesField,
|
|
292
|
-
...definitions.queryFields
|
|
293
|
-
};
|
|
294
|
-
for (const id of Object.keys(this._record)){
|
|
295
|
-
this._buildDocument(id);
|
|
296
|
-
const name = this._aliases[id];
|
|
297
|
-
const filterArg = {
|
|
298
|
-
type: this._inputObjects[`${id}-filter`]
|
|
299
|
-
};
|
|
300
|
-
queryFields[`all${name}`] = {
|
|
301
|
-
type: this._types[`${id}-connection`],
|
|
302
|
-
args: {
|
|
303
|
-
...connectionArgs,
|
|
304
|
-
filter: filterArg,
|
|
305
|
-
orderBy: {
|
|
306
|
-
type: toList(this._inputObjects[`${id}-order`])
|
|
307
|
-
}
|
|
308
|
-
},
|
|
309
|
-
resolve: async (_, args, ctx)=>{
|
|
310
|
-
return await ctx.resolveConnection(this.getModelIDs(id), args);
|
|
311
|
-
}
|
|
312
|
-
};
|
|
313
|
-
}
|
|
314
|
-
return new GraphQLSchema({
|
|
315
|
-
query: new GraphQLObjectType({
|
|
316
|
-
name: 'Query',
|
|
317
|
-
fields: queryFields
|
|
318
|
-
}),
|
|
319
|
-
mutation: new GraphQLObjectType({
|
|
320
|
-
name: 'Mutation',
|
|
321
|
-
fields: this._mutations
|
|
322
|
-
}),
|
|
323
|
-
subscription: new GraphQLObjectType({
|
|
324
|
-
name: 'Subscription',
|
|
325
|
-
fields: ()=>{
|
|
326
|
-
const fields = {
|
|
327
|
-
documentChanged: {
|
|
328
|
-
type: new GraphQLNonNull(definitions.documentInterface),
|
|
329
|
-
args: {
|
|
330
|
-
id: {
|
|
331
|
-
type: new GraphQLNonNull(GraphQLID)
|
|
332
|
-
}
|
|
333
|
-
},
|
|
334
|
-
resolve: (doc)=>doc,
|
|
335
|
-
subscribe: (_, args, ctx)=>{
|
|
336
|
-
return ctx.subscribeToDocumentChanged(args.id);
|
|
337
|
-
}
|
|
338
|
-
},
|
|
339
|
-
documentRemoved: {
|
|
340
|
-
type: new GraphQLNonNull(GraphQLID),
|
|
341
|
-
resolve: (id)=>id,
|
|
342
|
-
subscribe: (_src, _args, ctx)=>{
|
|
343
|
-
return ctx.subscribeToDocumentRemoved();
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
};
|
|
347
|
-
for (const [id, model] of Object.entries(this._record)){
|
|
348
|
-
const name = this._aliases[id];
|
|
349
|
-
fields[`new${name}Created`] = {
|
|
350
|
-
type: new GraphQLNonNull(this._types[id]),
|
|
351
|
-
resolve: (doc)=>doc,
|
|
352
|
-
subscribe: (_src, _args, ctx)=>{
|
|
353
|
-
return ctx.subscribeToDocumentCreated(this.getModelIDs(id));
|
|
354
|
-
}
|
|
355
|
-
};
|
|
356
|
-
for (const iid of model.interfaces ?? []){
|
|
357
|
-
const interfaceID = getGlobalID(iid, id).toString();
|
|
358
|
-
for (const [fieldName, relations] of Object.entries(this._relationsTo[interfaceID] ?? {})){
|
|
359
|
-
for (const relation of relations){
|
|
360
|
-
this._buildRelationSubscriptions(fields, interfaceID, fieldName, relation);
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
for (const [fieldName, relations] of Object.entries(this._relationsTo[id] ?? {})){
|
|
365
|
-
for (const relation of relations){
|
|
366
|
-
this._buildRelationSubscriptions(fields, id, fieldName, relation);
|
|
367
|
-
}
|
|
368
|
-
}
|
|
369
|
-
}
|
|
370
|
-
return fields;
|
|
371
|
-
}
|
|
372
|
-
})
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
_getDefinitions() {
|
|
376
|
-
if (this._definitions == null) {
|
|
377
|
-
throw new Error('Definitions must be initialized');
|
|
378
|
-
}
|
|
379
|
-
return this._definitions;
|
|
380
|
-
}
|
|
381
|
-
_getReference(path, parentName) {
|
|
382
|
-
const id = getReferenceID(path);
|
|
383
|
-
const type = this._types[id];
|
|
384
|
-
if (type != null) {
|
|
385
|
-
return type;
|
|
386
|
-
}
|
|
387
|
-
const schema = this._getReferenceSchema(id);
|
|
388
|
-
return this._buildReference(id, schema, parentName);
|
|
389
|
-
}
|
|
390
|
-
_getReferenceSchema(id) {
|
|
391
|
-
const schema = this._references[id];
|
|
392
|
-
if (schema == null) {
|
|
393
|
-
throw new Error(`Could not find reference: ${id}`);
|
|
394
|
-
}
|
|
395
|
-
return schema;
|
|
396
|
-
}
|
|
397
|
-
_getReferenceInput(path, isPartial, parentName = '') {
|
|
398
|
-
const id = getReferenceID(path);
|
|
399
|
-
const schema = this._getReferenceSchema(id);
|
|
400
|
-
if (schema.type === 'array') {
|
|
401
|
-
const itemType = this._getReferenceInput(schema.items.$ref, false, schema.title);
|
|
402
|
-
return new GraphQLList(new GraphQLNonNull(itemType));
|
|
403
|
-
}
|
|
404
|
-
if (schema.type === 'object') {
|
|
405
|
-
if (schema.additionalProperties) {
|
|
406
|
-
return GraphQLJSONObject;
|
|
407
|
-
}
|
|
408
|
-
const inputID = isPartial ? `${id}-partial` : id;
|
|
409
|
-
const existing = this._inputObjects[inputID];
|
|
410
|
-
if (existing != null) {
|
|
411
|
-
return existing;
|
|
412
|
-
}
|
|
413
|
-
const fields = {};
|
|
414
|
-
for (const [key, value] of Object.entries(schema.properties)){
|
|
415
|
-
const type = this._getReferenceInput(value.$ref, isPartial, schema.title);
|
|
416
|
-
fields[key] = {
|
|
417
|
-
type: isPartial || !schema.required.includes(key) ? type : new GraphQLNonNull(type)
|
|
418
|
-
};
|
|
419
|
-
}
|
|
420
|
-
const name = this._aliases[id] ?? pascalCase(parentName + schema.title);
|
|
421
|
-
const input = new GraphQLInputObjectType({
|
|
422
|
-
name: isPartial ? `Partial${name}Input` : `${name}Input`,
|
|
423
|
-
fields
|
|
424
|
-
});
|
|
425
|
-
this._inputObjects[inputID] = input;
|
|
426
|
-
return input;
|
|
427
|
-
}
|
|
428
|
-
return this._buildScalar(id, schema, parentName);
|
|
429
|
-
}
|
|
430
|
-
_buildSharedDefinitions() {
|
|
431
|
-
const nodeDefs = nodeDefinitions(async (id, ctx)=>{
|
|
432
|
-
if (id.startsWith('did:')) {
|
|
433
|
-
return id;
|
|
434
|
-
}
|
|
435
|
-
const decoded = decodeID(id);
|
|
436
|
-
if (decoded instanceof AttachmentID) {
|
|
437
|
-
return decoded;
|
|
438
|
-
}
|
|
439
|
-
if (decoded instanceof DocumentID) {
|
|
440
|
-
return await ctx.loadDocument(id);
|
|
441
|
-
}
|
|
442
|
-
throw new Error(`Unsupported node ID: ${id}`);
|
|
443
|
-
}, (node)=>{
|
|
444
|
-
return typeof node === 'string' ? 'AccountNode' : node instanceof AttachmentID ? 'AttachmentNode' : this._aliases[node.model];
|
|
445
|
-
});
|
|
446
|
-
const accountObject = new GraphQLObjectType({
|
|
447
|
-
name: 'AccountNode',
|
|
448
|
-
interfaces: [
|
|
449
|
-
nodeDefs.nodeInterface
|
|
450
|
-
],
|
|
451
|
-
fields: ()=>{
|
|
452
|
-
const config = {
|
|
453
|
-
id: {
|
|
454
|
-
type: new GraphQLNonNull(GraphQLID),
|
|
455
|
-
description: 'Globally unique identifier of the account (DID string)',
|
|
456
|
-
resolve: (did)=>did
|
|
457
|
-
},
|
|
458
|
-
isViewer: {
|
|
459
|
-
type: new GraphQLNonNull(GraphQLBoolean),
|
|
460
|
-
description: 'Whether the authenticated request issuer is this account or not',
|
|
461
|
-
resolve: (did, _, ctx)=>ctx.getViewer() === did
|
|
462
|
-
}
|
|
463
|
-
};
|
|
464
|
-
for (const [id, model] of Object.entries(this._record)){
|
|
465
|
-
switch(model.behavior){
|
|
466
|
-
case 'interface':
|
|
467
|
-
continue;
|
|
468
|
-
case 'default':
|
|
469
|
-
{
|
|
470
|
-
const name = this._aliases[id];
|
|
471
|
-
config[`own${name}Connection`] = {
|
|
472
|
-
type: this._types[`${id}-connection`],
|
|
473
|
-
args: {
|
|
474
|
-
...connectionArgs,
|
|
475
|
-
filter: {
|
|
476
|
-
type: this._inputObjects[`${id}-filter`]
|
|
477
|
-
},
|
|
478
|
-
orderBy: {
|
|
479
|
-
type: toList(this._inputObjects[`${id}-order`])
|
|
480
|
-
}
|
|
481
|
-
},
|
|
482
|
-
resolve: async (owner, args, ctx)=>{
|
|
483
|
-
return await ctx.resolveConnection(this.getModelIDs(id), {
|
|
484
|
-
...args,
|
|
485
|
-
owner
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
};
|
|
489
|
-
break;
|
|
490
|
-
}
|
|
491
|
-
case 'unique':
|
|
492
|
-
{
|
|
493
|
-
const name = this._aliases[id];
|
|
494
|
-
config[`own${name}`] = {
|
|
495
|
-
type: this._types[id],
|
|
496
|
-
args: model.uniqueFields.length ? {
|
|
497
|
-
with: {
|
|
498
|
-
type: new GraphQLNonNull(this._buildSetInputObjectType(id, model.uniqueFields))
|
|
499
|
-
}
|
|
500
|
-
} : {},
|
|
501
|
-
resolve: async (owner, args, ctx)=>{
|
|
502
|
-
const unique = getUniqueDocValue(model.uniqueFields, args.with);
|
|
503
|
-
const docID = DocumentID.create(id, owner, unique);
|
|
504
|
-
return await ctx.loadDocument(docID.toString());
|
|
505
|
-
}
|
|
506
|
-
};
|
|
507
|
-
}
|
|
508
|
-
}
|
|
509
|
-
}
|
|
510
|
-
return config;
|
|
511
|
-
}
|
|
512
|
-
});
|
|
513
|
-
const attachmentObject = new GraphQLObjectType({
|
|
514
|
-
name: 'AttachmentNode',
|
|
515
|
-
interfaces: [
|
|
516
|
-
nodeDefs.nodeInterface
|
|
517
|
-
],
|
|
518
|
-
fields: {
|
|
519
|
-
id: {
|
|
520
|
-
type: new GraphQLNonNull(GraphQLID),
|
|
521
|
-
resolve: (aid)=>aid.toString()
|
|
522
|
-
},
|
|
523
|
-
contentLength: {
|
|
524
|
-
type: new GraphQLNonNull(GraphQLInt)
|
|
525
|
-
},
|
|
526
|
-
mimeType: {
|
|
527
|
-
type: new GraphQLNonNull(GraphQLString)
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
});
|
|
531
|
-
const documentNodeFields = {
|
|
532
|
-
// Node interface
|
|
533
|
-
id: {
|
|
534
|
-
type: new GraphQLNonNull(GraphQLID)
|
|
535
|
-
},
|
|
536
|
-
// Metadata
|
|
537
|
-
model: {
|
|
538
|
-
type: new GraphQLNonNull(GraphQLString)
|
|
539
|
-
},
|
|
540
|
-
owner: {
|
|
541
|
-
type: new GraphQLNonNull(accountObject)
|
|
542
|
-
},
|
|
543
|
-
createdAt: {
|
|
544
|
-
type: new GraphQLNonNull(GraphQLDateTimeISO)
|
|
545
|
-
},
|
|
546
|
-
updatedAt: {
|
|
547
|
-
type: GraphQLDateTimeISO
|
|
548
|
-
}
|
|
549
|
-
};
|
|
550
|
-
const documentInterface = new GraphQLInterfaceType({
|
|
551
|
-
name: 'DocumentNode',
|
|
552
|
-
interfaces: [
|
|
553
|
-
nodeDefs.nodeInterface
|
|
554
|
-
],
|
|
555
|
-
fields: documentNodeFields
|
|
556
|
-
});
|
|
557
|
-
const queryFields = {
|
|
558
|
-
node: nodeDefs.nodeField,
|
|
559
|
-
viewer: {
|
|
560
|
-
type: accountObject,
|
|
561
|
-
description: 'Account issuing the request, if authenticated',
|
|
562
|
-
resolve: (_self, _args, ctx)=>ctx.getViewer()
|
|
563
|
-
}
|
|
564
|
-
};
|
|
565
|
-
return {
|
|
566
|
-
...nodeDefs,
|
|
567
|
-
accountObject,
|
|
568
|
-
attachmentObject,
|
|
569
|
-
documentInterface,
|
|
570
|
-
documentNodeFields,
|
|
571
|
-
queryFields
|
|
572
|
-
};
|
|
573
|
-
}
|
|
574
|
-
_buildDocument(id) {
|
|
575
|
-
if (this._types[id] != null) {
|
|
576
|
-
return this._types[id];
|
|
577
|
-
}
|
|
578
|
-
const model = this._record[id];
|
|
579
|
-
if (model == null) {
|
|
580
|
-
throw new Error(`Could not find model id: ${id}`);
|
|
581
|
-
}
|
|
582
|
-
const definitions = this._getDefinitions();
|
|
583
|
-
this._aliases[id] ??= pascalCase(model.name);
|
|
584
|
-
const name = this._aliases[id];
|
|
585
|
-
const config = {
|
|
586
|
-
name,
|
|
587
|
-
interfaces: ()=>{
|
|
588
|
-
return [
|
|
589
|
-
definitions.documentInterface,
|
|
590
|
-
definitions.nodeInterface
|
|
591
|
-
].concat(model.interfaces.map((interfaceID)=>{
|
|
592
|
-
return this._types[getGlobalID(interfaceID, id).toString()];
|
|
593
|
-
}));
|
|
594
|
-
},
|
|
595
|
-
fields: ()=>this._buildDocumentFields(id, model, name)
|
|
596
|
-
};
|
|
597
|
-
const isInterface = model.behavior === 'interface';
|
|
598
|
-
const nodeType = isInterface ? new GraphQLInterfaceType({
|
|
599
|
-
...config,
|
|
600
|
-
resolveType: (doc)=>this._aliases[doc.model]
|
|
601
|
-
}) : new GraphQLObjectType(config);
|
|
602
|
-
const { connectionType, edgeType } = connectionDefinitions({
|
|
603
|
-
nodeType
|
|
604
|
-
});
|
|
605
|
-
this._types[id] = nodeType;
|
|
606
|
-
this._types[`${id}-connection`] = connectionType;
|
|
607
|
-
this._types[`${id}-edge`] = edgeType;
|
|
608
|
-
this._buildDocumentDataObject(id, model, name);
|
|
609
|
-
this._buildDocumentInput(id, model);
|
|
610
|
-
this._buildObjectFilterInput(id, model.schema, name, true);
|
|
611
|
-
this._buildObjectOrderByInput(id, model.schema, name, true);
|
|
612
|
-
// TODO: move to dedicated method
|
|
613
|
-
if (!isInterface) {
|
|
614
|
-
if (model.behavior === 'default') {
|
|
615
|
-
this._mutations[`create${name}`] = mutationWithClientMutationId({
|
|
616
|
-
name: `Create${name}`,
|
|
617
|
-
inputFields: ()=>({
|
|
618
|
-
data: {
|
|
619
|
-
type: new GraphQLNonNull(this._inputObjects[id])
|
|
620
|
-
}
|
|
621
|
-
}),
|
|
622
|
-
outputFields: ()=>({
|
|
623
|
-
...definitions.queryFields,
|
|
624
|
-
document: {
|
|
625
|
-
type: this._types[id]
|
|
626
|
-
}
|
|
627
|
-
}),
|
|
628
|
-
mutateAndGetPayload: async (input, ctx, info)=>{
|
|
629
|
-
const document = await ctx.executeCreateMutation(id, input.data, info);
|
|
630
|
-
return {
|
|
631
|
-
document
|
|
632
|
-
};
|
|
633
|
-
}
|
|
634
|
-
});
|
|
635
|
-
} else if (model.behavior === 'unique') {
|
|
636
|
-
this._mutations[`set${name}`] = mutationWithClientMutationId({
|
|
637
|
-
name: `Set${name}`,
|
|
638
|
-
inputFields: ()=>({
|
|
639
|
-
data: {
|
|
640
|
-
type: new GraphQLNonNull(this._inputObjects[id])
|
|
641
|
-
}
|
|
642
|
-
}),
|
|
643
|
-
outputFields: ()=>({
|
|
644
|
-
...definitions.queryFields,
|
|
645
|
-
document: {
|
|
646
|
-
type: this._types[id]
|
|
647
|
-
}
|
|
648
|
-
}),
|
|
649
|
-
mutateAndGetPayload: async (input, ctx, info)=>{
|
|
650
|
-
const unique = getUniqueDocValue(model.uniqueFields, input.data);
|
|
651
|
-
const document = await ctx.executeSetMutation(id, unique, input.data, info);
|
|
652
|
-
return {
|
|
653
|
-
document
|
|
654
|
-
};
|
|
655
|
-
}
|
|
656
|
-
});
|
|
657
|
-
}
|
|
658
|
-
this._mutations[`update${name}`] = mutationWithClientMutationId({
|
|
659
|
-
name: `Update${name}`,
|
|
660
|
-
inputFields: ()=>({
|
|
661
|
-
id: {
|
|
662
|
-
type: new GraphQLNonNull(GraphQLID)
|
|
663
|
-
},
|
|
664
|
-
patch: {
|
|
665
|
-
type: new GraphQLNonNull(toList(PatchOperation))
|
|
666
|
-
},
|
|
667
|
-
from: {
|
|
668
|
-
type: this._inputObjects[`${id}-update`]
|
|
669
|
-
}
|
|
670
|
-
}),
|
|
671
|
-
outputFields: ()=>({
|
|
672
|
-
...definitions.queryFields,
|
|
673
|
-
document: {
|
|
674
|
-
type: this._types[id]
|
|
675
|
-
}
|
|
676
|
-
}),
|
|
677
|
-
mutateAndGetPayload: async (input, ctx, info)=>{
|
|
678
|
-
const document = await ctx.executeUpdateMutation(input, info);
|
|
679
|
-
return {
|
|
680
|
-
document
|
|
681
|
-
};
|
|
682
|
-
}
|
|
683
|
-
});
|
|
684
|
-
this._mutations[`remove${name}`] = mutationWithClientMutationId({
|
|
685
|
-
name: `Remove${name}`,
|
|
686
|
-
inputFields: ()=>({
|
|
687
|
-
id: {
|
|
688
|
-
type: new GraphQLNonNull(GraphQLID)
|
|
689
|
-
}
|
|
690
|
-
}),
|
|
691
|
-
outputFields: ()=>definitions.queryFields,
|
|
692
|
-
mutateAndGetPayload: async (input, ctx, info)=>{
|
|
693
|
-
await ctx.executeRemoveMutation(input.id, info);
|
|
694
|
-
return {};
|
|
695
|
-
}
|
|
696
|
-
});
|
|
697
|
-
}
|
|
698
|
-
return nodeType;
|
|
699
|
-
}
|
|
700
|
-
_buildDocumentFields(id, model, name) {
|
|
701
|
-
const dataObject = this._buildDocumentDataObject(id, model, name);
|
|
702
|
-
const definitions = this._getDefinitions();
|
|
703
|
-
const fields = {
|
|
704
|
-
...definitions.documentNodeFields,
|
|
705
|
-
data: {
|
|
706
|
-
type: new GraphQLNonNull(dataObject)
|
|
707
|
-
}
|
|
708
|
-
};
|
|
709
|
-
for (const [key, field] of Object.entries(dataObject.getFields())){
|
|
710
|
-
const meta = model.fieldsMeta[key];
|
|
711
|
-
if (meta == null) {
|
|
712
|
-
continue;
|
|
713
|
-
}
|
|
714
|
-
switch(meta.type){
|
|
715
|
-
case 'account':
|
|
716
|
-
{
|
|
717
|
-
if (isList(field.type)) {
|
|
718
|
-
fields[`${key}Accounts`] = {
|
|
719
|
-
type: new GraphQLNonNull(toOuputList(definitions.accountObject)),
|
|
720
|
-
resolve: (doc)=>doc.data?.[key] ?? []
|
|
721
|
-
};
|
|
722
|
-
} else {
|
|
723
|
-
fields[`${key}Account`] = {
|
|
724
|
-
type: definitions.accountObject,
|
|
725
|
-
resolve: (doc)=>doc.data?.[key] ?? null
|
|
726
|
-
};
|
|
727
|
-
}
|
|
728
|
-
break;
|
|
729
|
-
}
|
|
730
|
-
case 'attachment':
|
|
731
|
-
{
|
|
732
|
-
if (isList(field.type)) {
|
|
733
|
-
fields[`${key}Attachments`] = {
|
|
734
|
-
type: new GraphQLNonNull(toOuputList(definitions.attachmentObject)),
|
|
735
|
-
resolve: (doc)=>{
|
|
736
|
-
const ids = doc.data?.[key] ?? [];
|
|
737
|
-
return ids.map(AttachmentID.fromString);
|
|
738
|
-
}
|
|
739
|
-
};
|
|
740
|
-
} else {
|
|
741
|
-
fields[`${key}Attachment`] = {
|
|
742
|
-
type: definitions.attachmentObject,
|
|
743
|
-
resolve: (doc)=>{
|
|
744
|
-
const id = doc.data?.[key];
|
|
745
|
-
return id ? AttachmentID.fromString(id) : null;
|
|
746
|
-
}
|
|
747
|
-
};
|
|
748
|
-
}
|
|
749
|
-
break;
|
|
750
|
-
}
|
|
751
|
-
case 'document':
|
|
752
|
-
{
|
|
753
|
-
const relationModel = meta.model;
|
|
754
|
-
if (typeof relationModel === 'undefined') {
|
|
755
|
-
continue;
|
|
756
|
-
}
|
|
757
|
-
let relationModelID;
|
|
758
|
-
let relationType;
|
|
759
|
-
if (relationModel === null) {
|
|
760
|
-
relationModelID = null;
|
|
761
|
-
relationType = definitions.documentInterface;
|
|
762
|
-
} else {
|
|
763
|
-
relationModelID = getGlobalID(relationModel, id).toString();
|
|
764
|
-
relationType = this._buildDocument(relationModelID);
|
|
765
|
-
}
|
|
766
|
-
if (isList(field.type)) {
|
|
767
|
-
// TODO: add suport for filters?
|
|
768
|
-
fields[`${key}Documents`] = {
|
|
769
|
-
type: new GraphQLNonNull(toOuputList(relationType)),
|
|
770
|
-
resolve: async (doc, _args, ctx)=>{
|
|
771
|
-
const relationIDs = doc.data?.[key] ?? [];
|
|
772
|
-
return relationIDs.length ? await ctx.resolveList(this.getModelIDs(relationModelID), relationIDs) : [];
|
|
773
|
-
}
|
|
774
|
-
};
|
|
775
|
-
} else {
|
|
776
|
-
fields[`${key}Document`] = {
|
|
777
|
-
type: relationType,
|
|
778
|
-
resolve: async (doc, _args, ctx)=>{
|
|
779
|
-
const relationID = doc.data?.[key];
|
|
780
|
-
return relationID ? await ctx.loadDocument(relationID) : null;
|
|
781
|
-
}
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
|
-
break;
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
for (const iid of model.interfaces ?? []){
|
|
789
|
-
const interfaceID = getGlobalID(iid, id).toString();
|
|
790
|
-
for (const [fieldName, relations] of Object.entries(this._relationsTo[interfaceID] ?? {})){
|
|
791
|
-
for (const relation of relations){
|
|
792
|
-
this._buildRelationFields(fields, fieldName, relation);
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
for (const [fieldName, relations] of Object.entries(this._relationsTo[id] ?? {})){
|
|
797
|
-
for (const relation of relations){
|
|
798
|
-
this._buildRelationFields(fields, fieldName, relation);
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
return fields;
|
|
802
|
-
}
|
|
803
|
-
_buildDocumentDataObject(id, model, name) {
|
|
804
|
-
const typeID = `${id}-data`;
|
|
805
|
-
if (this._types[typeID] != null) {
|
|
806
|
-
return this._types[typeID];
|
|
807
|
-
}
|
|
808
|
-
const required = model.schema.required ?? [];
|
|
809
|
-
const fields = {};
|
|
810
|
-
for (const [key, value] of Object.entries(model.schema.properties ?? {})){
|
|
811
|
-
const fieldRef = value.$ref;
|
|
812
|
-
if (fieldRef == null) {
|
|
813
|
-
throw new Error(`Missing ref for field ${key} of object ${id}`);
|
|
814
|
-
}
|
|
815
|
-
const type = this._getReference(fieldRef);
|
|
816
|
-
fields[key] = {
|
|
817
|
-
type: required.includes(key) ? new GraphQLNonNull(type) : type
|
|
818
|
-
};
|
|
819
|
-
}
|
|
820
|
-
const config = {
|
|
821
|
-
name: `${name}Data`,
|
|
822
|
-
interfaces: ()=>{
|
|
823
|
-
return model.interfaces.map((interfaceID)=>{
|
|
824
|
-
const typeID = `${getGlobalID(interfaceID, id).toString()}-data`;
|
|
825
|
-
return this._types[typeID];
|
|
826
|
-
});
|
|
827
|
-
},
|
|
828
|
-
fields
|
|
829
|
-
};
|
|
830
|
-
const object = model.behavior === 'interface' ? new GraphQLInterfaceType(config) : new GraphQLObjectType(config);
|
|
831
|
-
this._types[typeID] = object;
|
|
832
|
-
return object;
|
|
833
|
-
}
|
|
834
|
-
_buildDocumentInput(id, model) {
|
|
835
|
-
if (model.behavior === 'interface') {
|
|
836
|
-
// No mutations for interfaces
|
|
837
|
-
return;
|
|
838
|
-
}
|
|
839
|
-
const setFields = {};
|
|
840
|
-
const updateFields = {};
|
|
841
|
-
for (const [key, value] of Object.entries(model.schema.properties)){
|
|
842
|
-
const path = value.$ref;
|
|
843
|
-
const setType = this._getReferenceInput(path, false);
|
|
844
|
-
setFields[key] = {
|
|
845
|
-
type: model.schema.required.includes(key) ? new GraphQLNonNull(setType) : setType
|
|
846
|
-
};
|
|
847
|
-
const id = getReferenceID(path);
|
|
848
|
-
const schema = this._getReferenceSchema(id);
|
|
849
|
-
if (schema.const == null) {
|
|
850
|
-
updateFields[key] = {
|
|
851
|
-
type: this._getReferenceInput(path, true)
|
|
852
|
-
};
|
|
853
|
-
}
|
|
854
|
-
}
|
|
855
|
-
const name = this._aliases[id];
|
|
856
|
-
this._inputObjects[id] = new GraphQLInputObjectType({
|
|
857
|
-
name: `${name}Input`,
|
|
858
|
-
fields: setFields
|
|
859
|
-
});
|
|
860
|
-
this._inputObjects[`${id}-update`] = new GraphQLInputObjectType({
|
|
861
|
-
name: `Partial${name}Input`,
|
|
862
|
-
fields: updateFields
|
|
863
|
-
});
|
|
864
|
-
}
|
|
865
|
-
_buildReferenceFilterInput(path, parentName = '') {
|
|
866
|
-
const id = getReferenceID(path);
|
|
867
|
-
const schema = this._getReferenceSchema(id);
|
|
868
|
-
let refParentName = parentName;
|
|
869
|
-
if (schema.type === 'object') {
|
|
870
|
-
return schema.additionalProperties ? null : this._buildObjectFilterInput(id, schema, parentName);
|
|
871
|
-
}
|
|
872
|
-
if (schema.type === 'array') {
|
|
873
|
-
const name = this._aliases[id] ?? pascalCase(parentName + schema.title);
|
|
874
|
-
refParentName = name;
|
|
875
|
-
// const itemType = this._buildReferenceFilterInput(schema.items.$ref, schema.title)
|
|
876
|
-
return new GraphQLInputObjectType({
|
|
877
|
-
name: `${name}Filter`,
|
|
878
|
-
isOneOf: true,
|
|
879
|
-
fields: {
|
|
880
|
-
// DB does not support filtering on array values
|
|
881
|
-
// contains: { type: toList(itemType) },
|
|
882
|
-
// isEmpty: { type: GraphQLBoolean },
|
|
883
|
-
isNull: {
|
|
884
|
-
type: GraphQLBoolean
|
|
885
|
-
}
|
|
886
|
-
}
|
|
887
|
-
});
|
|
888
|
-
}
|
|
889
|
-
const scalar = this._buildScalar(id, schema, refParentName);
|
|
890
|
-
const knownFilter = VALUE_FILTER_INPUT_TYPES[scalar.name];
|
|
891
|
-
if (knownFilter != null) {
|
|
892
|
-
return this._inputObjects[knownFilter];
|
|
893
|
-
}
|
|
894
|
-
const filter = isEnumType(scalar) ? createEnumValueFilterInput(scalar) : createScalarValueFilterInput(scalar);
|
|
895
|
-
if (this._inputObjects[filter.name] == null) {
|
|
896
|
-
this._inputObjects[filter.name] = filter;
|
|
897
|
-
}
|
|
898
|
-
return this._inputObjects[filter.name];
|
|
899
|
-
}
|
|
900
|
-
_buildReferenceOrderByInput(path, parentName = '') {
|
|
901
|
-
const id = getReferenceID(path);
|
|
902
|
-
const schema = this._getReferenceSchema(id);
|
|
903
|
-
switch(schema.type){
|
|
904
|
-
case 'array':
|
|
905
|
-
return null;
|
|
906
|
-
case 'object':
|
|
907
|
-
return schema.additionalProperties ? null : this._buildObjectOrderByInput(id, schema, parentName);
|
|
908
|
-
default:
|
|
909
|
-
return OrderByDirection;
|
|
910
|
-
}
|
|
911
|
-
}
|
|
912
|
-
_buildObjectFilterInput(id, schema, parentName = '', isDocument = false) {
|
|
913
|
-
const inputID = `${id}-filter`;
|
|
914
|
-
const existing = this._inputObjects[inputID];
|
|
915
|
-
if (existing != null) {
|
|
916
|
-
return existing;
|
|
917
|
-
}
|
|
918
|
-
const name = this._aliases[id] ?? pascalCase(parentName + (schema.title ?? ''));
|
|
919
|
-
const fields = isDocument ? {
|
|
920
|
-
_owner: {
|
|
921
|
-
type: this._inputObjects.AccountValueFilter
|
|
922
|
-
}
|
|
923
|
-
} : {};
|
|
924
|
-
for (const [key, value] of Object.entries(schema.properties)){
|
|
925
|
-
const type = this._buildReferenceFilterInput(value.$ref, name);
|
|
926
|
-
if (type !== null) {
|
|
927
|
-
fields[key] = {
|
|
928
|
-
type
|
|
929
|
-
};
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
const objectInput = new GraphQLInputObjectType({
|
|
933
|
-
name: `${name}ObjectFilter`,
|
|
934
|
-
fields
|
|
935
|
-
});
|
|
936
|
-
const input = new GraphQLInputObjectType({
|
|
937
|
-
name: `${name}Filter`,
|
|
938
|
-
isOneOf: true,
|
|
939
|
-
fields: ()=>({
|
|
940
|
-
where: {
|
|
941
|
-
type: objectInput
|
|
942
|
-
},
|
|
943
|
-
and: {
|
|
944
|
-
type: toList(this._inputObjects[inputID])
|
|
945
|
-
},
|
|
946
|
-
or: {
|
|
947
|
-
type: toList(this._inputObjects[inputID])
|
|
948
|
-
},
|
|
949
|
-
not: {
|
|
950
|
-
type: this._inputObjects[inputID]
|
|
951
|
-
}
|
|
952
|
-
})
|
|
953
|
-
});
|
|
954
|
-
this._inputObjects[inputID] = input;
|
|
955
|
-
return input;
|
|
956
|
-
}
|
|
957
|
-
_buildObjectOrderByInput(id, schema, parentName = '', isDocument = false) {
|
|
958
|
-
const inputID = `${id}-order`;
|
|
959
|
-
const existing = this._inputObjects[inputID];
|
|
960
|
-
if (existing != null) {
|
|
961
|
-
return existing;
|
|
962
|
-
}
|
|
963
|
-
const name = this._aliases[id] ?? pascalCase(parentName + (schema.title ?? ''));
|
|
964
|
-
const fields = isDocument ? {
|
|
965
|
-
_docOwner: {
|
|
966
|
-
type: OrderByDirection
|
|
967
|
-
},
|
|
968
|
-
_createdAt: {
|
|
969
|
-
type: OrderByDirection
|
|
970
|
-
}
|
|
971
|
-
} : {};
|
|
972
|
-
for (const [key, value] of Object.entries(schema.properties)){
|
|
973
|
-
const type = this._buildReferenceOrderByInput(value.$ref, name);
|
|
974
|
-
if (type != null) {
|
|
975
|
-
fields[key] = {
|
|
976
|
-
type
|
|
977
|
-
};
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
const input = new GraphQLInputObjectType({
|
|
981
|
-
name: `${name}OrderBy`,
|
|
982
|
-
isOneOf: true,
|
|
983
|
-
fields
|
|
984
|
-
});
|
|
985
|
-
this._inputObjects[inputID] = input;
|
|
986
|
-
return input;
|
|
987
|
-
}
|
|
988
|
-
_buildSetInputObjectType(id, withFields, relationField) {
|
|
989
|
-
const relationFieldName = relationField ? pascalCase(relationField) : '';
|
|
990
|
-
const inputID = `${id}-with-${relationFieldName}`;
|
|
991
|
-
const existing = this._inputObjects[inputID];
|
|
992
|
-
if (existing != null) {
|
|
993
|
-
return existing;
|
|
994
|
-
}
|
|
995
|
-
const model = this._record[id];
|
|
996
|
-
const name = this._aliases[id];
|
|
997
|
-
this._inputObjects[inputID] = new GraphQLInputObjectType({
|
|
998
|
-
name: `With${relationFieldName}${name}Input`,
|
|
999
|
-
fields: ()=>{
|
|
1000
|
-
const fields = {};
|
|
1001
|
-
for (const fieldName of withFields){
|
|
1002
|
-
const field = model.schema.properties[fieldName];
|
|
1003
|
-
if (field == null) {
|
|
1004
|
-
throw new Error(`Field ${fieldName} not found on model ${name}`);
|
|
1005
|
-
}
|
|
1006
|
-
const type = this._getReferenceInput(field.$ref, false);
|
|
1007
|
-
fields[fieldName] = {
|
|
1008
|
-
type: new GraphQLNonNull(type)
|
|
1009
|
-
};
|
|
1010
|
-
}
|
|
1011
|
-
return fields;
|
|
1012
|
-
}
|
|
1013
|
-
});
|
|
1014
|
-
return this._inputObjects[inputID];
|
|
1015
|
-
}
|
|
1016
|
-
_buildReference(id, schema, parentName = '') {
|
|
1017
|
-
if (this._types[id] != null) {
|
|
1018
|
-
return this._types[id];
|
|
1019
|
-
}
|
|
1020
|
-
switch(schema.type){
|
|
1021
|
-
case 'array':
|
|
1022
|
-
this._types[id] = this._buildArray(schema, parentName);
|
|
1023
|
-
return this._types[id];
|
|
1024
|
-
case 'object':
|
|
1025
|
-
this._types[id] = schema.additionalProperties ? GraphQLJSONObject : this._buildObject(id, schema, parentName);
|
|
1026
|
-
return this._types[id];
|
|
1027
|
-
default:
|
|
1028
|
-
return this._buildScalar(id, schema, parentName);
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
_buildScalar(id, schema, parentName = '') {
|
|
1032
|
-
if (this._types[id] != null) {
|
|
1033
|
-
return this._types[id];
|
|
1034
|
-
}
|
|
1035
|
-
switch(schema.type){
|
|
1036
|
-
case 'boolean':
|
|
1037
|
-
this._types[id] = GraphQLBoolean;
|
|
1038
|
-
break;
|
|
1039
|
-
case 'integer':
|
|
1040
|
-
this._types[id] = GraphQLInt;
|
|
1041
|
-
break;
|
|
1042
|
-
case 'number':
|
|
1043
|
-
this._types[id] = GraphQLFloat;
|
|
1044
|
-
break;
|
|
1045
|
-
case 'string':
|
|
1046
|
-
this._types[id] = this._buildString(schema, parentName);
|
|
1047
|
-
break;
|
|
1048
|
-
}
|
|
1049
|
-
return this._types[id];
|
|
1050
|
-
}
|
|
1051
|
-
_buildArray(schema, parentName = '') {
|
|
1052
|
-
const itemRef = schema.items.$ref;
|
|
1053
|
-
return new GraphQLList(new GraphQLNonNull(this._getReference(itemRef, pascalCase(parentName + schema.title))));
|
|
1054
|
-
}
|
|
1055
|
-
_buildObject(id, schema, parentName = '') {
|
|
1056
|
-
const name = pascalCase(parentName + schema.title);
|
|
1057
|
-
const fields = this._buildObjectFields(id, schema, name);
|
|
1058
|
-
return new GraphQLObjectType({
|
|
1059
|
-
name,
|
|
1060
|
-
fields
|
|
1061
|
-
});
|
|
1062
|
-
}
|
|
1063
|
-
_buildObjectFields(id, schema, parentName = '') {
|
|
1064
|
-
const required = schema.required ?? [];
|
|
1065
|
-
const fields = {};
|
|
1066
|
-
for (const [key, value] of Object.entries(schema.properties ?? {})){
|
|
1067
|
-
const fieldRef = value.$ref;
|
|
1068
|
-
if (fieldRef == null) {
|
|
1069
|
-
throw new Error(`Missing ref for field ${key} of object ${id}`);
|
|
1070
|
-
}
|
|
1071
|
-
const type = this._getReference(fieldRef, parentName);
|
|
1072
|
-
fields[key] = {
|
|
1073
|
-
type: required.includes(key) ? new GraphQLNonNull(type) : type
|
|
1074
|
-
};
|
|
1075
|
-
}
|
|
1076
|
-
return fields;
|
|
1077
|
-
}
|
|
1078
|
-
_buildRelationFields(fields, fieldName, relation) {
|
|
1079
|
-
if (relation.isList) {
|
|
1080
|
-
// DB does not support matches for array containing values
|
|
1081
|
-
return;
|
|
1082
|
-
}
|
|
1083
|
-
const relationID = relation.model;
|
|
1084
|
-
const relationName = `${pascalCase(fieldName)}Of${this._aliases[relationID]}`;
|
|
1085
|
-
fields[`in${relationName}`] = {
|
|
1086
|
-
type: new GraphQLNonNull(this._types[`${relationID}-connection`]),
|
|
1087
|
-
args: {
|
|
1088
|
-
...connectionArgs,
|
|
1089
|
-
filter: {
|
|
1090
|
-
type: this._inputObjects[`${relationID}-filter`]
|
|
1091
|
-
},
|
|
1092
|
-
orderBy: {
|
|
1093
|
-
type: toList(this._inputObjects[`${relationID}-order`])
|
|
1094
|
-
}
|
|
1095
|
-
},
|
|
1096
|
-
resolve: (doc, args, ctx)=>{
|
|
1097
|
-
const docFilter = {
|
|
1098
|
-
where: {
|
|
1099
|
-
[fieldName]: {
|
|
1100
|
-
equalTo: doc.id
|
|
1101
|
-
}
|
|
1102
|
-
}
|
|
1103
|
-
};
|
|
1104
|
-
const filter = args.filter ? {
|
|
1105
|
-
and: [
|
|
1106
|
-
args.filter,
|
|
1107
|
-
docFilter
|
|
1108
|
-
]
|
|
1109
|
-
} : docFilter;
|
|
1110
|
-
return ctx.resolveConnection(this.getModelIDs(relationID), {
|
|
1111
|
-
...args,
|
|
1112
|
-
filter
|
|
1113
|
-
});
|
|
1114
|
-
}
|
|
1115
|
-
};
|
|
1116
|
-
// TODO: add support for count relation
|
|
1117
|
-
// fields[`_${fieldName}_of_${relationName}_count`] = {
|
|
1118
|
-
// type: new GraphQLNonNull(GraphQLInt),
|
|
1119
|
-
// }
|
|
1120
|
-
}
|
|
1121
|
-
_buildRelationSubscriptions(fields, modelID, fieldName, relation) {
|
|
1122
|
-
if (relation.isList) {
|
|
1123
|
-
// Inverse relations to list fields are not supported in connections so we skip them for subscriptions as well
|
|
1124
|
-
return;
|
|
1125
|
-
}
|
|
1126
|
-
const targetFieldName = pascalCase(fieldName);
|
|
1127
|
-
fields[`edge${this._aliases[relation.model]}AddedAs${targetFieldName}To${this._aliases[modelID]}`] = {
|
|
1128
|
-
type: new GraphQLNonNull(this._types[`${relation.model}-edge`]),
|
|
1129
|
-
args: {
|
|
1130
|
-
id: {
|
|
1131
|
-
type: new GraphQLNonNull(GraphQLID)
|
|
1132
|
-
}
|
|
1133
|
-
},
|
|
1134
|
-
resolve: (edge)=>edge,
|
|
1135
|
-
subscribe: (_src, args, ctx)=>{
|
|
1136
|
-
return ctx.subscribeToDocumentEdgeAdded(this.getModelIDs(relation.model), (data)=>data[fieldName] === args.id);
|
|
1137
|
-
}
|
|
1138
|
-
};
|
|
1139
|
-
fields[`edge${this._aliases[relation.model]}RemovedAs${targetFieldName}From${this._aliases[modelID]}`] = {
|
|
1140
|
-
type: new GraphQLNonNull(GraphQLID),
|
|
1141
|
-
args: {
|
|
1142
|
-
id: {
|
|
1143
|
-
type: new GraphQLNonNull(GraphQLID)
|
|
1144
|
-
}
|
|
1145
|
-
},
|
|
1146
|
-
resolve: (id)=>id,
|
|
1147
|
-
subscribe: (_src, args, ctx)=>{
|
|
1148
|
-
return ctx.subscribeToDocumentEdgeRemoved(this.getModelIDs(relation.model), (data)=>data[fieldName] === args.id);
|
|
1149
|
-
}
|
|
1150
|
-
};
|
|
1151
|
-
}
|
|
1152
|
-
_buildString(schema, parentName) {
|
|
1153
|
-
if (schema.const != null) {
|
|
1154
|
-
return GraphQLString;
|
|
1155
|
-
}
|
|
1156
|
-
if (schema.enum != null) {
|
|
1157
|
-
return this._buildEnumString(schema, parentName);
|
|
1158
|
-
}
|
|
1159
|
-
if (schema.format != null) {
|
|
1160
|
-
return this._buildFormatString(schema);
|
|
1161
|
-
}
|
|
1162
|
-
return this._buildCustomString(schema);
|
|
1163
|
-
}
|
|
1164
|
-
_buildCustomString(schema) {
|
|
1165
|
-
if (schema.title == null) {
|
|
1166
|
-
// Plain string type
|
|
1167
|
-
return GraphQLString;
|
|
1168
|
-
}
|
|
1169
|
-
const title = schema.title.toLowerCase();
|
|
1170
|
-
return STRING_TITLES[title] ?? GraphQLString;
|
|
1171
|
-
}
|
|
1172
|
-
_buildEnumString(schema, parentName = '') {
|
|
1173
|
-
const name = pascalCase(parentName + schema.title);
|
|
1174
|
-
const typeName = `${name}-enum`;
|
|
1175
|
-
if (this._types[typeName] == null) {
|
|
1176
|
-
const values = {};
|
|
1177
|
-
for (const value of schema.enum){
|
|
1178
|
-
values[value] = {
|
|
1179
|
-
value
|
|
1180
|
-
};
|
|
1181
|
-
}
|
|
1182
|
-
this._types[typeName] = new GraphQLEnumType({
|
|
1183
|
-
name,
|
|
1184
|
-
values
|
|
1185
|
-
});
|
|
1186
|
-
}
|
|
1187
|
-
return this._types[typeName];
|
|
1188
|
-
}
|
|
1189
|
-
_buildFormatString(schema) {
|
|
1190
|
-
const scalar = STRING_FORMATS[schema.format];
|
|
1191
|
-
if (scalar == null) {
|
|
1192
|
-
throw new Error(`Unsupported string format: ${schema.format}`);
|
|
1193
|
-
}
|
|
1194
|
-
return scalar;
|
|
1195
|
-
}
|
|
1196
|
-
}
|
|
1197
|
-
export function createSchema(record, aliases) {
|
|
1198
|
-
return new SchemaBuilder({
|
|
1199
|
-
record,
|
|
1200
|
-
aliases
|
|
1201
|
-
}).build();
|
|
1202
|
-
}
|
|
1
|
+
import{AttachmentID as e,DocumentID as t,DocumentModelID as i,decodeID as n}from"@kubun/id";import{REFERENCE_PREFIX as s}from"@kubun/protocol";import{pascalCase as l}from"change-case";import{GraphQLBoolean as r,GraphQLEnumType as o,GraphQLFloat as a,GraphQLID as u,GraphQLInputObjectType as d,GraphQLInt as c,GraphQLInterfaceType as p,GraphQLList as h,GraphQLNonNull as f,GraphQLObjectType as m,GraphQLSchema as y,GraphQLString as _,isEnumType as b,isListType as w,isNonNullType as g}from"graphql";import{connectionArgs as D,connectionDefinitions as I,mutationWithClientMutationId as O,nodeDefinitions as $}from"graphql-relay";import{GraphQLDateTimeISO as j,GraphQLDID as v,GraphQLDuration as S,GraphQLEmailAddress as M,GraphQLJSON as F,GraphQLJSONObject as T,GraphQLURL as R}from"graphql-scalars";import{GraphQLAttachmentID as A,GraphQLDocID as L}from"./identifiers.js";import{getUniqueDocValue as q}from"./utils.js";let P={date:_,time:_,"date-time":_,duration:S,email:M,uri:R},E={attachmentid:A,did:v,docid:L};function G(e){return e.slice(s.length)}function C(e){return w(e)||g(e)&&w(e.ofType)}export function toDocumentModelID(e){return e instanceof i?e:i.fromString(e)}export function getGlobalID(e,t){let i=toDocumentModelID(e);return i.isLocal?i.toGlobal(toDocumentModelID(t)):i}export function toList(e){return new h(new f(e))}export function toOuputList(e){return new h(new f(e))}function V(e){return new d({name:`${e.name}ValueFilter`,isOneOf:!0,fields:{isNull:{type:r},equalTo:{type:e},notEqualTo:{type:e},in:{type:toList(e)},notIn:{type:toList(e)},lessThan:{type:e},lessThanOrEqualTo:{type:e},greaterThan:{type:e},greaterThanOrEqualTo:{type:e}}})}let x={AccountValueFilter:new d({name:"AccountValueFilter",isOneOf:!0,fields:{equalTo:{type:v},notEqualTo:{type:v},in:{type:toList(v)},notIn:{type:toList(v)}}}),BooleanValueFilter:new d({name:"BooleanValueFilter",isOneOf:!0,fields:{isNull:{type:r},equalTo:{type:r}}}),FloatValueFilter:V(a),IntValueFilter:V(c),StringValueFilter:V(_)},B={GraphQLBoolean:"BooleanValueFilter",GraphQLDID:"AccountValueFilter",GraphQLFloat:"FloatValueFilter",GraphQLInt:"IntValueFilter",GraphQLString:"StringValueFilter"},k=new d({name:"PatchSetOperation",fields:{path:{type:new f(_)},value:{type:F}}}),N=new d({name:"PatchRemoveOperation",fields:{path:{type:new f(_)}}}),Q=new d({name:"PatchDestinationOperation",fields:{from:{type:new f(_)},path:{type:new f(_)}}});export const PatchOperation=new d({name:"PatchOperation",isOneOf:!0,fields:{add:{type:k},set:{type:k},remove:{type:N},replace:{type:k},copy:{type:Q},move:{type:Q}}});let U=new o({name:"OrderByDirection",values:{ASC:{value:"asc"},DESC:{value:"desc"}}});export class SchemaBuilder{_aliases;_aliasToModelID;_definitions;_implemented={};_includedModelIDs;_includeInterfaceDependencies;_includeMutations;_includeRelationDependencies;_includeSubscriptions;_inputObjects={...x,PatchOperation};_mutations={};_nodeModelIDs;_onlyModels;_record;_references={};_relationsTo={};_subscriptions={};_types={OrderByDirection:U};constructor(e){for(let[t,i]of(this._record=e.record,this._onlyModels=e.onlyModels??null,this._includeInterfaceDependencies=e.includeInterfaceDependencies??!0,this._includeRelationDependencies=e.includeRelationDependencies??!0,this._includeMutations=e.includeMutations??!0,this._includeSubscriptions=e.includeSubscriptions??!0,this._aliasToModelID=e.aliases??{},this._aliases={},Object.entries(this._aliasToModelID)))this._aliases[i]=t;this._includedModelIDs=this._resolveModelIDsToInclude();let t={};for(let[n,s]of Object.entries(e.record)){let e=i.fromString(n);for(let i of(Object.assign(this._references,s.schema.$defs??{}),s.interfaces)){let s=getGlobalID(i,e).toString();t[s]??=new Set,t[s].add(n)}for(let[t,i]of Object.entries(s.fieldsMeta))if("document"===i.type&&null!=i.model){let l=G(s.schema.properties[t]?.$ref),r="array"===this._getReferenceSchema(l).type,o=getGlobalID(i.model,e).toString();this._relationsTo[o]??={},this._relationsTo[o][t]??=[],this._relationsTo[o][t].push({model:n,isList:r})}}let n=new Set;for(let e of Object.keys(t)){let i=new Set;for(let s of(!function e(t,i,n){let s=i[n];if(null==s)throw Error(`Interface ${n} not found in record`);for(let n of s)null!=i[n]?e(t,i,n):t.add(n)}(i,t,e),0!==i.size&&(this._implemented[e]=Array.from(i)),i))n.add(s)}this._nodeModelIDs=Array.from(n)}_resolveModelIdentifier(e){if(null!=this._record[e])return e;let t=this._aliasToModelID[e];if(null!=t&&null!=this._record[t])return t;throw Error(`Model identifier "${e}" not found. Must be a valid model ID or alias.`)}_resolveModelIDsToInclude(){if(null==this._onlyModels||0===this._onlyModels.length)return new Set(Object.keys(this._record));let e=new Set;for(let t of this._onlyModels){let i=this._resolveModelIdentifier(t);e.add(i)}if(!this._includeInterfaceDependencies&&!this._includeRelationDependencies)return e;let t=Array.from(e),i=new Set;for(;t.length>0;){let n=t.pop();if(null==n||i.has(n))continue;i.add(n);let s=this._record[n];if(null!=s){if(this._includeInterfaceDependencies)for(let i of s.interfaces){let s=getGlobalID(i,n).toString();e.has(s)||(e.add(s),t.push(s))}if(this._includeRelationDependencies){for(let i of Object.values(s.fieldsMeta))if("document"===i.type&&null!=i.model&&null!==i.model){let s=getGlobalID(i.model,n).toString();e.has(s)||(e.add(s),t.push(s))}}}}return e}getModelIDs(e){return null===e?this._nodeModelIDs:this._implemented[e]??[e]}build(){let e=this._buildSharedDefinitions();this._definitions=e;let t={nodes:e.nodesField,...e.queryFields};for(let e of this._includedModelIDs){this._buildDocument(e);let i=this._aliases[e],n={type:this._inputObjects[`${e}-filter`]};t[`all${i}`]={type:this._types[`${e}-connection`],args:{...D,filter:n,orderBy:{type:toList(this._inputObjects[`${e}-order`])},search:{type:_}},resolve:async(t,i,n)=>await n.resolveConnection(this.getModelIDs(e),i)}}let i=[...this._includedModelIDs][0];if(null!=i){let n=this._types[`${i}-connection`].getFields().pageInfo.type,s=new m({name:"SearchResultEdge",fields:()=>({cursor:{type:new f(_)},node:{type:new f(e.nodeInterface)},rank:{type:new f(a)}})}),l=new m({name:"SearchResultConnection",fields:()=>({edges:{type:new f(new h(new f(s)))},pageInfo:{type:n}})});t.search={type:new f(l),args:{query:{type:new f(_)},models:{type:new h(new f(_))},first:{type:c},after:{type:_}},resolve:async(e,t,i)=>{let{query:n,models:s,first:l}=t;if(""===n.trim())throw Error("Search query must not be empty");let r=s?s.map(e=>this._aliasToModelID[e]??e):[...this._includedModelIDs],o=await i.searchDocuments(n,r,l??20),a=o.map(e=>e.documentID),u=[...new Set(o.map(e=>e.modelID))],d=new Map((a.length>0?await i.resolveList(u,a):[]).map(e=>[e.id,e])),c=[];for(let e of o){let t=d.get(e.documentID);null!=t&&c.push({cursor:String(c.length),node:t,rank:Math.abs(e.rank)})}return{edges:c,pageInfo:{hasNextPage:!1,hasPreviousPage:!1,startCursor:c.length>0?c[0].cursor:null,endCursor:c.length>0?c[c.length-1].cursor:null}}}}}this._includeMutations&&(this._mutations.setModelAccessDefaults={type:e.modelAccessDefaultsObject,args:{modelId:{type:new f(u)},permissionType:{type:new f(_)},accessLevel:{type:new f(_)},allowedDIDs:{type:toList(_)}},resolve:async(e,t,i)=>await i.executeSetModelAccessDefaults(t.modelId,t.permissionType,t.accessLevel,t.allowedDIDs??null)},this._mutations.removeModelAccessDefaults={type:r,args:{modelId:{type:new f(u)},permissionTypes:{type:new f(toList(_))}},resolve:async(e,t,i)=>(await i.executeRemoveModelAccessDefaults(t.modelId,t.permissionTypes),!0)},this._mutations.setDocumentAccessOverride={type:e.documentInterface,args:{documentId:{type:new f(u)},permissionType:{type:new f(_)},accessLevel:{type:new f(_)},allowedDIDs:{type:toList(_)}},resolve:async(e,t,i)=>await i.executeSetDocumentAccessOverride(t.documentId,t.permissionType,t.accessLevel,t.allowedDIDs??null)},this._mutations.removeDocumentAccessOverride={type:r,args:{documentId:{type:new f(u)},permissionTypes:{type:new f(toList(_))}},resolve:async(e,t,i)=>(await i.executeRemoveDocumentAccessOverride(t.documentId,t.permissionTypes),!0)});let n={query:new m({name:"Query",fields:t})};return this._includeMutations&&(n.mutation=new m({name:"Mutation",fields:this._mutations})),this._includeSubscriptions&&(n.subscription=new m({name:"Subscription",fields:()=>{let t={documentChanged:{type:new f(e.documentInterface),args:{id:{type:new f(u)}},resolve:e=>e,subscribe:(e,t,i)=>i.subscribeToDocumentChanged(t.id)},documentRemoved:{type:new f(u),resolve:e=>e,subscribe:(e,t,i)=>i.subscribeToDocumentRemoved()}};for(let[e,i]of Object.entries(this._record)){if(!this._includedModelIDs.has(e))continue;let n=this._aliases[e];for(let s of(t[`new${n}Created`]={type:new f(this._types[e]),resolve:e=>e,subscribe:(t,i,n)=>n.subscribeToDocumentCreated(this.getModelIDs(e))},i.interfaces??[])){let i=getGlobalID(s,e).toString();for(let[e,n]of Object.entries(this._relationsTo[i]??{}))for(let s of n)this._buildRelationSubscriptions(t,i,e,s)}for(let[i,n]of Object.entries(this._relationsTo[e]??{}))for(let s of n)this._includedModelIDs.has(s.model)&&this._buildRelationSubscriptions(t,e,i,s)}return t}})),new y(n)}_getDefinitions(){if(null==this._definitions)throw Error("Definitions must be initialized");return this._definitions}_getReference(e,t){let i=G(e),n=this._types[i];if(null!=n)return n;let s=this._getReferenceSchema(i);return this._buildReference(i,s,t)}_getReferenceSchema(e){let t=this._references[e];if(null==t)throw Error(`Could not find reference: ${e}`);return t}_getReferenceInput(e,t,i=""){let n=G(e),s=this._getReferenceSchema(n);if("array"===s.type)return new h(new f(this._getReferenceInput(s.items.$ref,!1,s.title)));if("object"===s.type){if(s.additionalProperties)return T;let e=t?`${n}-partial`:n,r=this._inputObjects[e];if(null!=r)return r;let o={};for(let[e,i]of Object.entries(s.properties)){let n=this._getReferenceInput(i.$ref,t,s.title);o[e]={type:t||!s.required.includes(e)?n:new f(n)}}let a=this._aliases[n]??l(i+s.title),u=new d({name:t?`Partial${a}Input`:`${a}Input`,fields:o});return this._inputObjects[e]=u,u}return this._buildScalar(n,s,i)}_buildSharedDefinitions(){let i=$(async(i,s)=>{if(i.startsWith("did:"))return i;let l=n(i);if(l instanceof e)return l;if(l instanceof t)return await s.loadDocument(i);throw Error(`Unsupported node ID: ${i}`)},t=>"string"==typeof t?"AccountNode":t instanceof e?"AttachmentNode":this._aliases[t.model]),s=new m({name:"AccountNode",interfaces:[i.nodeInterface],fields:()=>{let e={id:{type:new f(u),description:"Globally unique identifier of the account (DID string)",resolve:e=>e},isViewer:{type:new f(r),description:"Whether the authenticated request issuer is this account or not",resolve:(e,t,i)=>i.getViewer()===e}};for(let[i,n]of Object.entries(this._record))if(this._includedModelIDs.has(i))switch(n.behavior){case"interface":continue;case"default":{let t=this._aliases[i];e[`own${t}Connection`]={type:this._types[`${i}-connection`],args:{...D,filter:{type:this._inputObjects[`${i}-filter`]},orderBy:{type:toList(this._inputObjects[`${i}-order`])}},resolve:async(e,t,n)=>await n.resolveConnection(this.getModelIDs(i),{...t,owner:e})};break}case"unique":{let s=this._aliases[i];e[`own${s}`]={type:this._types[i],args:n.uniqueFields.length?{with:{type:new f(this._buildSetInputObjectType(i,n.uniqueFields))}}:{},resolve:async(e,s,l)=>{let r=q(n.uniqueFields,s.with),o=t.create(i,e,r);return await l.loadDocument(o.toString())}}}}return e}}),l=new m({name:"AttachmentNode",interfaces:[i.nodeInterface],fields:{id:{type:new f(u),resolve:e=>e.toString()},contentLength:{type:new f(c)},mimeType:{type:new f(_)}}}),o=new m({name:"AccessRule",fields:{level:{type:new f(_)},allowedDIDs:{type:new h(new f(_))}}}),a=new m({name:"AccessPermissions",fields:{read:{type:o},write:{type:o}}}),d=new m({name:"ModelAccessDefaults",fields:()=>({ownerDID:{type:new f(_)},modelId:{type:new f(u)},permissions:{type:new f(a)}})}),y={id:{type:new f(u)},model:{type:new f(_)},owner:{type:new f(s)},createdAt:{type:new f(j)},updatedAt:{type:j},accessPermissions:{type:a,resolve:e=>e.data?.accessPermissions??null}},b=new p({name:"DocumentNode",interfaces:[i.nodeInterface],fields:y,resolveType:e=>this._aliases[e.model]}),w={node:i.nodeField,viewer:{type:s,description:"Account issuing the request, if authenticated",resolve:(e,t,i)=>i.getViewer()}};return{...i,accountObject:s,attachmentObject:l,modelAccessDefaultsObject:d,documentInterface:b,documentNodeFields:y,queryFields:w}}_buildDocument(e){if(null!=this._types[e])return this._types[e];let t=this._record[e];if(null==t)throw Error(`Could not find model id: ${e}`);let i=this._getDefinitions();this._aliases[e]??=l(t.name);let n=this._aliases[e],s={name:n,interfaces:()=>[i.documentInterface,i.nodeInterface].concat(t.interfaces.map(t=>this._types[getGlobalID(t,e).toString()]).filter(e=>null!=e)),fields:()=>this._buildDocumentFields(e,t,n)},r="interface"===t.behavior,o=r?new p({...s,resolveType:e=>this._aliases[e.model]}):new m(s),{connectionType:a,edgeType:d}=I({nodeType:o});return this._types[e]=o,this._types[`${e}-connection`]=a,this._types[`${e}-edge`]=d,this._buildDocumentDataObject(e,t,n),this._buildDocumentInput(e,t),this._buildObjectFilterInput(e,t.schema,n,!0),this._buildObjectOrderByInput(e,t.schema,n,!0),!r&&this._includeMutations&&("default"===t.behavior?this._mutations[`create${n}`]=O({name:`Create${n}`,inputFields:()=>({data:{type:new f(this._inputObjects[e])}}),outputFields:()=>({...i.queryFields,document:{type:this._types[e]}}),mutateAndGetPayload:async(t,i,n)=>({document:await i.executeCreateMutation(e,t.data,n)})}):"unique"===t.behavior&&(this._mutations[`set${n}`]=O({name:`Set${n}`,inputFields:()=>({data:{type:new f(this._inputObjects[e])}}),outputFields:()=>({...i.queryFields,document:{type:this._types[e]}}),mutateAndGetPayload:async(i,n,s)=>{let l=q(t.uniqueFields,i.data);return{document:await n.executeSetMutation(e,l,i.data,s)}}})),this._mutations[`update${n}`]=O({name:`Update${n}`,inputFields:()=>({id:{type:new f(u)},patch:{type:new f(toList(PatchOperation))},from:{type:this._inputObjects[`${e}-update`]}}),outputFields:()=>({...i.queryFields,document:{type:this._types[e]}}),mutateAndGetPayload:async(e,t,i)=>({document:await t.executeUpdateMutation(e,i)})}),this._mutations[`remove${n}`]=O({name:`Remove${n}`,inputFields:()=>({id:{type:new f(u)}}),outputFields:()=>({...i.queryFields,id:{type:new f(u)}}),mutateAndGetPayload:async(e,t,i)=>(await t.executeRemoveMutation(e.id,i),{id:e.id})})),o}_buildDocumentFields(t,i,n){let s=this._buildDocumentDataObject(t,i,n),l=this._getDefinitions(),r={...l.documentNodeFields,data:{type:new f(s)}};for(let[n,o]of Object.entries(s.getFields())){let s=i.fieldsMeta[n];if(null!=s)switch(s.type){case"account":C(o.type)?r[`${n}Accounts`]={type:new f(toOuputList(l.accountObject)),resolve:e=>e.data?.[n]??[]}:r[`${n}Account`]={type:l.accountObject,resolve:e=>e.data?.[n]??null};break;case"attachment":C(o.type)?r[`${n}Attachments`]={type:new f(toOuputList(l.attachmentObject)),resolve:t=>(t.data?.[n]??[]).map(e.fromString)}:r[`${n}Attachment`]={type:l.attachmentObject,resolve:t=>{let i=t.data?.[n];return i?e.fromString(i):null}};break;case"document":{let e,i,a=s.model;if(void 0===a)continue;null===a?(e=null,i=l.documentInterface):(e=getGlobalID(a,t).toString(),this._includedModelIDs.has(e)?i=this._buildDocument(e):(i=l.documentInterface,e=null)),C(o.type)?r[`${n}Documents`]={type:new f(toOuputList(i)),resolve:async(t,i,s)=>{let l=t.data?.[n]??[];return l.length?await s.resolveList(this.getModelIDs(e),l):[]}}:r[`${n}Document`]={type:i,resolve:async(e,t,i)=>{let s=e.data?.[n];return s?await i.loadDocument(s):null}}}}}for(let e of i.interfaces??[]){let i=getGlobalID(e,t).toString();for(let[e,t]of Object.entries(this._relationsTo[i]??{}))for(let i of t)this._includedModelIDs.has(i.model)&&this._buildRelationFields(r,e,i)}for(let[e,i]of Object.entries(this._relationsTo[t]??{}))for(let t of i)this._includedModelIDs.has(t.model)&&this._buildRelationFields(r,e,t);return r}_buildDocumentDataObject(e,t,i){let n=`${e}-data`;if(null!=this._types[n])return this._types[n];let s=t.schema.required??[],l={};for(let[i,n]of Object.entries(t.schema.properties??{})){let t=n.$ref;if(null==t)throw Error(`Missing ref for field ${i} of object ${e}`);let r=this._getReference(t);l[i]={type:s.includes(i)?new f(r):r}}let r={name:`${i}Data`,interfaces:()=>t.interfaces.map(t=>{let i=`${getGlobalID(t,e).toString()}-data`;return this._types[i]}).filter(e=>null!=e),fields:l},o="interface"===t.behavior?new p(r):new m(r);return this._types[n]=o,o}_buildDocumentInput(e,t){if("interface"===t.behavior)return;let i={},n={};for(let[e,s]of Object.entries(t.schema.properties)){let l=s.$ref,r=this._getReferenceInput(l,!1);i[e]={type:t.schema.required.includes(e)?new f(r):r};let o=G(l);null==this._getReferenceSchema(o).const&&(n[e]={type:this._getReferenceInput(l,!0)})}let s=this._aliases[e];this._inputObjects[e]=new d({name:`${s}Input`,fields:i}),this._inputObjects[`${e}-update`]=new d({name:`Partial${s}Input`,fields:n})}_buildReferenceFilterInput(e,t=""){let i=G(e),n=this._getReferenceSchema(i),s=t;if("object"===n.type)return n.additionalProperties?null:this._buildObjectFilterInput(i,n,t);if("array"===n.type){let e=this._aliases[i]??l(t+n.title);return s=e,new d({name:`${e}Filter`,isOneOf:!0,fields:{isNull:{type:r}}})}let o=this._buildScalar(i,n,s),a=B[o.name];if(null!=a)return this._inputObjects[a];let u=b(o)?new d({name:`${o.name}ValueFilter`,isOneOf:!0,fields:{isNull:{type:r},equalTo:{type:o},notEqualTo:{type:o},in:{type:toList(o)},notIn:{type:toList(o)}}}):V(o);return null==this._inputObjects[u.name]&&(this._inputObjects[u.name]=u),this._inputObjects[u.name]}_buildReferenceOrderByInput(e,t=""){let i=G(e),n=this._getReferenceSchema(i);switch(n.type){case"array":return null;case"object":return n.additionalProperties?null:this._buildObjectOrderByInput(i,n,t);default:return U}}_buildObjectFilterInput(e,t,i="",n=!1){let s=`${e}-filter`,r=this._inputObjects[s];if(null!=r)return r;let o=this._aliases[e]??l(i+(t.title??"")),a=n?{_owner:{type:this._inputObjects.AccountValueFilter}}:{};for(let[e,i]of Object.entries(t.properties)){let t=this._buildReferenceFilterInput(i.$ref,o);null!==t&&(a[e]={type:t})}let u=new d({name:`${o}ObjectFilter`,fields:a}),c=new d({name:`${o}Filter`,isOneOf:!0,fields:()=>({where:{type:u},and:{type:toList(this._inputObjects[s])},or:{type:toList(this._inputObjects[s])},not:{type:this._inputObjects[s]}})});return this._inputObjects[s]=c,c}_buildObjectOrderByInput(e,t,i="",n=!1){let s=`${e}-order`,r=this._inputObjects[s];if(null!=r)return r;let o=this._aliases[e]??l(i+(t.title??"")),a=n?{_docOwner:{type:U},_createdAt:{type:U}}:{};for(let[e,i]of Object.entries(t.properties)){let t=this._buildReferenceOrderByInput(i.$ref,o);null!=t&&(a[e]={type:t})}let u=new d({name:`${o}OrderBy`,isOneOf:!0,fields:a});return this._inputObjects[s]=u,u}_buildSetInputObjectType(e,t,i){let n=i?l(i):"",s=`${e}-with-${n}`,r=this._inputObjects[s];if(null!=r)return r;let o=this._record[e],a=this._aliases[e];return this._inputObjects[s]=new d({name:`With${n}${a}Input`,fields:()=>{let e={};for(let i of t){let t=o.schema.properties[i];if(null==t)throw Error(`Field ${i} not found on model ${a}`);let n=this._getReferenceInput(t.$ref,!1);e[i]={type:new f(n)}}return e}}),this._inputObjects[s]}_buildReference(e,t,i=""){if(null!=this._types[e])return this._types[e];switch(t.type){case"array":return this._types[e]=this._buildArray(t,i),this._types[e];case"object":return this._types[e]=t.additionalProperties?T:this._buildObject(e,t,i),this._types[e];default:return this._buildScalar(e,t,i)}}_buildScalar(e,t,i=""){if(null!=this._types[e])return this._types[e];switch(t.type){case"boolean":this._types[e]=r;break;case"integer":this._types[e]=c;break;case"number":this._types[e]=a;break;case"string":this._types[e]=this._buildString(t,i)}return this._types[e]}_buildArray(e,t=""){let i=e.items.$ref;return new h(new f(this._getReference(i,l(t+e.title))))}_buildObject(e,t,i=""){let n=l(i+t.title),s=this._buildObjectFields(e,t,n);return new m({name:n,fields:s})}_buildObjectFields(e,t,i=""){let n=t.required??[],s={};for(let[l,r]of Object.entries(t.properties??{})){let t=r.$ref;if(null==t)throw Error(`Missing ref for field ${l} of object ${e}`);let o=this._getReference(t,i);s[l]={type:n.includes(l)?new f(o):o}}return s}_buildRelationFields(e,t,i){if(i.isList)return;let n=i.model,s=`${l(t)}Of${this._aliases[n]}`;e[`in${s}`]={type:new f(this._types[`${n}-connection`]),args:{...D,filter:{type:this._inputObjects[`${n}-filter`]},orderBy:{type:toList(this._inputObjects[`${n}-order`])}},resolve:(e,i,s)=>{let l={where:{[t]:{equalTo:e.id}}},r=i.filter?{and:[i.filter,l]}:l;return s.resolveConnection(this.getModelIDs(n),{...i,filter:r})}}}_buildRelationSubscriptions(e,t,i,n){if(n.isList)return;let s=l(i);e[`edge${this._aliases[n.model]}AddedAs${s}To${this._aliases[t]}`]={type:new f(this._types[`${n.model}-edge`]),args:{id:{type:new f(u)}},resolve:e=>e,subscribe:(e,t,s)=>s.subscribeToDocumentEdgeAdded(this.getModelIDs(n.model),e=>e[i]===t.id)},e[`edge${this._aliases[n.model]}RemovedAs${s}From${this._aliases[t]}`]={type:new f(u),args:{id:{type:new f(u)}},resolve:e=>e,subscribe:(e,t,s)=>s.subscribeToDocumentEdgeRemoved(this.getModelIDs(n.model),e=>e[i]===t.id)}}_buildString(e,t){return null!=e.const?_:null!=e.enum?this._buildEnumString(e,t):null!=e.format?this._buildFormatString(e):this._buildCustomString(e)}_buildCustomString(e){return null==e.title?_:E[e.title.toLowerCase()]??_}_buildEnumString(e,t=""){let i=l(t+e.title),n=`${i}-enum`;if(null==this._types[n]){let t={};for(let i of e.enum)t[i]={value:i};this._types[n]=new o({name:i,values:t})}return this._types[n]}_buildFormatString(e){let t=P[e.format];if(null==t)throw Error(`Unsupported string format: ${e.format}`);return t}}export function createSchema(e){return new SchemaBuilder(e).build()}
|