Skip to content

Commit 9bf6d7f

Browse files
authored
refactor: move policy zmodel back to stdlib (#265)
* refactor: move policy zmodel back to stdlib * update
1 parent daaf0bb commit 9bf6d7f

File tree

8 files changed

+61
-75
lines changed

8 files changed

+61
-75
lines changed

packages/language/res/stdlib.zmodel

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,6 @@ function autoincrement(): Int {
116116
function dbgenerated(expr: String?): Any {
117117
} @@@expressionContext([DefaultValue])
118118

119-
/**
120-
* Gets entities value before an update. Only valid when used in a "update" policy rule.
121-
*/
122-
function future(): Any {
123-
} @@@expressionContext([AccessPolicy])
124-
125119
/**
126120
* Checks if the field value contains the search string. By default, the search is case-sensitive, and
127121
* "LIKE" operator is used to match. If `caseInSensitive` is true, "ILIKE" operator is used if
@@ -663,3 +657,56 @@ attribute @meta(_ name: String, _ value: Any)
663657
* Marks an attribute as deprecated.
664658
*/
665659
attribute @@@deprecated(_ message: String)
660+
661+
/* --- Policy Plugin --- */
662+
663+
/**
664+
* Defines an access policy that allows a set of operations when the given condition is true.
665+
*
666+
* @param operation: comma-separated list of "create", "read", "update", "delete". Use "all" to denote all operations.
667+
* @param condition: a boolean expression that controls if the operation should be allowed.
668+
*/
669+
attribute @@allow(_ operation: String @@@completionHint(["'create'", "'read'", "'update'", "'delete'", "'all'"]), _ condition: Boolean)
670+
671+
/**
672+
* Defines an access policy that allows the annotated field to be read or updated.
673+
* You can pass a third argument as `true` to make it override the model-level policies.
674+
*
675+
* @param operation: comma-separated list of "create", "read", "update", "delete". Use "all" to denote all operations.
676+
* @param condition: a boolean expression that controls if the operation should be allowed.
677+
* @param override: a boolean value that controls if the field-level policy should override the model-level policy.
678+
*/
679+
attribute @allow(_ operation: String @@@completionHint(["'create'", "'read'", "'update'", "'delete'", "'all'"]), _ condition: Boolean, _ override: Boolean?)
680+
681+
/**
682+
* Defines an access policy that denies a set of operations when the given condition is true.
683+
*
684+
* @param operation: comma-separated list of "create", "read", "update", "delete". Use "all" to denote all operations.
685+
* @param condition: a boolean expression that controls if the operation should be denied.
686+
*/
687+
attribute @@deny(_ operation: String @@@completionHint(["'create'", "'read'", "'update'", "'delete'", "'all'"]), _ condition: Boolean)
688+
689+
/**
690+
* Defines an access policy that denies the annotated field to be read or updated.
691+
*
692+
* @param operation: comma-separated list of "create", "read", "update", "delete". Use "all" to denote all operations.
693+
* @param condition: a boolean expression that controls if the operation should be denied.
694+
*/
695+
attribute @deny(_ operation: String @@@completionHint(["'create'", "'read'", "'update'", "'delete'", "'all'"]), _ condition: Boolean)
696+
697+
/**
698+
* Checks if the current user can perform the given operation on the given field.
699+
*
700+
* @param field: The field to check access for
701+
* @param operation: The operation to check access for. Can be "read", "create", "update", or "delete". If the operation is not provided,
702+
* it defaults the operation of the containing policy rule.
703+
*/
704+
function check(field: Any, operation: String?): Boolean {
705+
} @@@expressionContext([AccessPolicy])
706+
707+
/**
708+
* Gets entities value before an update. Only valid when used in a "update" policy rule.
709+
*/
710+
function future(): Any {
711+
} @@@expressionContext([AccessPolicy])
712+

packages/language/test/utils.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { invariant } from '@zenstackhq/common-helpers';
2-
import { glob } from 'glob';
32
import fs from 'node:fs';
43
import os from 'node:os';
54
import path from 'node:path';
@@ -10,7 +9,7 @@ export async function loadSchema(schema: string) {
109
// create a temp file
1110
const tempFile = path.join(os.tmpdir(), `zenstack-schema-${crypto.randomUUID()}.zmodel`);
1211
fs.writeFileSync(tempFile, schema);
13-
const r = await loadDocument(tempFile, getPluginModels());
12+
const r = await loadDocument(tempFile);
1413
expect(r).toSatisfy(
1514
(r) => r.success,
1615
`Failed to load schema: ${(r as any).errors?.map((e) => e.toString()).join(', ')}`,
@@ -23,7 +22,7 @@ export async function loadSchemaWithError(schema: string, error: string | RegExp
2322
// create a temp file
2423
const tempFile = path.join(os.tmpdir(), `zenstack-schema-${crypto.randomUUID()}.zmodel`);
2524
fs.writeFileSync(tempFile, schema);
26-
const r = await loadDocument(tempFile, getPluginModels());
25+
const r = await loadDocument(tempFile);
2726
expect(r.success).toBe(false);
2827
invariant(!r.success);
2928
if (typeof error === 'string') {
@@ -38,6 +37,3 @@ export async function loadSchemaWithError(schema: string, error: string | RegExp
3837
);
3938
}
4039
}
41-
function getPluginModels() {
42-
return glob.sync(path.resolve(__dirname, '../../runtime/src/plugins/**/plugin.zmodel'));
43-
}

packages/runtime/src/plugins/policy/plugin.zmodel

Lines changed: 0 additions & 43 deletions
This file was deleted.

packages/runtime/test/scripts/generate.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ async function generate(schemaPath: string) {
1919
const generator = new TsSchemaGenerator();
2020
const outputDir = path.dirname(schemaPath);
2121
const tsPath = path.join(outputDir, 'schema.ts');
22-
const pluginModelFiles = glob.sync(path.resolve(dir, '../../dist/**/plugin.zmodel'));
23-
const result = await loadDocument(schemaPath, pluginModelFiles);
22+
const result = await loadDocument(schemaPath);
2423
if (!result.success) {
2524
throw new Error(`Failed to load schema from ${schemaPath}: ${result.errors}`);
2625
}

packages/runtime/test/utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { invariant } from '@zenstackhq/common-helpers';
22
import { loadDocument } from '@zenstackhq/language';
33
import type { Model } from '@zenstackhq/language/ast';
44
import { PrismaSchemaGenerator } from '@zenstackhq/sdk';
5-
import { createTestProject, generateTsSchema, getPluginModules } from '@zenstackhq/testtools';
5+
import { createTestProject, generateTsSchema } from '@zenstackhq/testtools';
66
import SQLite from 'better-sqlite3';
77
import { PostgresDialect, SqliteDialect, type LogEvent } from 'kysely';
88
import { execSync } from 'node:child_process';
@@ -113,7 +113,7 @@ export async function createTestClient<Schema extends SchemaDef>(
113113
if (options?.usePrismaPush) {
114114
invariant(typeof schema === 'string' || schemaFile, 'a schema file must be provided when using prisma db push');
115115
if (!model) {
116-
const r = await loadDocument(path.join(workDir, 'schema.zmodel'), getPluginModules());
116+
const r = await loadDocument(path.join(workDir, 'schema.zmodel'));
117117
if (!r.success) {
118118
throw new Error(r.errors.join('\n'));
119119
}

packages/runtime/tsup.config.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { defineConfig } from 'tsup';
2-
import fs from 'node:fs';
32

43
export default defineConfig({
54
entry: {
@@ -14,7 +13,4 @@ export default defineConfig({
1413
clean: true,
1514
dts: true,
1615
format: ['cjs', 'esm'],
17-
async onSuccess() {
18-
fs.cpSync('src/plugins/policy/plugin.zmodel', 'dist/plugins/policy/plugin.zmodel');
19-
},
2016
});

packages/testtools/src/schema.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { loadDocument } from '@zenstackhq/language';
22
import { TsSchemaGenerator } from '@zenstackhq/sdk';
33
import type { SchemaDef } from '@zenstackhq/sdk/schema';
4-
import { glob } from 'glob';
54
import { execSync } from 'node:child_process';
65
import fs from 'node:fs';
76
import path from 'node:path';
@@ -41,8 +40,7 @@ export async function generateTsSchema(
4140
const noPrelude = schemaText.includes('datasource ');
4241
fs.writeFileSync(zmodelPath, `${noPrelude ? '' : makePrelude(provider, dbUrl)}\n\n${schemaText}`);
4342

44-
const pluginModelFiles = getPluginModules();
45-
const result = await loadDocument(zmodelPath, pluginModelFiles);
43+
const result = await loadDocument(zmodelPath);
4644
if (!result.success) {
4745
throw new Error(`Failed to load schema from ${zmodelPath}: ${result.errors}`);
4846
}
@@ -62,10 +60,6 @@ export async function generateTsSchema(
6260
return { ...(await compileAndLoad(workDir)), model: result.model };
6361
}
6462

65-
export function getPluginModules() {
66-
return glob.sync(path.resolve(__dirname, '../../runtime/src/plugins/**/plugin.zmodel'));
67-
}
68-
6963
async function compileAndLoad(workDir: string) {
7064
execSync('npx tsc', {
7165
cwd: workDir,
@@ -84,8 +78,7 @@ export function generateTsSchemaFromFile(filePath: string) {
8478

8579
export async function generateTsSchemaInPlace(schemaPath: string) {
8680
const workDir = path.dirname(schemaPath);
87-
const pluginModelFiles = glob.sync(path.resolve(__dirname, '../../runtime/src/plugins/**/plugin.zmodel'));
88-
const result = await loadDocument(schemaPath, pluginModelFiles);
81+
const result = await loadDocument(schemaPath);
8982
if (!result.success) {
9083
throw new Error(`Failed to load schema from ${schemaPath}: ${result.errors}`);
9184
}

tests/regression/generate.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,7 @@ async function main() {
1818
async function generate(schemaPath: string) {
1919
const generator = new TsSchemaGenerator();
2020
const outputDir = path.dirname(schemaPath);
21-
const tsPath = path.join(outputDir, 'schema.ts');
22-
const pluginModelFiles = glob.sync(path.resolve(dir, '../../packages/runtime/dist/**/plugin.zmodel'));
23-
const result = await loadDocument(schemaPath, pluginModelFiles);
21+
const result = await loadDocument(schemaPath);
2422
if (!result.success) {
2523
throw new Error(`Failed to load schema from ${schemaPath}: ${result.errors}`);
2624
}

0 commit comments

Comments
 (0)