@smartive/graphql-magic 14.1.0 → 15.0.1
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/.env +1 -1
- package/CHANGELOG.md +3 -3
- package/README.md +1 -36
- package/dist/bin/gqm.cjs +171 -123
- package/dist/cjs/index.cjs +133 -118
- package/dist/esm/context.d.ts +1 -1
- package/dist/esm/models/utils.d.ts +4 -4
- package/dist/esm/permissions/check.d.ts +1 -0
- package/dist/esm/permissions/check.js +19 -11
- package/dist/esm/permissions/check.js.map +1 -1
- package/dist/esm/resolvers/filters.js +1 -1
- package/dist/esm/resolvers/filters.js.map +1 -1
- package/dist/esm/resolvers/mutations.js +4 -4
- package/dist/esm/resolvers/mutations.js.map +1 -1
- package/dist/esm/resolvers/resolver.js +3 -0
- package/dist/esm/resolvers/resolver.js.map +1 -1
- package/dist/esm/resolvers/resolvers.d.ts +1 -1
- package/dist/esm/resolvers/resolvers.js +29 -23
- package/dist/esm/resolvers/resolvers.js.map +1 -1
- package/dist/esm/resolvers/selects.js +4 -3
- package/dist/esm/resolvers/selects.js.map +1 -1
- package/dist/esm/schema/generate.js +76 -72
- package/dist/esm/schema/generate.js.map +1 -1
- package/docker-compose.yml +0 -4
- package/docs/README.md +41 -0
- package/docs/babel.config.js +3 -0
- package/docs/docs/tutorial.md +425 -0
- package/docs/docusaurus.config.ts +110 -0
- package/docs/package-lock.json +14680 -0
- package/docs/package.json +47 -0
- package/docs/sidebars.ts +31 -0
- package/docs/src/css/custom.css +30 -0
- package/docs/src/pages/index.module.css +23 -0
- package/docs/src/pages/index.tsx +39 -0
- package/docs/src/pages/markdown-page.md +7 -0
- package/docs/static/.nojekyll +0 -0
- package/docs/static/img/docusaurus-social-card.jpg +0 -0
- package/docs/static/img/docusaurus.png +0 -0
- package/docs/static/img/favicon.ico +0 -0
- package/docs/static/img/logo.svg +1 -0
- package/docs/static/img/undraw_docusaurus_mountain.svg +171 -0
- package/docs/static/img/undraw_docusaurus_react.svg +170 -0
- package/docs/static/img/undraw_docusaurus_tree.svg +40 -0
- package/docs/tsconfig.json +7 -0
- package/package.json +5 -5
- package/src/bin/gqm/codegen.ts +3 -1
- package/src/bin/gqm/gqm.ts +3 -69
- package/src/bin/gqm/parse-knexfile.ts +5 -6
- package/src/bin/gqm/settings.ts +32 -5
- package/src/bin/gqm/templates.ts +60 -8
- package/src/context.ts +1 -1
- package/src/permissions/check.ts +24 -16
- package/src/resolvers/filters.ts +1 -1
- package/src/resolvers/mutations.ts +4 -4
- package/src/resolvers/resolver.ts +4 -0
- package/src/resolvers/resolvers.ts +33 -27
- package/src/resolvers/selects.ts +4 -3
- package/src/schema/generate.ts +78 -72
package/src/bin/gqm/templates.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
|
-
export const
|
|
2
|
-
|
|
1
|
+
export const GITIGNORE = (path: string) => `
|
|
2
|
+
# graphql-magic
|
|
3
|
+
${path}/**/*
|
|
4
|
+
${path}/**/.gitkeep
|
|
5
|
+
`;
|
|
6
|
+
|
|
7
|
+
export const EMPTY_MODELS = `import { ModelDefinitions, Models } from '@smartive/graphql-magic';
|
|
3
8
|
|
|
4
|
-
const
|
|
9
|
+
const modelDefinitions: ModelDefinitions = [
|
|
5
10
|
{
|
|
6
11
|
kind: 'entity',
|
|
7
12
|
name: 'User',
|
|
@@ -9,11 +14,10 @@ const rawModels: RawModels = [
|
|
|
9
14
|
},
|
|
10
15
|
]
|
|
11
16
|
|
|
12
|
-
export const models = new Models(
|
|
17
|
+
export const models = new Models(modelDefinitions);
|
|
13
18
|
`;
|
|
14
19
|
|
|
15
|
-
export const KNEXFILE = `
|
|
16
|
-
import { DateTime } from 'luxon';
|
|
20
|
+
export const KNEXFILE = `import { DateTime } from 'luxon';
|
|
17
21
|
import { types } from 'pg';
|
|
18
22
|
|
|
19
23
|
const dateOids = { date: 1082, timestamptz: 1184, timestamp: 1114 };
|
|
@@ -26,7 +30,7 @@ for (const oid of Object.values(numberOids)) {
|
|
|
26
30
|
types.setTypeParser(oid, Number);
|
|
27
31
|
}
|
|
28
32
|
|
|
29
|
-
const
|
|
33
|
+
const knexConfig = {
|
|
30
34
|
client: 'postgresql',
|
|
31
35
|
connection: {
|
|
32
36
|
host: process.env.DATABASE_HOST,
|
|
@@ -43,5 +47,53 @@ const config = {
|
|
|
43
47
|
},
|
|
44
48
|
} as const;
|
|
45
49
|
|
|
46
|
-
export default
|
|
50
|
+
export default knexConfig;
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
export const GET_ME = `import { gql } from '@smartive/graphql-magic';
|
|
54
|
+
|
|
55
|
+
export const GET_ME = gql\`
|
|
56
|
+
query GetMe {
|
|
57
|
+
me {
|
|
58
|
+
id
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
\`;
|
|
62
|
+
`;
|
|
63
|
+
|
|
64
|
+
export const EXECUTE = `
|
|
65
|
+
import knexConfig from "@/knexfile";
|
|
66
|
+
import { Context, User, execute } from "@smartive/graphql-magic";
|
|
67
|
+
import { randomUUID } from "crypto";
|
|
68
|
+
import { knex } from 'knex';
|
|
69
|
+
import { DateTime } from "luxon";
|
|
70
|
+
import { models } from "../config/models";
|
|
71
|
+
|
|
72
|
+
export const executeGraphql = async <T, V = undefined>(
|
|
73
|
+
body: {
|
|
74
|
+
query: string;
|
|
75
|
+
operationName?: string;
|
|
76
|
+
variables?: V;
|
|
77
|
+
options?: { email?: string };
|
|
78
|
+
}): Promise<{ data: T }> => {
|
|
79
|
+
const db = knex(knexConfig);
|
|
80
|
+
let user: User | undefined;
|
|
81
|
+
// TODO: get user
|
|
82
|
+
|
|
83
|
+
const result = await execute({
|
|
84
|
+
req: null as unknown as Context['req'],
|
|
85
|
+
body,
|
|
86
|
+
knex: db as unknown as Context['knex'],
|
|
87
|
+
locale: 'en',
|
|
88
|
+
locales: ['en'],
|
|
89
|
+
user,
|
|
90
|
+
models: models,
|
|
91
|
+
permissions: { ADMIN: true, UNAUTHENTICATED: true },
|
|
92
|
+
now: DateTime.local(),
|
|
93
|
+
});
|
|
94
|
+
await db.destroy();
|
|
95
|
+
|
|
96
|
+
// https://github.com/vercel/next.js/issues/47447#issuecomment-1500371732
|
|
97
|
+
return JSON.parse(JSON.stringify(result)) as { data: T };
|
|
98
|
+
}
|
|
47
99
|
`;
|
package/src/context.ts
CHANGED
package/src/permissions/check.ts
CHANGED
|
@@ -7,12 +7,14 @@ import { AliasGenerator, hash, ors } from '../resolvers/utils';
|
|
|
7
7
|
import { BasicValue } from '../values';
|
|
8
8
|
import { PermissionAction, PermissionLink, PermissionStack } from './generate';
|
|
9
9
|
|
|
10
|
+
export const getRole = (ctx: Pick<FullContext, 'user'>) => ctx.user?.role ?? 'UNAUTHENTICATED';
|
|
11
|
+
|
|
10
12
|
export const getPermissionStack = (
|
|
11
13
|
ctx: Pick<FullContext, 'permissions' | 'user'>,
|
|
12
14
|
type: string,
|
|
13
15
|
action: PermissionAction
|
|
14
16
|
): boolean | PermissionStack => {
|
|
15
|
-
const rolePermissions = ctx.permissions[ctx
|
|
17
|
+
const rolePermissions = ctx.permissions[getRole(ctx)];
|
|
16
18
|
if (typeof rolePermissions === 'boolean' || rolePermissions === undefined) {
|
|
17
19
|
return !!rolePermissions;
|
|
18
20
|
}
|
|
@@ -45,7 +47,7 @@ export const applyPermissions = (
|
|
|
45
47
|
}
|
|
46
48
|
|
|
47
49
|
if (permissionStack === false) {
|
|
48
|
-
console.error(`No applicable permissions exist for ${ctx
|
|
50
|
+
console.error(`No applicable permissions exist for ${getRole(ctx)} ${type} ${action}.`);
|
|
49
51
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
|
|
50
52
|
query.where(false);
|
|
51
53
|
return permissionStack;
|
|
@@ -113,7 +115,7 @@ export const getEntityToMutate = async (
|
|
|
113
115
|
.map(([key, value]) => `${key}: ${value}`)
|
|
114
116
|
.join(', ')}`
|
|
115
117
|
);
|
|
116
|
-
throw new PermissionError(ctx
|
|
118
|
+
throw new PermissionError(getRole(ctx), action, `this ${model.name}`, 'no available permissions applied');
|
|
117
119
|
}
|
|
118
120
|
|
|
119
121
|
if (model.parent) {
|
|
@@ -139,7 +141,7 @@ export const checkCanWrite = async (
|
|
|
139
141
|
return;
|
|
140
142
|
}
|
|
141
143
|
if (permissionStack === false) {
|
|
142
|
-
throw new PermissionError(ctx
|
|
144
|
+
throw new PermissionError(getRole(ctx), action, model.plural, 'no applicable permissions');
|
|
143
145
|
}
|
|
144
146
|
|
|
145
147
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- using `select(1 as any)` to instantiate an "empty" query builder
|
|
@@ -156,13 +158,9 @@ export const checkCanWrite = async (
|
|
|
156
158
|
}
|
|
157
159
|
|
|
158
160
|
const fieldPermissions = field[action === 'CREATE' ? 'creatable' : 'updatable'];
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
action,
|
|
163
|
-
`this ${model.name}'s ${field.name}`,
|
|
164
|
-
'field permission not available'
|
|
165
|
-
);
|
|
161
|
+
const role = getRole(ctx);
|
|
162
|
+
if (fieldPermissions && typeof fieldPermissions === 'object' && !fieldPermissions.roles?.includes(role)) {
|
|
163
|
+
throw new PermissionError(role, action, `this ${model.name}'s ${field.name}`, 'field permission not available');
|
|
166
164
|
}
|
|
167
165
|
|
|
168
166
|
linked = true;
|
|
@@ -178,7 +176,7 @@ export const checkCanWrite = async (
|
|
|
178
176
|
|
|
179
177
|
if (fieldPermissionStack === false || !fieldPermissionStack.length) {
|
|
180
178
|
throw new PermissionError(
|
|
181
|
-
|
|
179
|
+
role,
|
|
182
180
|
action,
|
|
183
181
|
`this ${model.name}'s ${field.name}`,
|
|
184
182
|
'no applicable permissions on data to link'
|
|
@@ -194,13 +192,14 @@ export const checkCanWrite = async (
|
|
|
194
192
|
);
|
|
195
193
|
}
|
|
196
194
|
|
|
195
|
+
const role = getRole(ctx);
|
|
197
196
|
if (linked) {
|
|
198
197
|
const canMutate = await query;
|
|
199
198
|
if (!canMutate) {
|
|
200
|
-
throw new PermissionError(
|
|
199
|
+
throw new PermissionError(role, action, `this ${model.name}`, 'no linkable entities');
|
|
201
200
|
}
|
|
202
201
|
} else if (action === 'CREATE') {
|
|
203
|
-
throw new PermissionError(
|
|
202
|
+
throw new PermissionError(role, action, `this ${model.name}`, 'no linkable entities');
|
|
204
203
|
}
|
|
205
204
|
};
|
|
206
205
|
|
|
@@ -213,12 +212,21 @@ const permissionLinkQuery = (
|
|
|
213
212
|
const aliases = new AliasGenerator();
|
|
214
213
|
let alias = aliases.getShort();
|
|
215
214
|
const { type, me, where } = links[0];
|
|
216
|
-
|
|
217
|
-
subQuery.from(`${type} as ${alias}`);
|
|
215
|
+
|
|
218
216
|
if (me) {
|
|
217
|
+
if (!ctx.user) {
|
|
218
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
|
|
219
|
+
subQuery.where(false);
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
219
223
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
|
|
220
224
|
subQuery.where({ [`${alias}.id`]: ctx.user.id });
|
|
221
225
|
}
|
|
226
|
+
|
|
227
|
+
// eslint-disable-next-line @typescript-eslint/no-floating-promises -- we do not need to await knex here
|
|
228
|
+
subQuery.from(`${type} as ${alias}`);
|
|
229
|
+
|
|
222
230
|
if (where) {
|
|
223
231
|
applyWhere(ctx.models.getModel(type, 'entity'), subQuery, alias, where, aliases);
|
|
224
232
|
}
|
package/src/resolvers/filters.ts
CHANGED
|
@@ -38,7 +38,7 @@ export const applyFilters = (node: FieldResolverNode, query: Knex.QueryBuilder,
|
|
|
38
38
|
normalizedArguments.where.deleted &&
|
|
39
39
|
(!Array.isArray(normalizedArguments.where.deleted) || normalizedArguments.where.deleted.some((v) => v))
|
|
40
40
|
) {
|
|
41
|
-
if (node.ctx.user
|
|
41
|
+
if (node.ctx.user?.role !== 'ADMIN') {
|
|
42
42
|
throw new ForbiddenError('You cannot access deleted entries.');
|
|
43
43
|
}
|
|
44
44
|
} else {
|
|
@@ -32,7 +32,7 @@ const create = async (model: EntityModel, { data: input }: { data: any }, ctx: F
|
|
|
32
32
|
const normalizedInput = { ...input };
|
|
33
33
|
normalizedInput.id = uuid();
|
|
34
34
|
normalizedInput.createdAt = ctx.now;
|
|
35
|
-
normalizedInput.createdById = ctx.user
|
|
35
|
+
normalizedInput.createdById = ctx.user?.id;
|
|
36
36
|
if (model.parent) {
|
|
37
37
|
normalizedInput.type = model.name;
|
|
38
38
|
}
|
|
@@ -165,7 +165,7 @@ const del = async (model: EntityModel, { where, dryRun }: { where: any; dryRun:
|
|
|
165
165
|
toDelete[currentModel.name][entity.id as string] = (entity[currentModel.displayField || 'id'] || entity.id) as string;
|
|
166
166
|
|
|
167
167
|
if (!dryRun) {
|
|
168
|
-
const normalizedInput = { deleted: true, deletedAt: ctx.now, deletedById: ctx.user
|
|
168
|
+
const normalizedInput = { deleted: true, deletedAt: ctx.now, deletedById: ctx.user?.id };
|
|
169
169
|
const data = { prev: entity, input: {}, normalizedInput, next: { ...entity, ...normalizedInput } };
|
|
170
170
|
if (ctx.mutationHook) {
|
|
171
171
|
beforeHooks.push(async () => {
|
|
@@ -319,7 +319,7 @@ const createRevision = async (model: EntityModel, data: Entity, ctx: Context) =>
|
|
|
319
319
|
id: revisionId,
|
|
320
320
|
[`${typeToField(model.parent || model.name)}Id`]: data.id,
|
|
321
321
|
createdAt: ctx.now,
|
|
322
|
-
createdById: ctx.user
|
|
322
|
+
createdById: ctx.user?.id,
|
|
323
323
|
};
|
|
324
324
|
|
|
325
325
|
if (model.deletable) {
|
|
@@ -354,7 +354,7 @@ const createRevision = async (model: EntityModel, data: Entity, ctx: Context) =>
|
|
|
354
354
|
const sanitize = (ctx: FullContext, model: EntityModel, data: Entity) => {
|
|
355
355
|
if (model.updatable) {
|
|
356
356
|
data.updatedAt = ctx.now;
|
|
357
|
-
data.updatedById = ctx.user
|
|
357
|
+
data.updatedById = ctx.user?.id;
|
|
358
358
|
}
|
|
359
359
|
|
|
360
360
|
for (const key of Object.keys(data)) {
|
|
@@ -29,6 +29,10 @@ export const resolve = async (ctx: FullContext, id?: string) => {
|
|
|
29
29
|
const { query, verifiedPermissionStacks } = await buildQuery(node);
|
|
30
30
|
|
|
31
31
|
if (ctx.info.fieldName === 'me') {
|
|
32
|
+
if (!node.ctx.user.id) {
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
|
|
32
36
|
void query.where({ [getColumn(node, 'id')]: node.ctx.user.id });
|
|
33
37
|
}
|
|
34
38
|
|
|
@@ -3,23 +3,25 @@ import { isRootModel, merge, not, typeToField } from '../models/utils';
|
|
|
3
3
|
import { mutationResolver } from './mutations';
|
|
4
4
|
import { queryResolver } from './resolver';
|
|
5
5
|
|
|
6
|
-
export const getResolvers = (models: Models) =>
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
.
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
6
|
+
export const getResolvers = (models: Models) => {
|
|
7
|
+
const resolvers: Record<string, any> = {
|
|
8
|
+
Query: merge([
|
|
9
|
+
{
|
|
10
|
+
me: queryResolver,
|
|
11
|
+
},
|
|
12
|
+
...models.entities
|
|
13
|
+
.filter(({ queriable }) => queriable)
|
|
14
|
+
.map((model) => ({
|
|
15
|
+
[typeToField(model.name)]: queryResolver,
|
|
16
|
+
})),
|
|
17
|
+
...models.entities
|
|
18
|
+
.filter(({ listQueriable }) => listQueriable)
|
|
19
|
+
.map((model) => ({
|
|
20
|
+
[model.pluralField]: queryResolver,
|
|
21
|
+
})),
|
|
22
|
+
]),
|
|
23
|
+
};
|
|
24
|
+
const mutations = [
|
|
23
25
|
...models.entities
|
|
24
26
|
.filter(not(isRootModel))
|
|
25
27
|
.filter(({ creatable }) => creatable)
|
|
@@ -39,13 +41,17 @@ export const getResolvers = (models: Models) => ({
|
|
|
39
41
|
[`delete${model.name}`]: mutationResolver,
|
|
40
42
|
[`restore${model.name}`]: mutationResolver,
|
|
41
43
|
})),
|
|
42
|
-
]
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
if (mutations.length) {
|
|
47
|
+
resolvers.Mutation = merge<unknown>(mutations);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
for (const model of models.entities.filter(isRootModel)) {
|
|
51
|
+
resolvers[model.name] = {
|
|
52
|
+
__resolveType: ({ TYPE }) => TYPE,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return resolvers;
|
|
57
|
+
};
|
package/src/resolvers/selects.ts
CHANGED
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
getNameOrAlias,
|
|
12
12
|
getSimpleFields,
|
|
13
13
|
} from '.';
|
|
14
|
-
import { PermissionError, UserInputError } from '..';
|
|
14
|
+
import { PermissionError, UserInputError, getRole } from '..';
|
|
15
15
|
|
|
16
16
|
export const applySelects = (node: ResolverNode, query: Knex.QueryBuilder, joins: Joins) => {
|
|
17
17
|
// Simple field selects
|
|
@@ -28,9 +28,10 @@ export const applySelects = (node: ResolverNode, query: Knex.QueryBuilder, joins
|
|
|
28
28
|
return false;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
const role = getRole(node.ctx);
|
|
32
|
+
if (typeof field.queriable === 'object' && !field.queriable.roles?.includes(role)) {
|
|
32
33
|
throw new PermissionError(
|
|
33
|
-
|
|
34
|
+
role,
|
|
34
35
|
'READ',
|
|
35
36
|
`${node.model.name}'s field "${field.name}"`,
|
|
36
37
|
'field permission not available'
|
package/src/schema/generate.ts
CHANGED
|
@@ -12,7 +12,7 @@ export const generateDefinitions = ({
|
|
|
12
12
|
entities,
|
|
13
13
|
objects,
|
|
14
14
|
}: Models): DefinitionNode[] => {
|
|
15
|
-
|
|
15
|
+
const definitions = [
|
|
16
16
|
// Predefined types
|
|
17
17
|
...rawEnums.map((model) => enm(model.name, model.values)),
|
|
18
18
|
...enums.map((model) => enm(model.name, model.values)),
|
|
@@ -191,84 +191,90 @@ export const generateDefinitions = ({
|
|
|
191
191
|
})),
|
|
192
192
|
...objects.filter((model) => model.name === 'Query').flatMap((model) => model.fields),
|
|
193
193
|
]),
|
|
194
|
+
];
|
|
194
195
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
196
|
+
const mutations = [
|
|
197
|
+
...entities.flatMap((model): Field[] => {
|
|
198
|
+
const mutations: Field[] = [];
|
|
198
199
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
200
|
+
if (!isRootModel(model)) {
|
|
201
|
+
if (model.creatable) {
|
|
202
|
+
mutations.push({
|
|
203
|
+
name: `create${model.name}`,
|
|
204
|
+
type: model.name,
|
|
205
|
+
nonNull: true,
|
|
206
|
+
args: [
|
|
207
|
+
{
|
|
208
|
+
name: 'data',
|
|
209
|
+
type: `Create${model.name}`,
|
|
210
|
+
nonNull: true,
|
|
211
|
+
},
|
|
212
|
+
],
|
|
213
|
+
});
|
|
214
|
+
}
|
|
214
215
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
216
|
+
if (model.updatable) {
|
|
217
|
+
mutations.push({
|
|
218
|
+
name: `update${model.name}`,
|
|
219
|
+
type: model.name,
|
|
220
|
+
nonNull: true,
|
|
221
|
+
args: [
|
|
222
|
+
{
|
|
223
|
+
name: 'where',
|
|
224
|
+
type: `${model.name}WhereUnique`,
|
|
225
|
+
nonNull: true,
|
|
226
|
+
},
|
|
227
|
+
{
|
|
228
|
+
name: 'data',
|
|
229
|
+
type: `Update${model.name}`,
|
|
230
|
+
nonNull: true,
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
});
|
|
234
|
+
}
|
|
234
235
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
}
|
|
236
|
+
if (model.deletable) {
|
|
237
|
+
mutations.push({
|
|
238
|
+
name: `delete${model.name}`,
|
|
239
|
+
type: 'ID',
|
|
240
|
+
nonNull: true,
|
|
241
|
+
args: [
|
|
242
|
+
{
|
|
243
|
+
name: 'where',
|
|
244
|
+
type: `${model.name}WhereUnique`,
|
|
245
|
+
nonNull: true,
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: 'dryRun',
|
|
249
|
+
type: 'Boolean',
|
|
250
|
+
},
|
|
251
|
+
],
|
|
252
|
+
});
|
|
253
|
+
mutations.push({
|
|
254
|
+
name: `restore${model.name}`,
|
|
255
|
+
type: 'ID',
|
|
256
|
+
nonNull: true,
|
|
257
|
+
args: [
|
|
258
|
+
{
|
|
259
|
+
name: 'where',
|
|
260
|
+
type: `${model.name}WhereUnique`,
|
|
261
|
+
nonNull: true,
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
});
|
|
265
265
|
}
|
|
266
|
+
}
|
|
266
267
|
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
]),
|
|
268
|
+
return mutations;
|
|
269
|
+
}),
|
|
270
|
+
...objects.filter((model) => model.name === 'Mutation').flatMap((model) => model.fields),
|
|
271
271
|
];
|
|
272
|
+
|
|
273
|
+
if (mutations.length) {
|
|
274
|
+
definitions.push(object('Mutation', mutations));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return definitions;
|
|
272
278
|
};
|
|
273
279
|
|
|
274
280
|
export const generate = (models: Models) => document(generateDefinitions(models));
|