zenstack 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/out/generator/index.js +1 -1
- package/out/generator/index.js.map +1 -1
- package/package.json +2 -3
- package/src/cli/cli-util.ts +0 -80
- package/src/cli/index.ts +0 -52
- package/src/extension.ts +0 -76
- package/src/generator/constants.ts +0 -2
- package/src/generator/index.ts +0 -74
- package/src/generator/next-auth/index.ts +0 -183
- package/src/generator/package.template.json +0 -9
- package/src/generator/prisma/expression-writer.ts +0 -352
- package/src/generator/prisma/index.ts +0 -24
- package/src/generator/prisma/plain-expression-builder.ts +0 -91
- package/src/generator/prisma/prisma-builder.ts +0 -366
- package/src/generator/prisma/query-gard-generator.ts +0 -208
- package/src/generator/prisma/schema-generator.ts +0 -295
- package/src/generator/react-hooks/index.ts +0 -265
- package/src/generator/service/index.ts +0 -96
- package/src/generator/tsconfig.template.json +0 -17
- package/src/generator/types.ts +0 -16
- package/src/generator/utils.ts +0 -9
- package/src/language-server/generated/ast.ts +0 -603
- package/src/language-server/generated/grammar.ts +0 -2190
- package/src/language-server/generated/module.ts +0 -24
- package/src/language-server/main.ts +0 -12
- package/src/language-server/stdlib.zmodel +0 -21
- package/src/language-server/types.ts +0 -9
- package/src/language-server/zmodel-index.ts +0 -33
- package/src/language-server/zmodel-linker.ts +0 -407
- package/src/language-server/zmodel-module.ts +0 -90
- package/src/language-server/zmodel-scope.ts +0 -21
- package/src/language-server/zmodel-validator.ts +0 -35
- package/src/language-server/zmodel.langium +0 -186
- package/src/utils/indent-string.ts +0 -6
|
@@ -1,366 +0,0 @@
|
|
|
1
|
-
import indentString from '../../utils/indent-string';
|
|
2
|
-
|
|
3
|
-
export class PrismaModel {
|
|
4
|
-
private datasources: DataSource[] = [];
|
|
5
|
-
private generators: Generator[] = [];
|
|
6
|
-
private models: Model[] = [];
|
|
7
|
-
private enums: Enum[] = [];
|
|
8
|
-
|
|
9
|
-
addDataSource(
|
|
10
|
-
name: string,
|
|
11
|
-
provider: string,
|
|
12
|
-
url: DataSourceUrl,
|
|
13
|
-
shadowDatabaseUrl?: DataSourceUrl
|
|
14
|
-
) {
|
|
15
|
-
const ds = new DataSource(name, provider, url, shadowDatabaseUrl);
|
|
16
|
-
this.datasources.push(ds);
|
|
17
|
-
return ds;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
addGenerator(
|
|
21
|
-
name: string,
|
|
22
|
-
provider: string,
|
|
23
|
-
output: string,
|
|
24
|
-
previewFeatures?: string[]
|
|
25
|
-
) {
|
|
26
|
-
const generator = new Generator(
|
|
27
|
-
name,
|
|
28
|
-
provider,
|
|
29
|
-
output,
|
|
30
|
-
previewFeatures
|
|
31
|
-
);
|
|
32
|
-
this.generators.push(generator);
|
|
33
|
-
return generator;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
addModel(name: string) {
|
|
37
|
-
const model = new Model(name);
|
|
38
|
-
this.models.push(model);
|
|
39
|
-
return model;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
addEnum(name: string, fields: string[]) {
|
|
43
|
-
const e = new Enum(name, fields);
|
|
44
|
-
this.enums.push(e);
|
|
45
|
-
return e;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
toString() {
|
|
49
|
-
return [
|
|
50
|
-
...this.datasources,
|
|
51
|
-
...this.generators,
|
|
52
|
-
...this.enums,
|
|
53
|
-
...this.models,
|
|
54
|
-
]
|
|
55
|
-
.map((d) => d.toString())
|
|
56
|
-
.join('\n\n');
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export class DataSource {
|
|
61
|
-
constructor(
|
|
62
|
-
public name: string,
|
|
63
|
-
public provider: string,
|
|
64
|
-
public url: DataSourceUrl,
|
|
65
|
-
public shadowDatabaseUrl?: DataSourceUrl
|
|
66
|
-
) {}
|
|
67
|
-
|
|
68
|
-
toString() {
|
|
69
|
-
return (
|
|
70
|
-
`datasource ${this.name} {\n` +
|
|
71
|
-
indentString(`provider="${this.provider}"\n`) +
|
|
72
|
-
indentString(`url=${this.url}\n`) +
|
|
73
|
-
(this.shadowDatabaseUrl
|
|
74
|
-
? indentString(`shadowDatabaseurl=${this.shadowDatabaseUrl}\n`)
|
|
75
|
-
: '') +
|
|
76
|
-
`}`
|
|
77
|
-
);
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export class DataSourceUrl {
|
|
82
|
-
constructor(public value: string, public isEnv: boolean) {}
|
|
83
|
-
|
|
84
|
-
toString() {
|
|
85
|
-
return this.isEnv ? `env("${this.value}")` : `"${this.value}"`;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export class Generator {
|
|
90
|
-
constructor(
|
|
91
|
-
public name: string,
|
|
92
|
-
public provider: string,
|
|
93
|
-
public output: string,
|
|
94
|
-
public previewFeatures?: string[]
|
|
95
|
-
) {}
|
|
96
|
-
|
|
97
|
-
toString() {
|
|
98
|
-
return (
|
|
99
|
-
`generator ${this.name} {\n` +
|
|
100
|
-
indentString(`provider = "${this.provider}"\n`) +
|
|
101
|
-
indentString(`output = "${this.output}"\n`) +
|
|
102
|
-
(this.previewFeatures
|
|
103
|
-
? indentString(
|
|
104
|
-
`previewFeatures = [${this.previewFeatures
|
|
105
|
-
?.map((f) => '"' + f + '"')
|
|
106
|
-
.join(', ')}]\n`
|
|
107
|
-
)
|
|
108
|
-
: '') +
|
|
109
|
-
`}`
|
|
110
|
-
);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export class Model {
|
|
115
|
-
public fields: ModelField[] = [];
|
|
116
|
-
public attributes: ModelAttribute[] = [];
|
|
117
|
-
constructor(public name: string) {}
|
|
118
|
-
|
|
119
|
-
addField(
|
|
120
|
-
name: string,
|
|
121
|
-
type: ModelFieldType | string,
|
|
122
|
-
attributes: FieldAttribute[] = []
|
|
123
|
-
) {
|
|
124
|
-
const field = new ModelField(name, type, attributes);
|
|
125
|
-
this.fields.push(field);
|
|
126
|
-
return field;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
addAttribute(name: string, args: AttributeArg[] = []) {
|
|
130
|
-
const attr = new ModelAttribute(name, args);
|
|
131
|
-
this.attributes.push(attr);
|
|
132
|
-
return attr;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
toString() {
|
|
136
|
-
return (
|
|
137
|
-
`model ${this.name} {\n` +
|
|
138
|
-
indentString(
|
|
139
|
-
[...this.fields, ...this.attributes]
|
|
140
|
-
.map((d) => d.toString())
|
|
141
|
-
.join('\n')
|
|
142
|
-
) +
|
|
143
|
-
`\n}`
|
|
144
|
-
);
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export type ScalarTypes =
|
|
149
|
-
| 'String'
|
|
150
|
-
| 'Boolean'
|
|
151
|
-
| 'Int'
|
|
152
|
-
| 'BigInt'
|
|
153
|
-
| 'Float'
|
|
154
|
-
| 'Decimal'
|
|
155
|
-
| 'DateTime'
|
|
156
|
-
| 'Json'
|
|
157
|
-
| 'Bytes'
|
|
158
|
-
| 'Unsupported';
|
|
159
|
-
|
|
160
|
-
export class ModelFieldType {
|
|
161
|
-
constructor(
|
|
162
|
-
public type: ScalarTypes | string,
|
|
163
|
-
public array?: boolean,
|
|
164
|
-
public optional?: boolean
|
|
165
|
-
) {}
|
|
166
|
-
|
|
167
|
-
toString() {
|
|
168
|
-
return `${this.type}${this.array ? '[]' : ''}${
|
|
169
|
-
this.optional ? '?' : ''
|
|
170
|
-
}`;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
export class ModelField {
|
|
175
|
-
constructor(
|
|
176
|
-
public name: string,
|
|
177
|
-
public type: ModelFieldType | string,
|
|
178
|
-
public attributes: FieldAttribute[] = []
|
|
179
|
-
) {}
|
|
180
|
-
|
|
181
|
-
addAttribute(name: string, args: AttributeArg[] = []) {
|
|
182
|
-
const attr = new FieldAttribute(name, args);
|
|
183
|
-
this.attributes.push(attr);
|
|
184
|
-
return attr;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
toString() {
|
|
188
|
-
return (
|
|
189
|
-
`${this.name} ${this.type}` +
|
|
190
|
-
(this.attributes.length > 0
|
|
191
|
-
? ' ' + this.attributes.map((a) => a.toString()).join(' ')
|
|
192
|
-
: '')
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
export class FieldAttribute {
|
|
198
|
-
constructor(public name: string, public args: AttributeArg[] = []) {}
|
|
199
|
-
|
|
200
|
-
toString() {
|
|
201
|
-
return (
|
|
202
|
-
`@${this.name}(` +
|
|
203
|
-
this.args.map((a) => a.toString()).join(', ') +
|
|
204
|
-
`)`
|
|
205
|
-
);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
export class ModelAttribute {
|
|
210
|
-
constructor(public name: string, public args: AttributeArg[] = []) {}
|
|
211
|
-
|
|
212
|
-
toString() {
|
|
213
|
-
return (
|
|
214
|
-
`@@${this.name}(` +
|
|
215
|
-
this.args.map((a) => a.toString()).join(', ') +
|
|
216
|
-
`)`
|
|
217
|
-
);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
export class AttributeArg {
|
|
222
|
-
constructor(
|
|
223
|
-
public name: string | undefined,
|
|
224
|
-
public value: AttributeArgValue
|
|
225
|
-
) {}
|
|
226
|
-
|
|
227
|
-
toString() {
|
|
228
|
-
return this.name
|
|
229
|
-
? `${this.name}: ${this.value}`
|
|
230
|
-
: this.value.toString();
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
export class AttributeArgValue {
|
|
235
|
-
constructor(
|
|
236
|
-
public type:
|
|
237
|
-
| 'String'
|
|
238
|
-
| 'FieldReference'
|
|
239
|
-
| 'Number'
|
|
240
|
-
| 'Boolean'
|
|
241
|
-
| 'Array'
|
|
242
|
-
| 'FunctionCall',
|
|
243
|
-
public value:
|
|
244
|
-
| string
|
|
245
|
-
| number
|
|
246
|
-
| boolean
|
|
247
|
-
| FieldReference
|
|
248
|
-
| FunctionCall
|
|
249
|
-
| AttributeArgValue[]
|
|
250
|
-
) {
|
|
251
|
-
switch (type) {
|
|
252
|
-
case 'String':
|
|
253
|
-
if (typeof value !== 'string')
|
|
254
|
-
throw new Error('Value must be string');
|
|
255
|
-
break;
|
|
256
|
-
case 'Number':
|
|
257
|
-
if (typeof value !== 'number')
|
|
258
|
-
throw new Error('Value must be number');
|
|
259
|
-
break;
|
|
260
|
-
case 'Boolean':
|
|
261
|
-
if (typeof value !== 'boolean')
|
|
262
|
-
throw new Error('Value must be boolean');
|
|
263
|
-
break;
|
|
264
|
-
case 'Array':
|
|
265
|
-
if (!Array.isArray(value))
|
|
266
|
-
throw new Error('Value must be array');
|
|
267
|
-
break;
|
|
268
|
-
case 'FieldReference':
|
|
269
|
-
if (
|
|
270
|
-
typeof value !== 'string' &&
|
|
271
|
-
!(value instanceof FieldReference)
|
|
272
|
-
)
|
|
273
|
-
throw new Error('Value must be string or FieldReference');
|
|
274
|
-
break;
|
|
275
|
-
case 'FunctionCall':
|
|
276
|
-
if (!(value instanceof FunctionCall))
|
|
277
|
-
throw new Error('Value must be FunctionCall');
|
|
278
|
-
break;
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
toString(): string {
|
|
283
|
-
switch (this.type) {
|
|
284
|
-
case 'String':
|
|
285
|
-
return `"${this.value}"`;
|
|
286
|
-
case 'Number':
|
|
287
|
-
return this.value.toString();
|
|
288
|
-
case 'FieldReference': {
|
|
289
|
-
if (typeof this.value === 'string') {
|
|
290
|
-
return this.value;
|
|
291
|
-
} else {
|
|
292
|
-
const fr = this.value as FieldReference;
|
|
293
|
-
let r = fr.field;
|
|
294
|
-
if (fr.args.length > 0) {
|
|
295
|
-
r +=
|
|
296
|
-
'(' +
|
|
297
|
-
fr.args.map((a) => a.toString()).join(',') +
|
|
298
|
-
')';
|
|
299
|
-
}
|
|
300
|
-
return r;
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
case 'FunctionCall':
|
|
304
|
-
return this.value.toString();
|
|
305
|
-
case 'Boolean':
|
|
306
|
-
return this.value ? 'true' : 'false';
|
|
307
|
-
case 'Array':
|
|
308
|
-
return (
|
|
309
|
-
'[' +
|
|
310
|
-
(this.value as AttributeArgValue[])
|
|
311
|
-
.map((v) => v.toString())
|
|
312
|
-
.join(', ') +
|
|
313
|
-
']'
|
|
314
|
-
);
|
|
315
|
-
default:
|
|
316
|
-
throw new Error(`Unknown attribute value type ${this.type}`);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
export class FieldReference {
|
|
322
|
-
constructor(public field: string, public args: FieldReferenceArg[] = []) {}
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
export class FieldReferenceArg {
|
|
326
|
-
constructor(public name: 'sort', public value: 'Asc' | 'Desc') {}
|
|
327
|
-
|
|
328
|
-
toString() {
|
|
329
|
-
return `${this.name}: ${this.value}`;
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
export class FunctionCall {
|
|
334
|
-
constructor(public func: string, public args: FunctionCallArg[] = []) {}
|
|
335
|
-
|
|
336
|
-
toString() {
|
|
337
|
-
return (
|
|
338
|
-
`${this.func}` +
|
|
339
|
-
'(' +
|
|
340
|
-
this.args.map((a) => a.toString()).join(', ') +
|
|
341
|
-
')'
|
|
342
|
-
);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
export class FunctionCallArg {
|
|
347
|
-
constructor(public name: string | undefined, public value: any) {}
|
|
348
|
-
|
|
349
|
-
toString() {
|
|
350
|
-
return this.name ? `${this.name}: ${this.value}` : this.value;
|
|
351
|
-
}
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
export class Enum {
|
|
355
|
-
constructor(public name: string, public fields: EnumField[]) {}
|
|
356
|
-
|
|
357
|
-
toString() {
|
|
358
|
-
return (
|
|
359
|
-
`enum ${this.name} {\n` +
|
|
360
|
-
indentString(this.fields.join('\n')) +
|
|
361
|
-
'\n}'
|
|
362
|
-
);
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
type EnumField = String;
|
|
@@ -1,208 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DataModel,
|
|
3
|
-
isDataModel,
|
|
4
|
-
isEnum,
|
|
5
|
-
isLiteralExpr,
|
|
6
|
-
} from '@lang/generated/ast';
|
|
7
|
-
import { PolicyKind, PolicyOperationKind } from '@zenstackhq/internal';
|
|
8
|
-
import path from 'path';
|
|
9
|
-
import { Project, SourceFile, VariableDeclarationKind } from 'ts-morph';
|
|
10
|
-
import { GUARD_FIELD_NAME, RUNTIME_PACKAGE } from '../constants';
|
|
11
|
-
import { Context } from '../types';
|
|
12
|
-
import ExpressionWriter from './expression-writer';
|
|
13
|
-
|
|
14
|
-
export default class QueryGuardGenerator {
|
|
15
|
-
constructor(private readonly context: Context) {}
|
|
16
|
-
|
|
17
|
-
async generate() {
|
|
18
|
-
const project = new Project();
|
|
19
|
-
const sf = project.createSourceFile(
|
|
20
|
-
path.join(this.context.outDir, 'query/guard.ts'),
|
|
21
|
-
undefined,
|
|
22
|
-
{ overwrite: true }
|
|
23
|
-
);
|
|
24
|
-
|
|
25
|
-
sf.addImportDeclaration({
|
|
26
|
-
namedImports: [{ name: 'QueryContext' }],
|
|
27
|
-
moduleSpecifier: RUNTIME_PACKAGE,
|
|
28
|
-
isTypeOnly: true,
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
// import enums
|
|
32
|
-
for (const e of this.context.schema.declarations.filter((d) =>
|
|
33
|
-
isEnum(d)
|
|
34
|
-
)) {
|
|
35
|
-
sf.addImportDeclaration({
|
|
36
|
-
namedImports: [{ name: e.name }],
|
|
37
|
-
moduleSpecifier: '../.prisma',
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const models = this.context.schema.declarations.filter((d) =>
|
|
42
|
-
isDataModel(d)
|
|
43
|
-
) as DataModel[];
|
|
44
|
-
|
|
45
|
-
this.generateFieldMapping(models, sf);
|
|
46
|
-
|
|
47
|
-
models.forEach((model) => this.generateQueryGuardForModel(model, sf));
|
|
48
|
-
|
|
49
|
-
sf.formatText({});
|
|
50
|
-
await project.save();
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
private generateFieldMapping(models: DataModel[], sourceFile: SourceFile) {
|
|
54
|
-
const mapping = Object.fromEntries(
|
|
55
|
-
models.map((m) => [
|
|
56
|
-
m.name,
|
|
57
|
-
Object.fromEntries(
|
|
58
|
-
m.fields
|
|
59
|
-
.filter((f) => isDataModel(f.type.reference?.ref))
|
|
60
|
-
.map((f) => [
|
|
61
|
-
f.name,
|
|
62
|
-
{
|
|
63
|
-
type: f.type.reference!.ref!.name,
|
|
64
|
-
isArray: f.type.array,
|
|
65
|
-
},
|
|
66
|
-
])
|
|
67
|
-
),
|
|
68
|
-
])
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
sourceFile.addVariableStatement({
|
|
72
|
-
isExported: true,
|
|
73
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
74
|
-
declarations: [
|
|
75
|
-
{
|
|
76
|
-
name: '_fieldMapping',
|
|
77
|
-
initializer: JSON.stringify(mapping),
|
|
78
|
-
},
|
|
79
|
-
],
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
private getPolicyExpressions(
|
|
84
|
-
model: DataModel,
|
|
85
|
-
kind: PolicyKind,
|
|
86
|
-
operation: PolicyOperationKind
|
|
87
|
-
) {
|
|
88
|
-
const attrs = model.attributes.filter(
|
|
89
|
-
(attr) => attr.decl.ref?.name === kind
|
|
90
|
-
);
|
|
91
|
-
return attrs
|
|
92
|
-
.filter((attr) => {
|
|
93
|
-
if (
|
|
94
|
-
!isLiteralExpr(attr.args[0].value) ||
|
|
95
|
-
typeof attr.args[0].value.value !== 'string'
|
|
96
|
-
) {
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
const ops = attr.args[0].value.value
|
|
100
|
-
.split(',')
|
|
101
|
-
.map((s) => s.trim());
|
|
102
|
-
return ops.includes(operation) || ops.includes('all');
|
|
103
|
-
})
|
|
104
|
-
.map((attr) => attr.args[1].value);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
private async generateQueryGuardForModel(
|
|
108
|
-
model: DataModel,
|
|
109
|
-
sourceFile: SourceFile
|
|
110
|
-
) {
|
|
111
|
-
for (const kind of ['create', 'update', 'read', 'delete']) {
|
|
112
|
-
const func = sourceFile
|
|
113
|
-
.addFunction({
|
|
114
|
-
name: model.name + '_' + kind,
|
|
115
|
-
returnType: 'any',
|
|
116
|
-
parameters: [
|
|
117
|
-
{
|
|
118
|
-
name: 'context',
|
|
119
|
-
type: 'QueryContext',
|
|
120
|
-
},
|
|
121
|
-
],
|
|
122
|
-
isExported: true,
|
|
123
|
-
})
|
|
124
|
-
.addBody();
|
|
125
|
-
|
|
126
|
-
func.addStatements('const { user } = context;');
|
|
127
|
-
|
|
128
|
-
// r = <guard object>;
|
|
129
|
-
func.addVariableStatement({
|
|
130
|
-
declarationKind: VariableDeclarationKind.Const,
|
|
131
|
-
declarations: [
|
|
132
|
-
{
|
|
133
|
-
name: 'r',
|
|
134
|
-
initializer: (writer) => {
|
|
135
|
-
const exprWriter = new ExpressionWriter(writer);
|
|
136
|
-
const denies = this.getPolicyExpressions(
|
|
137
|
-
model,
|
|
138
|
-
'deny',
|
|
139
|
-
kind as PolicyOperationKind
|
|
140
|
-
);
|
|
141
|
-
const allows = this.getPolicyExpressions(
|
|
142
|
-
model,
|
|
143
|
-
'allow',
|
|
144
|
-
kind as PolicyOperationKind
|
|
145
|
-
);
|
|
146
|
-
|
|
147
|
-
const writeDenies = () => {
|
|
148
|
-
writer.conditionalWrite(
|
|
149
|
-
denies.length > 1,
|
|
150
|
-
'{ AND: ['
|
|
151
|
-
);
|
|
152
|
-
denies.forEach((expr, i) => {
|
|
153
|
-
writer.block(() => {
|
|
154
|
-
writer.write('NOT: ');
|
|
155
|
-
exprWriter.write(expr);
|
|
156
|
-
});
|
|
157
|
-
writer.conditionalWrite(
|
|
158
|
-
i !== denies.length - 1,
|
|
159
|
-
','
|
|
160
|
-
);
|
|
161
|
-
});
|
|
162
|
-
writer.conditionalWrite(
|
|
163
|
-
denies.length > 1,
|
|
164
|
-
']}'
|
|
165
|
-
);
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
const writeAllows = () => {
|
|
169
|
-
writer.conditionalWrite(
|
|
170
|
-
allows.length > 1,
|
|
171
|
-
'{ OR: ['
|
|
172
|
-
);
|
|
173
|
-
allows.forEach((expr, i) => {
|
|
174
|
-
exprWriter.write(expr);
|
|
175
|
-
writer.conditionalWrite(
|
|
176
|
-
i !== allows.length - 1,
|
|
177
|
-
','
|
|
178
|
-
);
|
|
179
|
-
});
|
|
180
|
-
writer.conditionalWrite(
|
|
181
|
-
allows.length > 1,
|
|
182
|
-
']}'
|
|
183
|
-
);
|
|
184
|
-
};
|
|
185
|
-
|
|
186
|
-
if (allows.length > 0 && denies.length > 0) {
|
|
187
|
-
writer.writeLine('{ AND: [');
|
|
188
|
-
writeDenies();
|
|
189
|
-
writer.writeLine(',');
|
|
190
|
-
writeAllows();
|
|
191
|
-
writer.writeLine(']}');
|
|
192
|
-
} else if (denies.length > 0) {
|
|
193
|
-
writeDenies();
|
|
194
|
-
} else if (allows.length > 0) {
|
|
195
|
-
writeAllows();
|
|
196
|
-
} else {
|
|
197
|
-
// disallow any operation
|
|
198
|
-
writer.write(`{ ${GUARD_FIELD_NAME}: false }`);
|
|
199
|
-
}
|
|
200
|
-
},
|
|
201
|
-
},
|
|
202
|
-
],
|
|
203
|
-
});
|
|
204
|
-
|
|
205
|
-
func.addStatements('return r;');
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|