@cerbos/orm-prisma 0.1.2 → 1.0.0-alpha.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/README.md +141 -14
- package/lib/cjs/index.d.ts +20 -5
- package/lib/cjs/index.js +115 -53
- package/lib/index.d.ts +20 -5
- package/lib/index.js +112 -51
- package/package.json +9 -4
- package/lib/cjs/index.js.map +0 -1
- package/lib/cjs/index.test.d.ts +0 -1
- package/lib/cjs/index.test.js +0 -311
- package/lib/index.test.d.ts +0 -1
- package/lib/index.test.js +0 -306
package/README.md
CHANGED
|
@@ -2,10 +2,24 @@
|
|
|
2
2
|
|
|
3
3
|
An adapater library that takes a [Cerbos](https://cerbos.dev) Query Plan ([PlanResources API](https://docs.cerbos.dev/cerbos/latest/api/index.html#resources-query-plan)) response and converts it into a [Prisma](https://prisma.io) where class object. This is designed to work alongside a project using the [Cerbos Javascript SDK](https://github.com/cerbos/cerbos-sdk-javascript).
|
|
4
4
|
|
|
5
|
-
The following conditions are supported: `and`, `or`, `eq`, `ne`, `lt`, `gt`, `lte`, `gte` and `in`.
|
|
5
|
+
The following conditions are supported: `and`, `or`, `eq`, `ne`, `lt`, `gt`, `lte`, `gte` and `in`, as well as relational filters `some`, `none`, `is` and `isNot`.
|
|
6
|
+
|
|
7
|
+
Not Supported:
|
|
8
|
+
|
|
9
|
+
- `every`
|
|
10
|
+
- `contains`
|
|
11
|
+
- `search`
|
|
12
|
+
- `mode`
|
|
13
|
+
- `startsWith`
|
|
14
|
+
- `endsWith`
|
|
15
|
+
- Scalar filters
|
|
16
|
+
- Composite type filters
|
|
17
|
+
- Atomic number operations
|
|
18
|
+
- Composite keys
|
|
6
19
|
|
|
7
20
|
## Requirements
|
|
8
21
|
- Cerbos > v0.16
|
|
22
|
+
- `@cerbos/http` or `@cerbos/grpc` client
|
|
9
23
|
|
|
10
24
|
## Usage
|
|
11
25
|
|
|
@@ -13,32 +27,145 @@ The following conditions are supported: `and`, `or`, `eq`, `ne`, `lt`, `gt`, `lt
|
|
|
13
27
|
npm install @cerbos/orm-prisma
|
|
14
28
|
```
|
|
15
29
|
|
|
16
|
-
This package exports a
|
|
30
|
+
This package exports a function:
|
|
17
31
|
|
|
18
|
-
```
|
|
19
|
-
|
|
32
|
+
```ts
|
|
33
|
+
import { queryPlanToPrisma, PlanKind } from "@cerbos/orm-prisma";
|
|
34
|
+
|
|
35
|
+
queryPlanToPrisma({ queryPlan, fieldNameMapper, relationMapper }): {
|
|
36
|
+
kind: PlanKind,
|
|
37
|
+
filters?: any // a filter to pass to the findMany() function of Prisma
|
|
38
|
+
}
|
|
20
39
|
```
|
|
21
40
|
|
|
22
|
-
|
|
41
|
+
where `PlanKind` is:
|
|
23
42
|
|
|
24
|
-
|
|
43
|
+
```ts
|
|
44
|
+
export enum PlanKind {
|
|
45
|
+
/**
|
|
46
|
+
* The specified action is always allowed for the principal on resources matching the input.
|
|
47
|
+
*/
|
|
48
|
+
ALWAYS_ALLOWED = "KIND_ALWAYS_ALLOWED",
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* The specified action is always denied for the principal on resources matching the input.
|
|
52
|
+
*/
|
|
53
|
+
ALWAYS_DENIED = "KIND_ALWAYS_DENIED",
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* The specified action is conditionally allowed for the principal on resources matching the input.
|
|
57
|
+
*/
|
|
58
|
+
CONDITIONAL = "KIND_CONDITIONAL",
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The function reqiures the full query plan from Cerbos to be passed in an object along with a `fieldNameMapper` and option `relationMapper` if the model has relations.
|
|
63
|
+
|
|
64
|
+
A basic implementation can be as simple as:
|
|
25
65
|
|
|
26
66
|
```js
|
|
27
|
-
{
|
|
28
|
-
|
|
67
|
+
import { GRPC as Cerbos } from "@cerbos/grpc";
|
|
68
|
+
import { PrismaClient } from "@prisma/client";
|
|
69
|
+
|
|
70
|
+
import { queryPlanToPrisma, PlanKind } from "@cerbos/orm-prisma";
|
|
71
|
+
|
|
72
|
+
const prisma = new PrismaClient();
|
|
73
|
+
const cerbos = new Cerbos("localhost:3592", { tls: false });
|
|
74
|
+
|
|
75
|
+
// Fetch the query plan from Cerbos passing in the principal
|
|
76
|
+
// resource type and action
|
|
77
|
+
const queryPlan = await cerbos.planResources({
|
|
78
|
+
principal: {....},
|
|
79
|
+
resource: { kind: "resourceKind" },
|
|
80
|
+
action: "view"
|
|
81
|
+
})
|
|
82
|
+
|
|
83
|
+
// Generate the prisma filter from the query plan
|
|
84
|
+
const result = queryPlanToPrisma({
|
|
85
|
+
queryPlan,
|
|
86
|
+
fieldNameMapper: {
|
|
87
|
+
"request.resource.attr.aFieldName": "prismaModelFieldName"
|
|
88
|
+
},
|
|
89
|
+
relationMapper: {
|
|
90
|
+
"request.resource.attr.aRelatedModel": {
|
|
91
|
+
"relation": "aRelatedModel",
|
|
92
|
+
"field": "id" // the column it is joined on
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
// The query plan says the user would always be denied
|
|
98
|
+
// return empty or throw an error depending on your app.
|
|
99
|
+
if(result.kind == PlanKind.ALWAYS_DENIED) {
|
|
100
|
+
return console.log([]);
|
|
29
101
|
}
|
|
30
102
|
|
|
103
|
+
// Pass the filters in as where conditions
|
|
104
|
+
// If you have prexisting where conditions, you can pass them in an AND clause
|
|
105
|
+
const result = await prisma.myModel.findMany({
|
|
106
|
+
where: {
|
|
107
|
+
AND: result.filters
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
console.log(result)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
The `fieldNameMapper` is used to convert the field names in the query plan response to names of fields in the Prisma model - this can be done as a map or a function:
|
|
115
|
+
|
|
116
|
+
```js
|
|
117
|
+
const filters = queryPlanToPrisma({
|
|
118
|
+
queryPlan,
|
|
119
|
+
fieldNameMapper: {
|
|
120
|
+
"request.resource.attr.aFieldName": "prismaModelFieldName"
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
31
124
|
//or
|
|
32
125
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
126
|
+
const filters = queryPlanToPrisma({
|
|
127
|
+
queryPlan,
|
|
128
|
+
fieldNameMapper: (fieldName: string): string => {
|
|
129
|
+
if(fieldName.indexOf("request.resource.") > 0) {
|
|
130
|
+
return fieldName.replace("request.resource.attr", "")
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if(fieldName.indexOf("request.principal.") > 0) {
|
|
134
|
+
return fieldName.replace("request.principal.attr", "")
|
|
135
|
+
}
|
|
36
136
|
}
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
The `relationMapper` is used to convert references to fields which are joins at the database level
|
|
37
142
|
|
|
38
|
-
|
|
39
|
-
|
|
143
|
+
```js
|
|
144
|
+
const filters = queryPlanToPrisma({
|
|
145
|
+
queryPlan,
|
|
146
|
+
fieldNameMapper: {},
|
|
147
|
+
relationMapper: {
|
|
148
|
+
"request.resource.attr.aRelatedModel": {
|
|
149
|
+
"relation": "aRelatedModel",
|
|
150
|
+
"field": "id" // the column it is joined on
|
|
151
|
+
}
|
|
40
152
|
}
|
|
41
|
-
}
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
//or
|
|
156
|
+
|
|
157
|
+
const filters = queryPlanToPrisma({
|
|
158
|
+
queryPlan,
|
|
159
|
+
fieldNameMapper: {},
|
|
160
|
+
relationMapper: (fieldName: string): string => {
|
|
161
|
+
if(fieldName.indexOf("request.resource.") > 0) {
|
|
162
|
+
return {
|
|
163
|
+
"relation": fieldName.replace("request.resource.attr.", ""),
|
|
164
|
+
"field": "id" // the column it is joined on
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
});
|
|
42
169
|
```
|
|
43
170
|
|
|
44
171
|
## Full Example
|
package/lib/cjs/index.d.ts
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
|
-
import { PlanResourcesResponse } from "@cerbos/core";
|
|
1
|
+
import { PlanResourcesResponse, PlanKind as PK } from "@cerbos/core";
|
|
2
|
+
export declare type PlanKind = PK;
|
|
3
|
+
export declare const PlanKind: typeof PK;
|
|
4
|
+
declare type FieldMapper = {
|
|
5
|
+
[key: string]: string;
|
|
6
|
+
} | ((key: string) => string);
|
|
7
|
+
declare type Relation = {
|
|
8
|
+
relation: string;
|
|
9
|
+
field: string;
|
|
10
|
+
};
|
|
11
|
+
declare type RelationMapper = {
|
|
12
|
+
[key: string]: Relation;
|
|
13
|
+
} | ((key: string) => Relation);
|
|
2
14
|
interface QueryPlanToPrismaArgs {
|
|
3
15
|
queryPlan: PlanResourcesResponse;
|
|
4
|
-
fieldNameMapper:
|
|
5
|
-
|
|
6
|
-
} | ((key: string) => string);
|
|
16
|
+
fieldNameMapper: FieldMapper;
|
|
17
|
+
relationMapper?: RelationMapper;
|
|
7
18
|
}
|
|
8
|
-
|
|
19
|
+
interface QueryPlanToPrismaResult {
|
|
20
|
+
kind: PlanKind;
|
|
21
|
+
filters?: any;
|
|
22
|
+
}
|
|
23
|
+
export declare function queryPlanToPrisma({ queryPlan, fieldNameMapper, relationMapper }: QueryPlanToPrismaArgs): QueryPlanToPrismaResult;
|
|
9
24
|
export {};
|
package/lib/cjs/index.js
CHANGED
|
@@ -1,21 +1,39 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.queryPlanToPrisma = exports.PlanKind = void 0;
|
|
3
4
|
const core_1 = require("@cerbos/core");
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
5
|
+
exports.PlanKind = core_1.PlanKind;
|
|
6
|
+
function queryPlanToPrisma({ queryPlan, fieldNameMapper, relationMapper = {} }) {
|
|
7
|
+
if (queryPlan.kind === exports.PlanKind.ALWAYS_ALLOWED)
|
|
8
|
+
return {
|
|
9
|
+
kind: exports.PlanKind.ALWAYS_ALLOWED,
|
|
10
|
+
filters: {}
|
|
11
|
+
};
|
|
12
|
+
if (queryPlan.kind === exports.PlanKind.ALWAYS_DENIED)
|
|
13
|
+
return {
|
|
14
|
+
kind: exports.PlanKind.ALWAYS_DENIED
|
|
15
|
+
};
|
|
16
|
+
return {
|
|
17
|
+
kind: exports.PlanKind.CONDITIONAL,
|
|
18
|
+
filters: mapOperand(queryPlan.condition, (key) => {
|
|
19
|
+
if (typeof fieldNameMapper === "function") {
|
|
20
|
+
return fieldNameMapper(key);
|
|
21
|
+
}
|
|
22
|
+
else {
|
|
23
|
+
return (fieldNameMapper[key] = fieldNameMapper[key] || key);
|
|
24
|
+
}
|
|
25
|
+
}, (key) => {
|
|
26
|
+
if (typeof relationMapper === "function") {
|
|
27
|
+
return relationMapper(key);
|
|
28
|
+
}
|
|
29
|
+
else if (relationMapper[key]) {
|
|
30
|
+
return relationMapper[key];
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}, {})
|
|
34
|
+
};
|
|
17
35
|
}
|
|
18
|
-
exports.
|
|
36
|
+
exports.queryPlanToPrisma = queryPlanToPrisma;
|
|
19
37
|
function isExpression(e) {
|
|
20
38
|
return e.operator !== undefined;
|
|
21
39
|
}
|
|
@@ -26,56 +44,100 @@ function isVariable(e) {
|
|
|
26
44
|
return e.variable !== undefined;
|
|
27
45
|
}
|
|
28
46
|
function getOperandName(operands) {
|
|
29
|
-
|
|
47
|
+
const op = operands.find(o => o.hasOwnProperty("name"));
|
|
48
|
+
if (!op)
|
|
49
|
+
return;
|
|
50
|
+
return op.name;
|
|
30
51
|
}
|
|
31
52
|
function getOperandValue(operands) {
|
|
32
|
-
|
|
53
|
+
const op = operands.find(o => o.hasOwnProperty("value"));
|
|
54
|
+
if (!op)
|
|
55
|
+
return;
|
|
56
|
+
return op.value;
|
|
33
57
|
}
|
|
34
|
-
function mapOperand(operand, getFieldName, output = {}) {
|
|
58
|
+
function mapOperand(operand, getFieldName, getRelationName, output = {}) {
|
|
35
59
|
if (isExpression(operand)) {
|
|
36
60
|
const { operator, operands } = operand;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
const opName = getOperandName(operands);
|
|
62
|
+
const opValue = getOperandValue(operands);
|
|
63
|
+
const relation = opName && getRelationName(opName);
|
|
64
|
+
const fieldName = opName && getFieldName(opName);
|
|
65
|
+
if (operator == "and") {
|
|
66
|
+
if (operands.length < 2)
|
|
67
|
+
throw Error("Expected atleast 2 operands");
|
|
68
|
+
output.AND = operands.map((o) => mapOperand(o, getFieldName, getRelationName, {}));
|
|
69
|
+
}
|
|
70
|
+
if (operator == "or") {
|
|
71
|
+
if (operands.length < 2)
|
|
72
|
+
throw Error("Expected atleast 2 operands");
|
|
73
|
+
output.OR = operands.map((o) => mapOperand(o, getFieldName, getRelationName, {}));
|
|
74
|
+
}
|
|
75
|
+
if (operator == "not") {
|
|
76
|
+
if (operands.length > 1)
|
|
77
|
+
throw Error("Expected only one operand");
|
|
78
|
+
output.NOT = operands.map((o) => mapOperand(o, getFieldName, getRelationName, {}))[0];
|
|
79
|
+
}
|
|
80
|
+
if (operator == "eq") {
|
|
81
|
+
if (relation) {
|
|
82
|
+
output[relation.relation] = {
|
|
83
|
+
is: {
|
|
84
|
+
[relation.field]: opValue
|
|
85
|
+
}
|
|
52
86
|
};
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
output[
|
|
56
|
-
|
|
87
|
+
}
|
|
88
|
+
else if (fieldName) {
|
|
89
|
+
output[fieldName] = {
|
|
90
|
+
equals: opValue,
|
|
57
91
|
};
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (operator == "ne") {
|
|
95
|
+
if (relation) {
|
|
96
|
+
output[relation.relation] = {
|
|
97
|
+
isNot: {
|
|
98
|
+
[relation.field]: opValue
|
|
99
|
+
}
|
|
62
100
|
};
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
output[
|
|
66
|
-
|
|
101
|
+
}
|
|
102
|
+
else if (fieldName) {
|
|
103
|
+
output[fieldName] = {
|
|
104
|
+
not: opValue,
|
|
67
105
|
};
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (operator == "lt" && fieldName) {
|
|
109
|
+
output[fieldName] = {
|
|
110
|
+
lt: opValue,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
if (operator == "gt" && fieldName) {
|
|
114
|
+
output[fieldName] = {
|
|
115
|
+
gt: opValue,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
if (operator == "le" && fieldName) {
|
|
119
|
+
output[fieldName] = {
|
|
120
|
+
lte: opValue,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
if (operator == "ge" && fieldName) {
|
|
124
|
+
output[fieldName] = {
|
|
125
|
+
gte: opValue,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
if (operator == "in") {
|
|
129
|
+
if (relation) {
|
|
130
|
+
output[relation.relation] = {
|
|
131
|
+
some: {
|
|
132
|
+
[relation.field]: opValue
|
|
133
|
+
}
|
|
72
134
|
};
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
output[
|
|
76
|
-
in:
|
|
135
|
+
}
|
|
136
|
+
else if (fieldName) {
|
|
137
|
+
output[fieldName] = {
|
|
138
|
+
in: opValue,
|
|
77
139
|
};
|
|
78
|
-
|
|
140
|
+
}
|
|
79
141
|
}
|
|
80
142
|
}
|
|
81
143
|
return output;
|
package/lib/index.d.ts
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
|
-
import { PlanResourcesResponse } from "@cerbos/core";
|
|
1
|
+
import { PlanResourcesResponse, PlanKind as PK } from "@cerbos/core";
|
|
2
|
+
export declare type PlanKind = PK;
|
|
3
|
+
export declare const PlanKind: typeof PK;
|
|
4
|
+
declare type FieldMapper = {
|
|
5
|
+
[key: string]: string;
|
|
6
|
+
} | ((key: string) => string);
|
|
7
|
+
declare type Relation = {
|
|
8
|
+
relation: string;
|
|
9
|
+
field: string;
|
|
10
|
+
};
|
|
11
|
+
declare type RelationMapper = {
|
|
12
|
+
[key: string]: Relation;
|
|
13
|
+
} | ((key: string) => Relation);
|
|
2
14
|
interface QueryPlanToPrismaArgs {
|
|
3
15
|
queryPlan: PlanResourcesResponse;
|
|
4
|
-
fieldNameMapper:
|
|
5
|
-
|
|
6
|
-
} | ((key: string) => string);
|
|
16
|
+
fieldNameMapper: FieldMapper;
|
|
17
|
+
relationMapper?: RelationMapper;
|
|
7
18
|
}
|
|
8
|
-
|
|
19
|
+
interface QueryPlanToPrismaResult {
|
|
20
|
+
kind: PlanKind;
|
|
21
|
+
filters?: any;
|
|
22
|
+
}
|
|
23
|
+
export declare function queryPlanToPrisma({ queryPlan, fieldNameMapper, relationMapper }: QueryPlanToPrismaArgs): QueryPlanToPrismaResult;
|
|
9
24
|
export {};
|
package/lib/index.js
CHANGED
|
@@ -1,17 +1,34 @@
|
|
|
1
|
-
import { PlanKind } from "@cerbos/core";
|
|
2
|
-
export
|
|
1
|
+
import { PlanKind as PK } from "@cerbos/core";
|
|
2
|
+
export const PlanKind = PK;
|
|
3
|
+
export function queryPlanToPrisma({ queryPlan, fieldNameMapper, relationMapper = {} }) {
|
|
3
4
|
if (queryPlan.kind === PlanKind.ALWAYS_ALLOWED)
|
|
4
|
-
return {
|
|
5
|
+
return {
|
|
6
|
+
kind: PlanKind.ALWAYS_ALLOWED,
|
|
7
|
+
filters: {}
|
|
8
|
+
};
|
|
5
9
|
if (queryPlan.kind === PlanKind.ALWAYS_DENIED)
|
|
6
|
-
return {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
10
|
+
return {
|
|
11
|
+
kind: PlanKind.ALWAYS_DENIED
|
|
12
|
+
};
|
|
13
|
+
return {
|
|
14
|
+
kind: PlanKind.CONDITIONAL,
|
|
15
|
+
filters: mapOperand(queryPlan.condition, (key) => {
|
|
16
|
+
if (typeof fieldNameMapper === "function") {
|
|
17
|
+
return fieldNameMapper(key);
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
20
|
+
return (fieldNameMapper[key] = fieldNameMapper[key] || key);
|
|
21
|
+
}
|
|
22
|
+
}, (key) => {
|
|
23
|
+
if (typeof relationMapper === "function") {
|
|
24
|
+
return relationMapper(key);
|
|
25
|
+
}
|
|
26
|
+
else if (relationMapper[key]) {
|
|
27
|
+
return relationMapper[key];
|
|
28
|
+
}
|
|
29
|
+
return null;
|
|
30
|
+
}, {})
|
|
31
|
+
};
|
|
15
32
|
}
|
|
16
33
|
function isExpression(e) {
|
|
17
34
|
return e.operator !== undefined;
|
|
@@ -23,56 +40,100 @@ function isVariable(e) {
|
|
|
23
40
|
return e.variable !== undefined;
|
|
24
41
|
}
|
|
25
42
|
function getOperandName(operands) {
|
|
26
|
-
|
|
43
|
+
const op = operands.find(o => o.hasOwnProperty("name"));
|
|
44
|
+
if (!op)
|
|
45
|
+
return;
|
|
46
|
+
return op.name;
|
|
27
47
|
}
|
|
28
48
|
function getOperandValue(operands) {
|
|
29
|
-
|
|
49
|
+
const op = operands.find(o => o.hasOwnProperty("value"));
|
|
50
|
+
if (!op)
|
|
51
|
+
return;
|
|
52
|
+
return op.value;
|
|
30
53
|
}
|
|
31
|
-
function mapOperand(operand, getFieldName, output = {}) {
|
|
54
|
+
function mapOperand(operand, getFieldName, getRelationName, output = {}) {
|
|
32
55
|
if (isExpression(operand)) {
|
|
33
56
|
const { operator, operands } = operand;
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
57
|
+
const opName = getOperandName(operands);
|
|
58
|
+
const opValue = getOperandValue(operands);
|
|
59
|
+
const relation = opName && getRelationName(opName);
|
|
60
|
+
const fieldName = opName && getFieldName(opName);
|
|
61
|
+
if (operator == "and") {
|
|
62
|
+
if (operands.length < 2)
|
|
63
|
+
throw Error("Expected atleast 2 operands");
|
|
64
|
+
output.AND = operands.map((o) => mapOperand(o, getFieldName, getRelationName, {}));
|
|
65
|
+
}
|
|
66
|
+
if (operator == "or") {
|
|
67
|
+
if (operands.length < 2)
|
|
68
|
+
throw Error("Expected atleast 2 operands");
|
|
69
|
+
output.OR = operands.map((o) => mapOperand(o, getFieldName, getRelationName, {}));
|
|
70
|
+
}
|
|
71
|
+
if (operator == "not") {
|
|
72
|
+
if (operands.length > 1)
|
|
73
|
+
throw Error("Expected only one operand");
|
|
74
|
+
output.NOT = operands.map((o) => mapOperand(o, getFieldName, getRelationName, {}))[0];
|
|
75
|
+
}
|
|
76
|
+
if (operator == "eq") {
|
|
77
|
+
if (relation) {
|
|
78
|
+
output[relation.relation] = {
|
|
79
|
+
is: {
|
|
80
|
+
[relation.field]: opValue
|
|
81
|
+
}
|
|
49
82
|
};
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
output[
|
|
53
|
-
|
|
83
|
+
}
|
|
84
|
+
else if (fieldName) {
|
|
85
|
+
output[fieldName] = {
|
|
86
|
+
equals: opValue,
|
|
54
87
|
};
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (operator == "ne") {
|
|
91
|
+
if (relation) {
|
|
92
|
+
output[relation.relation] = {
|
|
93
|
+
isNot: {
|
|
94
|
+
[relation.field]: opValue
|
|
95
|
+
}
|
|
59
96
|
};
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
output[
|
|
63
|
-
|
|
97
|
+
}
|
|
98
|
+
else if (fieldName) {
|
|
99
|
+
output[fieldName] = {
|
|
100
|
+
not: opValue,
|
|
64
101
|
};
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if (operator == "lt" && fieldName) {
|
|
105
|
+
output[fieldName] = {
|
|
106
|
+
lt: opValue,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
if (operator == "gt" && fieldName) {
|
|
110
|
+
output[fieldName] = {
|
|
111
|
+
gt: opValue,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (operator == "le" && fieldName) {
|
|
115
|
+
output[fieldName] = {
|
|
116
|
+
lte: opValue,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
if (operator == "ge" && fieldName) {
|
|
120
|
+
output[fieldName] = {
|
|
121
|
+
gte: opValue,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
if (operator == "in") {
|
|
125
|
+
if (relation) {
|
|
126
|
+
output[relation.relation] = {
|
|
127
|
+
some: {
|
|
128
|
+
[relation.field]: opValue
|
|
129
|
+
}
|
|
69
130
|
};
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
output[
|
|
73
|
-
in:
|
|
131
|
+
}
|
|
132
|
+
else if (fieldName) {
|
|
133
|
+
output[fieldName] = {
|
|
134
|
+
in: opValue,
|
|
74
135
|
};
|
|
75
|
-
|
|
136
|
+
}
|
|
76
137
|
}
|
|
77
138
|
}
|
|
78
139
|
return output;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cerbos/orm-prisma",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0-alpha.0",
|
|
4
4
|
"homepage": "https://cerbos.dev",
|
|
5
5
|
"description": "",
|
|
6
6
|
"private": false,
|
|
@@ -15,17 +15,22 @@
|
|
|
15
15
|
"preversion": "npm run lint",
|
|
16
16
|
"version": "git add -A src",
|
|
17
17
|
"postversion": "git push && git push --tags",
|
|
18
|
-
"test": "jest src/**.test.ts"
|
|
18
|
+
"test": "npx prisma db push --force-reset && cerbos run --log-level=error --set=storage.disk.directory=../policies --set=telemetry.disabled=true -- jest src/**.test.ts",
|
|
19
|
+
"cerbos": "cerbos server --config=./cerbos-config.yaml"
|
|
19
20
|
},
|
|
20
21
|
"author": "Cerbos",
|
|
21
22
|
"license": "Apache-2.0",
|
|
22
23
|
"devDependencies": {
|
|
23
|
-
"@
|
|
24
|
+
"@prisma/client": "^4.3.1",
|
|
25
|
+
"@types/jest": "^29.0.3",
|
|
26
|
+
"@types/node": "^18.7.21",
|
|
27
|
+
"prisma": "^4.3.1",
|
|
24
28
|
"ts-jest": "^28.0.8",
|
|
25
29
|
"ts-node": "^10.4.0",
|
|
26
30
|
"typescript": "^4.5.4"
|
|
27
31
|
},
|
|
28
32
|
"dependencies": {
|
|
29
|
-
"@cerbos/core": "^0.5.1"
|
|
33
|
+
"@cerbos/core": "^0.5.1",
|
|
34
|
+
"@cerbos/grpc": "^0.8.0"
|
|
30
35
|
}
|
|
31
36
|
}
|
package/lib/cjs/index.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
|
package/lib/cjs/index.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/lib/cjs/index.test.js
DELETED
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const _1 = __importDefault(require("."));
|
|
7
|
-
const core_1 = require("@cerbos/core");
|
|
8
|
-
test("always allowed", () => {
|
|
9
|
-
const queryPlan = {
|
|
10
|
-
requestId: "",
|
|
11
|
-
metadata: undefined,
|
|
12
|
-
kind: core_1.PlanKind.ALWAYS_ALLOWED,
|
|
13
|
-
};
|
|
14
|
-
const result = (0, _1.default)({
|
|
15
|
-
queryPlan,
|
|
16
|
-
fieldNameMapper: {
|
|
17
|
-
"request.resource.attr.ownerId": "ownerId",
|
|
18
|
-
},
|
|
19
|
-
});
|
|
20
|
-
expect(result).toStrictEqual({});
|
|
21
|
-
});
|
|
22
|
-
test("always denied", () => {
|
|
23
|
-
const queryPlan = {
|
|
24
|
-
requestId: "",
|
|
25
|
-
metadata: undefined,
|
|
26
|
-
kind: core_1.PlanKind.ALWAYS_DENIED,
|
|
27
|
-
};
|
|
28
|
-
const result = (0, _1.default)({
|
|
29
|
-
queryPlan,
|
|
30
|
-
fieldNameMapper: {
|
|
31
|
-
"request.resource.attr.ownerId": "ownerId",
|
|
32
|
-
},
|
|
33
|
-
});
|
|
34
|
-
expect(result).toStrictEqual({ "1": { "equals": 0 } });
|
|
35
|
-
});
|
|
36
|
-
test("conditional - eq", () => {
|
|
37
|
-
const queryPlan = {
|
|
38
|
-
requestId: "",
|
|
39
|
-
metadata: undefined,
|
|
40
|
-
kind: core_1.PlanKind.CONDITIONAL,
|
|
41
|
-
condition: {
|
|
42
|
-
operator: "eq",
|
|
43
|
-
operands: [{ name: "request.resource.attr.ownerId" }, { value: "sally" }],
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
const result = (0, _1.default)({
|
|
47
|
-
queryPlan,
|
|
48
|
-
fieldNameMapper: {
|
|
49
|
-
"request.resource.attr.ownerId": "ownerId",
|
|
50
|
-
},
|
|
51
|
-
});
|
|
52
|
-
expect(result).toStrictEqual({ ownerId: { equals: "sally" } });
|
|
53
|
-
});
|
|
54
|
-
test("conditional - eq - inverted order", () => {
|
|
55
|
-
const queryPlan = {
|
|
56
|
-
requestId: "",
|
|
57
|
-
metadata: undefined,
|
|
58
|
-
kind: core_1.PlanKind.CONDITIONAL,
|
|
59
|
-
condition: {
|
|
60
|
-
operator: "eq",
|
|
61
|
-
operands: [
|
|
62
|
-
{ value: "sally" },
|
|
63
|
-
{ name: "request.resource.attr.ownerId" }
|
|
64
|
-
],
|
|
65
|
-
},
|
|
66
|
-
};
|
|
67
|
-
const result = (0, _1.default)({
|
|
68
|
-
queryPlan,
|
|
69
|
-
fieldNameMapper: {
|
|
70
|
-
"request.resource.attr.ownerId": "ownerId",
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
expect(result).toStrictEqual({ ownerId: { equals: "sally" } });
|
|
74
|
-
});
|
|
75
|
-
test("conditional - and", () => {
|
|
76
|
-
const queryPlan = {
|
|
77
|
-
requestId: "",
|
|
78
|
-
metadata: undefined,
|
|
79
|
-
kind: core_1.PlanKind.CONDITIONAL,
|
|
80
|
-
condition: {
|
|
81
|
-
"operator": "and",
|
|
82
|
-
"operands": [
|
|
83
|
-
{
|
|
84
|
-
"operator": "eq",
|
|
85
|
-
"operands": [
|
|
86
|
-
{
|
|
87
|
-
"name": "request.resource.attr.department"
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
"value": "marketing"
|
|
91
|
-
}
|
|
92
|
-
]
|
|
93
|
-
},
|
|
94
|
-
{
|
|
95
|
-
"operator": "ne",
|
|
96
|
-
"operands": [
|
|
97
|
-
{
|
|
98
|
-
"name": "request.resource.attr.team"
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
"value": "design"
|
|
102
|
-
}
|
|
103
|
-
]
|
|
104
|
-
}
|
|
105
|
-
]
|
|
106
|
-
},
|
|
107
|
-
};
|
|
108
|
-
const result = (0, _1.default)({
|
|
109
|
-
queryPlan,
|
|
110
|
-
fieldNameMapper: {
|
|
111
|
-
"request.resource.attr.department": "department",
|
|
112
|
-
"request.resource.attr.team": "team",
|
|
113
|
-
},
|
|
114
|
-
});
|
|
115
|
-
expect(result).toStrictEqual({
|
|
116
|
-
AND: [
|
|
117
|
-
{
|
|
118
|
-
department: { equals: "marketing" }
|
|
119
|
-
},
|
|
120
|
-
{
|
|
121
|
-
team: { not: "design" }
|
|
122
|
-
}
|
|
123
|
-
]
|
|
124
|
-
});
|
|
125
|
-
});
|
|
126
|
-
test("conditional - or", () => {
|
|
127
|
-
const queryPlan = {
|
|
128
|
-
requestId: "",
|
|
129
|
-
metadata: undefined,
|
|
130
|
-
kind: core_1.PlanKind.CONDITIONAL,
|
|
131
|
-
condition: {
|
|
132
|
-
"operator": "or",
|
|
133
|
-
"operands": [
|
|
134
|
-
{
|
|
135
|
-
"operator": "eq",
|
|
136
|
-
"operands": [
|
|
137
|
-
{
|
|
138
|
-
"name": "request.resource.attr.department"
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
"value": "marketing"
|
|
142
|
-
}
|
|
143
|
-
]
|
|
144
|
-
},
|
|
145
|
-
{
|
|
146
|
-
"operator": "ne",
|
|
147
|
-
"operands": [
|
|
148
|
-
{
|
|
149
|
-
"name": "request.resource.attr.team"
|
|
150
|
-
},
|
|
151
|
-
{
|
|
152
|
-
"value": "design"
|
|
153
|
-
}
|
|
154
|
-
]
|
|
155
|
-
}
|
|
156
|
-
]
|
|
157
|
-
},
|
|
158
|
-
};
|
|
159
|
-
const result = (0, _1.default)({
|
|
160
|
-
queryPlan,
|
|
161
|
-
fieldNameMapper: {
|
|
162
|
-
"request.resource.attr.department": "department",
|
|
163
|
-
"request.resource.attr.team": "team",
|
|
164
|
-
},
|
|
165
|
-
});
|
|
166
|
-
expect(result).toStrictEqual({
|
|
167
|
-
OR: [
|
|
168
|
-
{
|
|
169
|
-
department: { equals: "marketing" }
|
|
170
|
-
},
|
|
171
|
-
{
|
|
172
|
-
team: { not: "design" }
|
|
173
|
-
}
|
|
174
|
-
]
|
|
175
|
-
});
|
|
176
|
-
});
|
|
177
|
-
test("conditional - in", () => {
|
|
178
|
-
const queryPlan = {
|
|
179
|
-
requestId: "",
|
|
180
|
-
metadata: undefined,
|
|
181
|
-
kind: core_1.PlanKind.CONDITIONAL,
|
|
182
|
-
condition: {
|
|
183
|
-
"operator": "in",
|
|
184
|
-
"operands": [
|
|
185
|
-
{
|
|
186
|
-
"name": "request.resource.attr.department"
|
|
187
|
-
},
|
|
188
|
-
{
|
|
189
|
-
"value": ["marketing", "design"]
|
|
190
|
-
}
|
|
191
|
-
]
|
|
192
|
-
},
|
|
193
|
-
};
|
|
194
|
-
const result = (0, _1.default)({
|
|
195
|
-
queryPlan,
|
|
196
|
-
fieldNameMapper: {
|
|
197
|
-
"request.resource.attr.department": "department",
|
|
198
|
-
},
|
|
199
|
-
});
|
|
200
|
-
expect(result).toStrictEqual({
|
|
201
|
-
department: { in: ["marketing", "design"] }
|
|
202
|
-
});
|
|
203
|
-
});
|
|
204
|
-
test("conditional - gt", () => {
|
|
205
|
-
const queryPlan = {
|
|
206
|
-
requestId: "",
|
|
207
|
-
metadata: undefined,
|
|
208
|
-
kind: core_1.PlanKind.CONDITIONAL,
|
|
209
|
-
condition: {
|
|
210
|
-
"operator": "gt",
|
|
211
|
-
"operands": [
|
|
212
|
-
{
|
|
213
|
-
"name": "request.resource.attr.amount"
|
|
214
|
-
},
|
|
215
|
-
{
|
|
216
|
-
"value": 1000
|
|
217
|
-
}
|
|
218
|
-
]
|
|
219
|
-
},
|
|
220
|
-
};
|
|
221
|
-
const result = (0, _1.default)({
|
|
222
|
-
queryPlan,
|
|
223
|
-
fieldNameMapper: {
|
|
224
|
-
"request.resource.attr.amount": "amount",
|
|
225
|
-
},
|
|
226
|
-
});
|
|
227
|
-
expect(result).toStrictEqual({
|
|
228
|
-
amount: { gt: 1000 }
|
|
229
|
-
});
|
|
230
|
-
});
|
|
231
|
-
test("conditional - lt", () => {
|
|
232
|
-
const queryPlan = {
|
|
233
|
-
requestId: "",
|
|
234
|
-
metadata: undefined,
|
|
235
|
-
kind: core_1.PlanKind.CONDITIONAL,
|
|
236
|
-
condition: {
|
|
237
|
-
"operator": "lt",
|
|
238
|
-
"operands": [
|
|
239
|
-
{
|
|
240
|
-
"name": "request.resource.attr.amount"
|
|
241
|
-
},
|
|
242
|
-
{
|
|
243
|
-
"value": 1000
|
|
244
|
-
}
|
|
245
|
-
]
|
|
246
|
-
},
|
|
247
|
-
};
|
|
248
|
-
const result = (0, _1.default)({
|
|
249
|
-
queryPlan,
|
|
250
|
-
fieldNameMapper: {
|
|
251
|
-
"request.resource.attr.amount": "amount",
|
|
252
|
-
},
|
|
253
|
-
});
|
|
254
|
-
expect(result).toStrictEqual({
|
|
255
|
-
amount: { lt: 1000 }
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
test("conditional - gte", () => {
|
|
259
|
-
const queryPlan = {
|
|
260
|
-
requestId: "",
|
|
261
|
-
metadata: undefined,
|
|
262
|
-
kind: core_1.PlanKind.CONDITIONAL,
|
|
263
|
-
condition: {
|
|
264
|
-
"operator": "gte",
|
|
265
|
-
"operands": [
|
|
266
|
-
{
|
|
267
|
-
"name": "request.resource.attr.amount"
|
|
268
|
-
},
|
|
269
|
-
{
|
|
270
|
-
"value": 1000
|
|
271
|
-
}
|
|
272
|
-
]
|
|
273
|
-
},
|
|
274
|
-
};
|
|
275
|
-
const result = (0, _1.default)({
|
|
276
|
-
queryPlan,
|
|
277
|
-
fieldNameMapper: {
|
|
278
|
-
"request.resource.attr.amount": "amount",
|
|
279
|
-
},
|
|
280
|
-
});
|
|
281
|
-
expect(result).toStrictEqual({
|
|
282
|
-
amount: { gte: 1000 }
|
|
283
|
-
});
|
|
284
|
-
});
|
|
285
|
-
test("conditional - lte", () => {
|
|
286
|
-
const queryPlan = {
|
|
287
|
-
requestId: "",
|
|
288
|
-
metadata: undefined,
|
|
289
|
-
kind: core_1.PlanKind.CONDITIONAL,
|
|
290
|
-
condition: {
|
|
291
|
-
"operator": "lte",
|
|
292
|
-
"operands": [
|
|
293
|
-
{
|
|
294
|
-
"name": "request.resource.attr.amount"
|
|
295
|
-
},
|
|
296
|
-
{
|
|
297
|
-
"value": 1000
|
|
298
|
-
}
|
|
299
|
-
]
|
|
300
|
-
},
|
|
301
|
-
};
|
|
302
|
-
const result = (0, _1.default)({
|
|
303
|
-
queryPlan,
|
|
304
|
-
fieldNameMapper: {
|
|
305
|
-
"request.resource.attr.amount": "amount",
|
|
306
|
-
},
|
|
307
|
-
});
|
|
308
|
-
expect(result).toStrictEqual({
|
|
309
|
-
amount: { lte: 1000 }
|
|
310
|
-
});
|
|
311
|
-
});
|
package/lib/index.test.d.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
package/lib/index.test.js
DELETED
|
@@ -1,306 +0,0 @@
|
|
|
1
|
-
import queryPlanToPrisma from ".";
|
|
2
|
-
import { PlanKind } from "@cerbos/core";
|
|
3
|
-
test("always allowed", () => {
|
|
4
|
-
const queryPlan = {
|
|
5
|
-
requestId: "",
|
|
6
|
-
metadata: undefined,
|
|
7
|
-
kind: PlanKind.ALWAYS_ALLOWED,
|
|
8
|
-
};
|
|
9
|
-
const result = queryPlanToPrisma({
|
|
10
|
-
queryPlan,
|
|
11
|
-
fieldNameMapper: {
|
|
12
|
-
"request.resource.attr.ownerId": "ownerId",
|
|
13
|
-
},
|
|
14
|
-
});
|
|
15
|
-
expect(result).toStrictEqual({});
|
|
16
|
-
});
|
|
17
|
-
test("always denied", () => {
|
|
18
|
-
const queryPlan = {
|
|
19
|
-
requestId: "",
|
|
20
|
-
metadata: undefined,
|
|
21
|
-
kind: PlanKind.ALWAYS_DENIED,
|
|
22
|
-
};
|
|
23
|
-
const result = queryPlanToPrisma({
|
|
24
|
-
queryPlan,
|
|
25
|
-
fieldNameMapper: {
|
|
26
|
-
"request.resource.attr.ownerId": "ownerId",
|
|
27
|
-
},
|
|
28
|
-
});
|
|
29
|
-
expect(result).toStrictEqual({ "1": { "equals": 0 } });
|
|
30
|
-
});
|
|
31
|
-
test("conditional - eq", () => {
|
|
32
|
-
const queryPlan = {
|
|
33
|
-
requestId: "",
|
|
34
|
-
metadata: undefined,
|
|
35
|
-
kind: PlanKind.CONDITIONAL,
|
|
36
|
-
condition: {
|
|
37
|
-
operator: "eq",
|
|
38
|
-
operands: [{ name: "request.resource.attr.ownerId" }, { value: "sally" }],
|
|
39
|
-
},
|
|
40
|
-
};
|
|
41
|
-
const result = queryPlanToPrisma({
|
|
42
|
-
queryPlan,
|
|
43
|
-
fieldNameMapper: {
|
|
44
|
-
"request.resource.attr.ownerId": "ownerId",
|
|
45
|
-
},
|
|
46
|
-
});
|
|
47
|
-
expect(result).toStrictEqual({ ownerId: { equals: "sally" } });
|
|
48
|
-
});
|
|
49
|
-
test("conditional - eq - inverted order", () => {
|
|
50
|
-
const queryPlan = {
|
|
51
|
-
requestId: "",
|
|
52
|
-
metadata: undefined,
|
|
53
|
-
kind: PlanKind.CONDITIONAL,
|
|
54
|
-
condition: {
|
|
55
|
-
operator: "eq",
|
|
56
|
-
operands: [
|
|
57
|
-
{ value: "sally" },
|
|
58
|
-
{ name: "request.resource.attr.ownerId" }
|
|
59
|
-
],
|
|
60
|
-
},
|
|
61
|
-
};
|
|
62
|
-
const result = queryPlanToPrisma({
|
|
63
|
-
queryPlan,
|
|
64
|
-
fieldNameMapper: {
|
|
65
|
-
"request.resource.attr.ownerId": "ownerId",
|
|
66
|
-
},
|
|
67
|
-
});
|
|
68
|
-
expect(result).toStrictEqual({ ownerId: { equals: "sally" } });
|
|
69
|
-
});
|
|
70
|
-
test("conditional - and", () => {
|
|
71
|
-
const queryPlan = {
|
|
72
|
-
requestId: "",
|
|
73
|
-
metadata: undefined,
|
|
74
|
-
kind: PlanKind.CONDITIONAL,
|
|
75
|
-
condition: {
|
|
76
|
-
"operator": "and",
|
|
77
|
-
"operands": [
|
|
78
|
-
{
|
|
79
|
-
"operator": "eq",
|
|
80
|
-
"operands": [
|
|
81
|
-
{
|
|
82
|
-
"name": "request.resource.attr.department"
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
"value": "marketing"
|
|
86
|
-
}
|
|
87
|
-
]
|
|
88
|
-
},
|
|
89
|
-
{
|
|
90
|
-
"operator": "ne",
|
|
91
|
-
"operands": [
|
|
92
|
-
{
|
|
93
|
-
"name": "request.resource.attr.team"
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
"value": "design"
|
|
97
|
-
}
|
|
98
|
-
]
|
|
99
|
-
}
|
|
100
|
-
]
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
const result = queryPlanToPrisma({
|
|
104
|
-
queryPlan,
|
|
105
|
-
fieldNameMapper: {
|
|
106
|
-
"request.resource.attr.department": "department",
|
|
107
|
-
"request.resource.attr.team": "team",
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
expect(result).toStrictEqual({
|
|
111
|
-
AND: [
|
|
112
|
-
{
|
|
113
|
-
department: { equals: "marketing" }
|
|
114
|
-
},
|
|
115
|
-
{
|
|
116
|
-
team: { not: "design" }
|
|
117
|
-
}
|
|
118
|
-
]
|
|
119
|
-
});
|
|
120
|
-
});
|
|
121
|
-
test("conditional - or", () => {
|
|
122
|
-
const queryPlan = {
|
|
123
|
-
requestId: "",
|
|
124
|
-
metadata: undefined,
|
|
125
|
-
kind: PlanKind.CONDITIONAL,
|
|
126
|
-
condition: {
|
|
127
|
-
"operator": "or",
|
|
128
|
-
"operands": [
|
|
129
|
-
{
|
|
130
|
-
"operator": "eq",
|
|
131
|
-
"operands": [
|
|
132
|
-
{
|
|
133
|
-
"name": "request.resource.attr.department"
|
|
134
|
-
},
|
|
135
|
-
{
|
|
136
|
-
"value": "marketing"
|
|
137
|
-
}
|
|
138
|
-
]
|
|
139
|
-
},
|
|
140
|
-
{
|
|
141
|
-
"operator": "ne",
|
|
142
|
-
"operands": [
|
|
143
|
-
{
|
|
144
|
-
"name": "request.resource.attr.team"
|
|
145
|
-
},
|
|
146
|
-
{
|
|
147
|
-
"value": "design"
|
|
148
|
-
}
|
|
149
|
-
]
|
|
150
|
-
}
|
|
151
|
-
]
|
|
152
|
-
},
|
|
153
|
-
};
|
|
154
|
-
const result = queryPlanToPrisma({
|
|
155
|
-
queryPlan,
|
|
156
|
-
fieldNameMapper: {
|
|
157
|
-
"request.resource.attr.department": "department",
|
|
158
|
-
"request.resource.attr.team": "team",
|
|
159
|
-
},
|
|
160
|
-
});
|
|
161
|
-
expect(result).toStrictEqual({
|
|
162
|
-
OR: [
|
|
163
|
-
{
|
|
164
|
-
department: { equals: "marketing" }
|
|
165
|
-
},
|
|
166
|
-
{
|
|
167
|
-
team: { not: "design" }
|
|
168
|
-
}
|
|
169
|
-
]
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
test("conditional - in", () => {
|
|
173
|
-
const queryPlan = {
|
|
174
|
-
requestId: "",
|
|
175
|
-
metadata: undefined,
|
|
176
|
-
kind: PlanKind.CONDITIONAL,
|
|
177
|
-
condition: {
|
|
178
|
-
"operator": "in",
|
|
179
|
-
"operands": [
|
|
180
|
-
{
|
|
181
|
-
"name": "request.resource.attr.department"
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
"value": ["marketing", "design"]
|
|
185
|
-
}
|
|
186
|
-
]
|
|
187
|
-
},
|
|
188
|
-
};
|
|
189
|
-
const result = queryPlanToPrisma({
|
|
190
|
-
queryPlan,
|
|
191
|
-
fieldNameMapper: {
|
|
192
|
-
"request.resource.attr.department": "department",
|
|
193
|
-
},
|
|
194
|
-
});
|
|
195
|
-
expect(result).toStrictEqual({
|
|
196
|
-
department: { in: ["marketing", "design"] }
|
|
197
|
-
});
|
|
198
|
-
});
|
|
199
|
-
test("conditional - gt", () => {
|
|
200
|
-
const queryPlan = {
|
|
201
|
-
requestId: "",
|
|
202
|
-
metadata: undefined,
|
|
203
|
-
kind: PlanKind.CONDITIONAL,
|
|
204
|
-
condition: {
|
|
205
|
-
"operator": "gt",
|
|
206
|
-
"operands": [
|
|
207
|
-
{
|
|
208
|
-
"name": "request.resource.attr.amount"
|
|
209
|
-
},
|
|
210
|
-
{
|
|
211
|
-
"value": 1000
|
|
212
|
-
}
|
|
213
|
-
]
|
|
214
|
-
},
|
|
215
|
-
};
|
|
216
|
-
const result = queryPlanToPrisma({
|
|
217
|
-
queryPlan,
|
|
218
|
-
fieldNameMapper: {
|
|
219
|
-
"request.resource.attr.amount": "amount",
|
|
220
|
-
},
|
|
221
|
-
});
|
|
222
|
-
expect(result).toStrictEqual({
|
|
223
|
-
amount: { gt: 1000 }
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
test("conditional - lt", () => {
|
|
227
|
-
const queryPlan = {
|
|
228
|
-
requestId: "",
|
|
229
|
-
metadata: undefined,
|
|
230
|
-
kind: PlanKind.CONDITIONAL,
|
|
231
|
-
condition: {
|
|
232
|
-
"operator": "lt",
|
|
233
|
-
"operands": [
|
|
234
|
-
{
|
|
235
|
-
"name": "request.resource.attr.amount"
|
|
236
|
-
},
|
|
237
|
-
{
|
|
238
|
-
"value": 1000
|
|
239
|
-
}
|
|
240
|
-
]
|
|
241
|
-
},
|
|
242
|
-
};
|
|
243
|
-
const result = queryPlanToPrisma({
|
|
244
|
-
queryPlan,
|
|
245
|
-
fieldNameMapper: {
|
|
246
|
-
"request.resource.attr.amount": "amount",
|
|
247
|
-
},
|
|
248
|
-
});
|
|
249
|
-
expect(result).toStrictEqual({
|
|
250
|
-
amount: { lt: 1000 }
|
|
251
|
-
});
|
|
252
|
-
});
|
|
253
|
-
test("conditional - gte", () => {
|
|
254
|
-
const queryPlan = {
|
|
255
|
-
requestId: "",
|
|
256
|
-
metadata: undefined,
|
|
257
|
-
kind: PlanKind.CONDITIONAL,
|
|
258
|
-
condition: {
|
|
259
|
-
"operator": "gte",
|
|
260
|
-
"operands": [
|
|
261
|
-
{
|
|
262
|
-
"name": "request.resource.attr.amount"
|
|
263
|
-
},
|
|
264
|
-
{
|
|
265
|
-
"value": 1000
|
|
266
|
-
}
|
|
267
|
-
]
|
|
268
|
-
},
|
|
269
|
-
};
|
|
270
|
-
const result = queryPlanToPrisma({
|
|
271
|
-
queryPlan,
|
|
272
|
-
fieldNameMapper: {
|
|
273
|
-
"request.resource.attr.amount": "amount",
|
|
274
|
-
},
|
|
275
|
-
});
|
|
276
|
-
expect(result).toStrictEqual({
|
|
277
|
-
amount: { gte: 1000 }
|
|
278
|
-
});
|
|
279
|
-
});
|
|
280
|
-
test("conditional - lte", () => {
|
|
281
|
-
const queryPlan = {
|
|
282
|
-
requestId: "",
|
|
283
|
-
metadata: undefined,
|
|
284
|
-
kind: PlanKind.CONDITIONAL,
|
|
285
|
-
condition: {
|
|
286
|
-
"operator": "lte",
|
|
287
|
-
"operands": [
|
|
288
|
-
{
|
|
289
|
-
"name": "request.resource.attr.amount"
|
|
290
|
-
},
|
|
291
|
-
{
|
|
292
|
-
"value": 1000
|
|
293
|
-
}
|
|
294
|
-
]
|
|
295
|
-
},
|
|
296
|
-
};
|
|
297
|
-
const result = queryPlanToPrisma({
|
|
298
|
-
queryPlan,
|
|
299
|
-
fieldNameMapper: {
|
|
300
|
-
"request.resource.attr.amount": "amount",
|
|
301
|
-
},
|
|
302
|
-
});
|
|
303
|
-
expect(result).toStrictEqual({
|
|
304
|
-
amount: { lte: 1000 }
|
|
305
|
-
});
|
|
306
|
-
});
|