zenstack 0.1.0 → 0.1.2
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/out/cli/index.js +4 -51
- package/out/cli/index.js.map +1 -1
- package/out/cli/package.template.json +10 -0
- package/out/cli/tsconfig.template.json +17 -0
- package/out/generator/constants.js +6 -0
- package/out/generator/constants.js.map +1 -0
- package/out/generator/index.js +76 -0
- package/out/generator/index.js.map +1 -0
- package/out/generator/next-auth/index.js +3 -3
- package/out/generator/package.template.json +9 -0
- package/out/generator/prisma/expression-writer.js +287 -0
- package/out/generator/prisma/expression-writer.js.map +1 -0
- package/out/generator/prisma/index.js +8 -182
- package/out/generator/prisma/index.js.map +1 -1
- package/out/generator/prisma/plain-expression-builder.js +69 -0
- package/out/generator/prisma/plain-expression-builder.js.map +1 -0
- package/out/generator/prisma/prisma-builder.js +1 -1
- package/out/generator/prisma/prisma-builder.js.map +1 -1
- package/out/generator/prisma/query-gard-generator.js +159 -0
- package/out/generator/prisma/query-gard-generator.js.map +1 -0
- package/out/generator/prisma/schema-generator.js +202 -0
- package/out/generator/prisma/schema-generator.js.map +1 -0
- package/out/generator/query-guard/index.js +2 -0
- package/out/generator/query-guard/index.js.map +1 -0
- package/out/generator/react-hooks/index.js +1 -1
- package/out/generator/react-hooks/index.js.map +1 -1
- package/out/generator/server/data/expression-writer.js +42 -36
- package/out/generator/server/data/expression-writer.js.map +1 -1
- package/out/generator/server/data/plain-expression-builder.js +18 -2
- package/out/generator/server/data/plain-expression-builder.js.map +1 -1
- package/out/generator/service/index.js +51 -1
- package/out/generator/service/index.js.map +1 -1
- package/out/generator/tsconfig.template.json +17 -0
- package/out/utils/indent-string.js +3 -19
- package/out/utils/indent-string.js.map +1 -1
- package/package.json +7 -4
- package/src/cli/index.ts +5 -33
- package/src/generator/constants.ts +2 -0
- package/src/generator/index.ts +59 -0
- package/src/generator/next-auth/index.ts +3 -3
- package/src/generator/package.template.json +9 -0
- package/src/generator/{server/data → prisma}/expression-writer.ts +65 -63
- package/src/generator/prisma/index.ts +10 -309
- package/src/generator/{server/data → prisma}/plain-expression-builder.ts +22 -3
- package/src/generator/prisma/prisma-builder.ts +1 -1
- package/src/generator/prisma/query-gard-generator.ts +208 -0
- package/src/generator/prisma/schema-generator.ts +295 -0
- package/src/generator/react-hooks/index.ts +2 -4
- package/src/generator/service/index.ts +54 -1
- package/src/generator/tsconfig.template.json +17 -0
- package/src/utils/indent-string.ts +3 -38
- package/src/generator/server/data/data-generator.ts +0 -483
- package/src/generator/server/function/function-generator.ts +0 -32
- package/src/generator/server/index.ts +0 -57
- package/src/generator/server/server-code-generator.ts +0 -6
|
@@ -1,483 +0,0 @@
|
|
|
1
|
-
import { Context, GeneratorError } from '../../types';
|
|
2
|
-
import {
|
|
3
|
-
CodeBlockWriter,
|
|
4
|
-
OptionalKind,
|
|
5
|
-
ParameterDeclarationStructure,
|
|
6
|
-
Project,
|
|
7
|
-
SourceFile,
|
|
8
|
-
VariableDeclarationKind,
|
|
9
|
-
} from 'ts-morph';
|
|
10
|
-
import {
|
|
11
|
-
DataModel,
|
|
12
|
-
Expression,
|
|
13
|
-
isInvocationExpr,
|
|
14
|
-
isLiteralExpr,
|
|
15
|
-
} from '@lang/generated/ast';
|
|
16
|
-
import * as path from 'path';
|
|
17
|
-
import { camelCase, paramCase } from 'change-case';
|
|
18
|
-
import { extractDataModelsWithAllowRules } from '../../utils';
|
|
19
|
-
import { ServerCodeGenerator } from '../server-code-generator';
|
|
20
|
-
import ExpressionWriter from './expression-writer';
|
|
21
|
-
import { streamAllContents } from 'langium';
|
|
22
|
-
import colors from 'colors';
|
|
23
|
-
|
|
24
|
-
type ServerOperation = 'get' | 'create' | 'find' | 'update' | 'del';
|
|
25
|
-
type PolicyAction = 'create' | 'read' | 'update' | 'delete';
|
|
26
|
-
|
|
27
|
-
export default class DataServerGenerator implements ServerCodeGenerator {
|
|
28
|
-
generate(project: Project, context: Context): void {
|
|
29
|
-
const models = extractDataModelsWithAllowRules(context.schema);
|
|
30
|
-
this.generateIndex(models, project, context);
|
|
31
|
-
this.generateUtils(project, context);
|
|
32
|
-
models.forEach((model) =>
|
|
33
|
-
this.generateForModel(model, project, context)
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
console.log(colors.blue(' ✔️ Server-side CRUD generated'));
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
//#region Index & Utils
|
|
40
|
-
|
|
41
|
-
private generateIndex(
|
|
42
|
-
models: DataModel[],
|
|
43
|
-
project: Project,
|
|
44
|
-
context: Context
|
|
45
|
-
) {
|
|
46
|
-
const content = `
|
|
47
|
-
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
48
|
-
import { RequestHandlerOptions } from '..';
|
|
49
|
-
${models.map((model) => this.writeModelImport(model)).join('\n')}
|
|
50
|
-
|
|
51
|
-
export default async function (
|
|
52
|
-
req: NextApiRequest,
|
|
53
|
-
res: NextApiResponse,
|
|
54
|
-
path: string[],
|
|
55
|
-
options: RequestHandlerOptions
|
|
56
|
-
) {
|
|
57
|
-
const [type, ...rest] = path;
|
|
58
|
-
switch (type) {
|
|
59
|
-
${models
|
|
60
|
-
.map((model) => this.writeModelEntrance(model))
|
|
61
|
-
.join('\n')}
|
|
62
|
-
default:
|
|
63
|
-
res.status(404).json({ error: 'Unknown type: ' + type });
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
`;
|
|
67
|
-
const sf = project.createSourceFile(
|
|
68
|
-
path.join(context.outDir, 'server/data/index.ts'),
|
|
69
|
-
content,
|
|
70
|
-
{ overwrite: true }
|
|
71
|
-
);
|
|
72
|
-
sf.formatText();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
private generateUtils(project: Project, context: Context) {
|
|
76
|
-
const content = `
|
|
77
|
-
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
78
|
-
import { RequestHandlerOptions } from '..';
|
|
79
|
-
|
|
80
|
-
export async function getUser(
|
|
81
|
-
req: NextApiRequest,
|
|
82
|
-
res: NextApiResponse,
|
|
83
|
-
options: RequestHandlerOptions
|
|
84
|
-
) {
|
|
85
|
-
return await options.getServerUser(req, res);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function unauthorized(res: NextApiResponse) {
|
|
89
|
-
res.status(403).json({ message: 'Unauthorized' });
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
export function notFound(res: NextApiResponse) {
|
|
93
|
-
res.status(404).json({ message: 'Entity not found' });
|
|
94
|
-
}
|
|
95
|
-
`;
|
|
96
|
-
const sf = project.createSourceFile(
|
|
97
|
-
path.join(context.outDir, 'server/data/_utils.ts'),
|
|
98
|
-
content,
|
|
99
|
-
{ overwrite: true }
|
|
100
|
-
);
|
|
101
|
-
sf.formatText();
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
private writeModelImport(model: DataModel) {
|
|
105
|
-
return `import ${camelCase(model.name)}Handler from './${paramCase(
|
|
106
|
-
model.name
|
|
107
|
-
)}';`;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
private writeModelEntrance(model: DataModel) {
|
|
111
|
-
return `
|
|
112
|
-
case '${camelCase(model.name)}':
|
|
113
|
-
return ${camelCase(model.name)}Handler(req, res, rest, options);
|
|
114
|
-
`;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
//#endregion
|
|
118
|
-
|
|
119
|
-
//#region Per-Model
|
|
120
|
-
|
|
121
|
-
private generateForModel(
|
|
122
|
-
model: DataModel,
|
|
123
|
-
project: Project,
|
|
124
|
-
context: Context
|
|
125
|
-
) {
|
|
126
|
-
const content = `
|
|
127
|
-
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
128
|
-
import type { Prisma as P } from '@zenstack/.prisma';
|
|
129
|
-
import { RequestHandlerOptions } from '..';
|
|
130
|
-
import service from '@zenstack/service';
|
|
131
|
-
import { getUser, notFound } from './_utils';
|
|
132
|
-
|
|
133
|
-
export default async function (
|
|
134
|
-
req: NextApiRequest,
|
|
135
|
-
res: NextApiResponse,
|
|
136
|
-
path: string[],
|
|
137
|
-
options: RequestHandlerOptions
|
|
138
|
-
) {
|
|
139
|
-
switch (req.method) {
|
|
140
|
-
case 'GET':
|
|
141
|
-
if (path.length > 0) {
|
|
142
|
-
return get(req, res, path[0], options);
|
|
143
|
-
} else {
|
|
144
|
-
return find(req, res, options);
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
case 'POST':
|
|
148
|
-
return create(req, res, options);
|
|
149
|
-
|
|
150
|
-
case 'PUT':
|
|
151
|
-
return update(req, res, path[0], options);
|
|
152
|
-
|
|
153
|
-
case 'DELETE':
|
|
154
|
-
return del(req, res, path[0], options);
|
|
155
|
-
|
|
156
|
-
default:
|
|
157
|
-
throw new Error('Unsupported HTTP method: ' + req.method);
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
`;
|
|
161
|
-
const sf = project.createSourceFile(
|
|
162
|
-
path.join(
|
|
163
|
-
context.outDir,
|
|
164
|
-
`server/data/${paramCase(model.name)}.ts`
|
|
165
|
-
),
|
|
166
|
-
content,
|
|
167
|
-
{ overwrite: true }
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
this.generateFind(sf, model);
|
|
171
|
-
this.generateGet(sf, model);
|
|
172
|
-
this.generateCreate(sf, model);
|
|
173
|
-
this.generateUpdate(sf, model);
|
|
174
|
-
this.generateDel(sf, model);
|
|
175
|
-
|
|
176
|
-
sf.formatText();
|
|
177
|
-
sf.saveSync();
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
private generateServeFunction(
|
|
181
|
-
sourceFile: SourceFile,
|
|
182
|
-
model: DataModel,
|
|
183
|
-
operation: ServerOperation
|
|
184
|
-
) {
|
|
185
|
-
const parameters: OptionalKind<ParameterDeclarationStructure>[] = [];
|
|
186
|
-
|
|
187
|
-
parameters.push({
|
|
188
|
-
name: 'req',
|
|
189
|
-
type: 'NextApiRequest',
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
parameters.push({
|
|
193
|
-
name: 'res',
|
|
194
|
-
type: 'NextApiResponse',
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
if (
|
|
198
|
-
operation === 'get' ||
|
|
199
|
-
operation === 'update' ||
|
|
200
|
-
operation === 'del'
|
|
201
|
-
) {
|
|
202
|
-
// an extra "id" parameter
|
|
203
|
-
parameters.push({
|
|
204
|
-
name: 'id',
|
|
205
|
-
type: 'string',
|
|
206
|
-
});
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
parameters.push({
|
|
210
|
-
name: 'options',
|
|
211
|
-
type: 'RequestHandlerOptions',
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
const func = sourceFile
|
|
215
|
-
.addFunction({
|
|
216
|
-
name: operation,
|
|
217
|
-
isAsync: true,
|
|
218
|
-
parameters,
|
|
219
|
-
})
|
|
220
|
-
.addBody();
|
|
221
|
-
|
|
222
|
-
if (this.modelUsesAuth(model)) {
|
|
223
|
-
func.addStatements([
|
|
224
|
-
`const user = await getUser(req, res, options);`,
|
|
225
|
-
]);
|
|
226
|
-
}
|
|
227
|
-
return func;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
private modelUsesAuth(model: DataModel) {
|
|
231
|
-
return !!streamAllContents(model).find(
|
|
232
|
-
(node) =>
|
|
233
|
-
isInvocationExpr(node) && node.function.ref?.name === 'auth'
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
//#region Find & Get
|
|
238
|
-
|
|
239
|
-
private generateFind(sourceFile: SourceFile, model: DataModel) {
|
|
240
|
-
const func = this.generateServeFunction(sourceFile, model, 'find');
|
|
241
|
-
|
|
242
|
-
func.addStatements([
|
|
243
|
-
`const query: P.${model.name}FindManyArgs = req.query.q? (JSON.parse(req.query.q as string)): {};`,
|
|
244
|
-
]);
|
|
245
|
-
|
|
246
|
-
func.addVariableStatement({
|
|
247
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
248
|
-
declarations: [
|
|
249
|
-
{
|
|
250
|
-
name: 'args',
|
|
251
|
-
type: `P.${model.name}FindManyArgs`,
|
|
252
|
-
initializer: (writer) => {
|
|
253
|
-
writer.block(() => {
|
|
254
|
-
writer.writeLine('...query,');
|
|
255
|
-
writer.write('where:');
|
|
256
|
-
writer.block(() => {
|
|
257
|
-
writer.write('AND: [');
|
|
258
|
-
writer.write('{ ...query.where },');
|
|
259
|
-
this.writeFindArgs(writer, model, 'read');
|
|
260
|
-
writer.write(']');
|
|
261
|
-
});
|
|
262
|
-
});
|
|
263
|
-
},
|
|
264
|
-
},
|
|
265
|
-
],
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
func.addStatements([
|
|
269
|
-
`res.status(200).send(await service.db.${camelCase(
|
|
270
|
-
model.name
|
|
271
|
-
)}.findMany(args));`,
|
|
272
|
-
]);
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
private generateGet(sourceFile: SourceFile, model: DataModel) {
|
|
276
|
-
const func = this.generateServeFunction(sourceFile, model, 'get');
|
|
277
|
-
|
|
278
|
-
func.addStatements([
|
|
279
|
-
`const query: P.${model.name}FindFirstArgs = req.query.q? (JSON.parse(req.query.q as string)): {};`,
|
|
280
|
-
]);
|
|
281
|
-
|
|
282
|
-
func.addVariableStatement({
|
|
283
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
284
|
-
declarations: [
|
|
285
|
-
{
|
|
286
|
-
name: 'args',
|
|
287
|
-
type: `P.${model.name}FindFirstArgs`,
|
|
288
|
-
initializer: (writer) => {
|
|
289
|
-
writer.block(() => {
|
|
290
|
-
writer.writeLine('...query,');
|
|
291
|
-
writer.write('where:');
|
|
292
|
-
writer.block(() => {
|
|
293
|
-
writer.write('AND: [');
|
|
294
|
-
writer.write('{ id },');
|
|
295
|
-
this.writeFindArgs(writer, model, 'read');
|
|
296
|
-
writer.write(']');
|
|
297
|
-
});
|
|
298
|
-
});
|
|
299
|
-
},
|
|
300
|
-
},
|
|
301
|
-
],
|
|
302
|
-
});
|
|
303
|
-
|
|
304
|
-
func.addStatements([
|
|
305
|
-
`
|
|
306
|
-
const r = await service.db.${camelCase(model.name)}.findFirst(args);
|
|
307
|
-
if (!r) {
|
|
308
|
-
notFound(res);
|
|
309
|
-
} else {
|
|
310
|
-
res.status(200).send(r);
|
|
311
|
-
}
|
|
312
|
-
`,
|
|
313
|
-
]);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
private writeFindArgs(
|
|
317
|
-
writer: CodeBlockWriter,
|
|
318
|
-
model: DataModel,
|
|
319
|
-
action: PolicyAction
|
|
320
|
-
) {
|
|
321
|
-
writer.block(() => {
|
|
322
|
-
writer.writeLine('AND: [');
|
|
323
|
-
this.writeDenyRules(writer, model, action);
|
|
324
|
-
this.writeAllowRules(writer, model, action);
|
|
325
|
-
writer.writeLine(']');
|
|
326
|
-
});
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
//#endregion
|
|
330
|
-
|
|
331
|
-
//#region Create
|
|
332
|
-
|
|
333
|
-
private generateCreate(sourceFile: SourceFile, model: DataModel) {
|
|
334
|
-
const func = this.generateServeFunction(sourceFile, model, 'create');
|
|
335
|
-
|
|
336
|
-
func.addVariableStatement({
|
|
337
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
338
|
-
declarations: [
|
|
339
|
-
{
|
|
340
|
-
name: 'args',
|
|
341
|
-
type: `P.${model.name}CreateArgs`,
|
|
342
|
-
initializer: 'req.body',
|
|
343
|
-
},
|
|
344
|
-
],
|
|
345
|
-
});
|
|
346
|
-
|
|
347
|
-
// TODO: policy
|
|
348
|
-
|
|
349
|
-
func.addStatements([
|
|
350
|
-
`
|
|
351
|
-
const r = await service.db.${camelCase(model.name)}.create(args);
|
|
352
|
-
res.status(200).send(r);
|
|
353
|
-
`,
|
|
354
|
-
]);
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
//#endregion
|
|
358
|
-
|
|
359
|
-
//#region Update
|
|
360
|
-
|
|
361
|
-
private generateUpdate(sourceFile: SourceFile, model: DataModel) {
|
|
362
|
-
const func = this.generateServeFunction(sourceFile, model, 'update');
|
|
363
|
-
|
|
364
|
-
func.addVariableStatement({
|
|
365
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
366
|
-
declarations: [
|
|
367
|
-
{
|
|
368
|
-
name: 'body',
|
|
369
|
-
type: `P.${model.name}UpdateArgs`,
|
|
370
|
-
initializer: 'req.body',
|
|
371
|
-
},
|
|
372
|
-
],
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
// TODO: policy
|
|
376
|
-
|
|
377
|
-
func.addStatements([
|
|
378
|
-
`
|
|
379
|
-
const r = await service.db.${camelCase(model.name)}.update({
|
|
380
|
-
...body,
|
|
381
|
-
where: { id }
|
|
382
|
-
});
|
|
383
|
-
res.status(200).send(r);
|
|
384
|
-
`,
|
|
385
|
-
]);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
//#endregion
|
|
389
|
-
|
|
390
|
-
//#region Delete
|
|
391
|
-
|
|
392
|
-
private generateDel(sourceFile: SourceFile, model: DataModel) {
|
|
393
|
-
const func = this.generateServeFunction(sourceFile, model, 'del');
|
|
394
|
-
|
|
395
|
-
func.addStatements([
|
|
396
|
-
`const args: P.${model.name}DeleteArgs = req.query.q? (JSON.parse(req.query.q as string)): {};`,
|
|
397
|
-
]);
|
|
398
|
-
|
|
399
|
-
// TODO: policy
|
|
400
|
-
|
|
401
|
-
func.addStatements([
|
|
402
|
-
`
|
|
403
|
-
const r = await service.db.${camelCase(model.name)}.delete({
|
|
404
|
-
...args,
|
|
405
|
-
where: { id }
|
|
406
|
-
});
|
|
407
|
-
res.status(200).send(r);
|
|
408
|
-
`,
|
|
409
|
-
]);
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
//#endregion
|
|
413
|
-
|
|
414
|
-
//#endregion
|
|
415
|
-
|
|
416
|
-
//#region Policy
|
|
417
|
-
|
|
418
|
-
private ruleSpecCovers(ruleSpec: Expression, action: string) {
|
|
419
|
-
if (!isLiteralExpr(ruleSpec) || typeof ruleSpec.value !== 'string') {
|
|
420
|
-
throw new GeneratorError(`Rule spec must be a string literal`);
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
const specs = ruleSpec.value.split(',').map((s) => s.trim());
|
|
424
|
-
return specs.includes('all') || specs.includes(action);
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
private writeDenyRules(
|
|
428
|
-
writer: CodeBlockWriter,
|
|
429
|
-
model: DataModel,
|
|
430
|
-
action: PolicyAction
|
|
431
|
-
) {
|
|
432
|
-
const attrs = model.attributes.filter(
|
|
433
|
-
(attr) =>
|
|
434
|
-
attr.args.length > 0 &&
|
|
435
|
-
attr.decl.ref?.name === 'deny' &&
|
|
436
|
-
attr.args.length > 1 &&
|
|
437
|
-
this.ruleSpecCovers(attr.args[0].value, action)
|
|
438
|
-
);
|
|
439
|
-
attrs.forEach((attr) =>
|
|
440
|
-
this.writeDenyRule(writer, model, attr.args[1].value)
|
|
441
|
-
);
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
private writeDenyRule(
|
|
445
|
-
writer: CodeBlockWriter,
|
|
446
|
-
model: DataModel,
|
|
447
|
-
rule: Expression
|
|
448
|
-
) {
|
|
449
|
-
writer.block(() => {
|
|
450
|
-
writer.writeLine('NOT: ');
|
|
451
|
-
new ExpressionWriter(writer).write(rule);
|
|
452
|
-
});
|
|
453
|
-
writer.write(',');
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
private writeAllowRules(
|
|
457
|
-
writer: CodeBlockWriter,
|
|
458
|
-
model: DataModel,
|
|
459
|
-
action: PolicyAction
|
|
460
|
-
) {
|
|
461
|
-
const attrs = model.attributes.filter(
|
|
462
|
-
(attr) =>
|
|
463
|
-
attr.args.length > 0 &&
|
|
464
|
-
attr.decl.ref?.name === 'allow' &&
|
|
465
|
-
attr.args.length > 1 &&
|
|
466
|
-
this.ruleSpecCovers(attr.args[0].value, action)
|
|
467
|
-
);
|
|
468
|
-
attrs.forEach((attr) =>
|
|
469
|
-
this.writeAllowRule(writer, model, attr.args[1].value)
|
|
470
|
-
);
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
private writeAllowRule(
|
|
474
|
-
writer: CodeBlockWriter,
|
|
475
|
-
model: DataModel,
|
|
476
|
-
rule: Expression
|
|
477
|
-
) {
|
|
478
|
-
new ExpressionWriter(writer).write(rule);
|
|
479
|
-
writer.write(',');
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
//#endregion
|
|
483
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { Context } from '../../types';
|
|
2
|
-
import { Project } from 'ts-morph';
|
|
3
|
-
import * as path from 'path';
|
|
4
|
-
import { ServerCodeGenerator } from '../server-code-generator';
|
|
5
|
-
|
|
6
|
-
export default class FunctionServerGenerator implements ServerCodeGenerator {
|
|
7
|
-
generate(project: Project, context: Context) {
|
|
8
|
-
this.generateIndex(project, context);
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
private generateIndex(project: Project, context: Context) {
|
|
12
|
-
const content = `
|
|
13
|
-
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
14
|
-
import { RequestHandlerOptions } from '..';
|
|
15
|
-
|
|
16
|
-
export default async function (
|
|
17
|
-
req: NextApiRequest,
|
|
18
|
-
res: NextApiResponse,
|
|
19
|
-
path: string[],
|
|
20
|
-
options: RequestHandlerOptions
|
|
21
|
-
) {
|
|
22
|
-
throw new Error('Not implemented');
|
|
23
|
-
}
|
|
24
|
-
`;
|
|
25
|
-
const sf = project.createSourceFile(
|
|
26
|
-
path.join(context.outDir, 'server/function/index.ts'),
|
|
27
|
-
content,
|
|
28
|
-
{ overwrite: true }
|
|
29
|
-
);
|
|
30
|
-
sf.formatText();
|
|
31
|
-
}
|
|
32
|
-
}
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
import { Project } from 'ts-morph';
|
|
2
|
-
import { Context, Generator } from '../types';
|
|
3
|
-
import * as path from 'path';
|
|
4
|
-
import DataServerGenerator from './data/data-generator';
|
|
5
|
-
import FunctionServerGenerator from './function/function-generator';
|
|
6
|
-
|
|
7
|
-
export default class ServerGenerator implements Generator {
|
|
8
|
-
async generate(context: Context) {
|
|
9
|
-
const project = new Project();
|
|
10
|
-
|
|
11
|
-
this.generateIndex(project, context);
|
|
12
|
-
|
|
13
|
-
new DataServerGenerator().generate(project, context);
|
|
14
|
-
new FunctionServerGenerator().generate(project, context);
|
|
15
|
-
|
|
16
|
-
await project.save();
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
generateIndex(project: Project, context: Context) {
|
|
20
|
-
const content = `
|
|
21
|
-
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
22
|
-
import dataHandler from './data';
|
|
23
|
-
import functionHandler from './function';
|
|
24
|
-
|
|
25
|
-
export type AuthUser = { id: string } & Record<string, any>;
|
|
26
|
-
|
|
27
|
-
export type RequestHandlerOptions = {
|
|
28
|
-
getServerUser: (
|
|
29
|
-
req: NextApiRequest,
|
|
30
|
-
res: NextApiResponse
|
|
31
|
-
) => Promise<AuthUser | undefined>;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
export function RequestHandler(options: RequestHandlerOptions) {
|
|
35
|
-
return async (req: NextApiRequest, res: NextApiResponse) => {
|
|
36
|
-
const [route, ...rest] = req.query.path as string[];
|
|
37
|
-
switch (route) {
|
|
38
|
-
case 'data':
|
|
39
|
-
return dataHandler(req, res, rest, options);
|
|
40
|
-
|
|
41
|
-
case 'function':
|
|
42
|
-
return functionHandler(req, res, rest, options);
|
|
43
|
-
|
|
44
|
-
default:
|
|
45
|
-
res.status(404).json({ error: 'Unknown route: ' + route });
|
|
46
|
-
}
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
`;
|
|
50
|
-
const sf = project.createSourceFile(
|
|
51
|
-
path.join(context.outDir, 'server/index.ts'),
|
|
52
|
-
content,
|
|
53
|
-
{ overwrite: true }
|
|
54
|
-
);
|
|
55
|
-
sf.formatText();
|
|
56
|
-
}
|
|
57
|
-
}
|