@cerbos/orm-prisma 1.0.0-alpha.0 → 1.1.0-alpha.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/README.md CHANGED
@@ -12,8 +12,8 @@ Not Supported:
12
12
  - `mode`
13
13
  - `startsWith`
14
14
  - `endsWith`
15
+ - `isSet`
15
16
  - Scalar filters
16
- - Composite type filters
17
17
  - Atomic number operations
18
18
  - Composite keys
19
19
 
@@ -20,5 +20,5 @@ interface QueryPlanToPrismaResult {
20
20
  kind: PlanKind;
21
21
  filters?: any;
22
22
  }
23
- export declare function queryPlanToPrisma({ queryPlan, fieldNameMapper, relationMapper }: QueryPlanToPrismaArgs): QueryPlanToPrismaResult;
23
+ export declare function queryPlanToPrisma({ queryPlan, fieldNameMapper, relationMapper, }: QueryPlanToPrismaArgs): QueryPlanToPrismaResult;
24
24
  export {};
package/lib/cjs/index.js CHANGED
@@ -3,35 +3,45 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.queryPlanToPrisma = exports.PlanKind = void 0;
4
4
  const core_1 = require("@cerbos/core");
5
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
- }, {})
6
+ function queryPlanToPrisma({ queryPlan, fieldNameMapper, relationMapper = {}, }) {
7
+ const toFieldName = (key) => {
8
+ if (typeof fieldNameMapper === "function") {
9
+ return fieldNameMapper(key);
10
+ }
11
+ else if (fieldNameMapper[key]) {
12
+ return fieldNameMapper[key];
13
+ }
14
+ else {
15
+ return key;
16
+ }
17
+ };
18
+ const toRelationName = (key) => {
19
+ if (typeof relationMapper === "function") {
20
+ return relationMapper(key);
21
+ }
22
+ else if (relationMapper[key]) {
23
+ return relationMapper[key];
24
+ }
25
+ return null;
34
26
  };
27
+ switch (queryPlan.kind) {
28
+ case exports.PlanKind.ALWAYS_ALLOWED:
29
+ return {
30
+ kind: exports.PlanKind.ALWAYS_ALLOWED,
31
+ filters: {},
32
+ };
33
+ case exports.PlanKind.ALWAYS_DENIED:
34
+ return {
35
+ kind: exports.PlanKind.ALWAYS_DENIED,
36
+ };
37
+ case exports.PlanKind.CONDITIONAL:
38
+ return {
39
+ kind: exports.PlanKind.CONDITIONAL,
40
+ filters: mapOperand(queryPlan.condition, toFieldName, toRelationName),
41
+ };
42
+ default:
43
+ throw Error(`Invalid query plan.`);
44
+ }
35
45
  }
36
46
  exports.queryPlanToPrisma = queryPlanToPrisma;
37
47
  function isExpression(e) {
@@ -43,102 +53,117 @@ function isValue(e) {
43
53
  function isVariable(e) {
44
54
  return e.variable !== undefined;
45
55
  }
46
- function getOperandName(operands) {
47
- const op = operands.find(o => o.hasOwnProperty("name"));
56
+ function getOperandVariable(operands) {
57
+ const op = operands.find((o) => o.hasOwnProperty("name"));
48
58
  if (!op)
49
59
  return;
50
60
  return op.name;
51
61
  }
52
62
  function getOperandValue(operands) {
53
- const op = operands.find(o => o.hasOwnProperty("value"));
63
+ const op = operands.find((o) => isValue(o));
54
64
  if (!op)
55
65
  return;
56
66
  return op.value;
57
67
  }
68
+ const OPERATORS = {
69
+ eq: {
70
+ relationalCondition: "is",
71
+ fieldCondition: "equals",
72
+ },
73
+ ne: {
74
+ relationalCondition: "isNot",
75
+ fieldCondition: "not",
76
+ },
77
+ in: {
78
+ relationalCondition: "some",
79
+ fieldCondition: "in",
80
+ },
81
+ lt: {
82
+ fieldCondition: "lt",
83
+ },
84
+ gt: {
85
+ fieldCondition: "gt",
86
+ },
87
+ le: {
88
+ fieldCondition: "lte",
89
+ },
90
+ ge: {
91
+ fieldCondition: "gte",
92
+ },
93
+ };
58
94
  function mapOperand(operand, getFieldName, getRelationName, output = {}) {
59
- if (isExpression(operand)) {
60
- const { operator, operands } = operand;
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
- }
86
- };
87
- }
88
- else if (fieldName) {
89
- output[fieldName] = {
90
- equals: opValue,
91
- };
92
- }
93
- }
94
- if (operator == "ne") {
95
- if (relation) {
96
- output[relation.relation] = {
97
- isNot: {
98
- [relation.field]: opValue
99
- }
100
- };
101
- }
102
- else if (fieldName) {
103
- output[fieldName] = {
104
- not: opValue,
105
- };
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
- };
95
+ if (!isExpression(operand))
96
+ throw Error(`Query plan did not contain an expression for operand ${operand}`);
97
+ const { operator, operands } = operand;
98
+ // HANDLE NESTING OPERATIONS: AND/OR/NOT
99
+ if (operator == "and") {
100
+ if (operands.length < 2)
101
+ throw Error("Expected atleast 2 operands");
102
+ output.AND = operands.map((o) => mapOperand(o, getFieldName, getRelationName, {}));
103
+ return output;
104
+ }
105
+ if (operator == "or") {
106
+ if (operands.length < 2)
107
+ throw Error("Expected atleast 2 operands");
108
+ output.OR = operands.map((o) => mapOperand(o, getFieldName, getRelationName, {}));
109
+ return output;
110
+ }
111
+ if (operator == "not") {
112
+ if (operands.length > 1)
113
+ throw Error("Expected only one operand");
114
+ output.NOT = operands.map((o) => mapOperand(o, getFieldName, getRelationName, {}))[0];
115
+ return output;
116
+ }
117
+ // get the operation parameters
118
+ const operation = OPERATORS[operator];
119
+ if (!operation)
120
+ throw Error(`Unsupported operator ${operator}`);
121
+ const opVariable = getOperandVariable(operands);
122
+ if (!opVariable)
123
+ throw Error(`Unexpected variable ${operands}`);
124
+ const opValue = getOperandValue(operands);
125
+ const relation = getRelationName(opVariable);
126
+ const fieldName = getFieldName(opVariable);
127
+ // There is a relational mapper for this variable
128
+ if (relation && operation.relationalCondition) {
129
+ output[relation.relation] = {
130
+ [operation.relationalCondition]: {
131
+ [relation.field]: opValue,
132
+ },
133
+ };
134
+ return output;
135
+ }
136
+ else if (fieldName && operation.fieldCondition) {
137
+ // There is a field mapper for this variable
138
+ const fieldPath = fieldName.split(".");
139
+ if (fieldPath.length > 1) {
140
+ output[fieldPath[0]] = convertPathToJSON(fieldPath.splice(1), {
141
+ [operation.fieldCondition]: opValue,
142
+ });
117
143
  }
118
- if (operator == "le" && fieldName) {
119
- output[fieldName] = {
120
- lte: opValue,
144
+ else {
145
+ output[fieldPath[0]] = {
146
+ [operation.fieldCondition]: opValue,
121
147
  };
122
148
  }
123
- if (operator == "ge" && fieldName) {
124
- output[fieldName] = {
125
- gte: opValue,
126
- };
149
+ return output;
150
+ }
151
+ else {
152
+ throw Error("Failed to map");
153
+ }
154
+ }
155
+ function convertPathToJSON(segments, value) {
156
+ const result = {};
157
+ let current = result;
158
+ for (let i = 0; i < segments.length; i++) {
159
+ const segment = segments[i];
160
+ if (i === segments.length - 1) {
161
+ current[segment] = value;
127
162
  }
128
- if (operator == "in") {
129
- if (relation) {
130
- output[relation.relation] = {
131
- some: {
132
- [relation.field]: opValue
133
- }
134
- };
135
- }
136
- else if (fieldName) {
137
- output[fieldName] = {
138
- in: opValue,
139
- };
140
- }
163
+ else {
164
+ current[segment] = {};
165
+ current = current[segment];
141
166
  }
142
167
  }
143
- return output;
168
+ return result;
144
169
  }
package/lib/index.d.ts CHANGED
@@ -20,5 +20,5 @@ interface QueryPlanToPrismaResult {
20
20
  kind: PlanKind;
21
21
  filters?: any;
22
22
  }
23
- export declare function queryPlanToPrisma({ queryPlan, fieldNameMapper, relationMapper }: QueryPlanToPrismaArgs): QueryPlanToPrismaResult;
23
+ export declare function queryPlanToPrisma({ queryPlan, fieldNameMapper, relationMapper, }: QueryPlanToPrismaArgs): QueryPlanToPrismaResult;
24
24
  export {};
package/lib/index.js CHANGED
@@ -1,34 +1,44 @@
1
- import { PlanKind as PK } from "@cerbos/core";
1
+ import { PlanKind as PK, } from "@cerbos/core";
2
2
  export const PlanKind = PK;
3
- export function queryPlanToPrisma({ queryPlan, fieldNameMapper, relationMapper = {} }) {
4
- if (queryPlan.kind === PlanKind.ALWAYS_ALLOWED)
5
- return {
6
- kind: PlanKind.ALWAYS_ALLOWED,
7
- filters: {}
8
- };
9
- if (queryPlan.kind === PlanKind.ALWAYS_DENIED)
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
- }, {})
3
+ export function queryPlanToPrisma({ queryPlan, fieldNameMapper, relationMapper = {}, }) {
4
+ const toFieldName = (key) => {
5
+ if (typeof fieldNameMapper === "function") {
6
+ return fieldNameMapper(key);
7
+ }
8
+ else if (fieldNameMapper[key]) {
9
+ return fieldNameMapper[key];
10
+ }
11
+ else {
12
+ return key;
13
+ }
14
+ };
15
+ const toRelationName = (key) => {
16
+ if (typeof relationMapper === "function") {
17
+ return relationMapper(key);
18
+ }
19
+ else if (relationMapper[key]) {
20
+ return relationMapper[key];
21
+ }
22
+ return null;
31
23
  };
24
+ switch (queryPlan.kind) {
25
+ case PlanKind.ALWAYS_ALLOWED:
26
+ return {
27
+ kind: PlanKind.ALWAYS_ALLOWED,
28
+ filters: {},
29
+ };
30
+ case PlanKind.ALWAYS_DENIED:
31
+ return {
32
+ kind: PlanKind.ALWAYS_DENIED,
33
+ };
34
+ case PlanKind.CONDITIONAL:
35
+ return {
36
+ kind: PlanKind.CONDITIONAL,
37
+ filters: mapOperand(queryPlan.condition, toFieldName, toRelationName),
38
+ };
39
+ default:
40
+ throw Error(`Invalid query plan.`);
41
+ }
32
42
  }
33
43
  function isExpression(e) {
34
44
  return e.operator !== undefined;
@@ -39,102 +49,117 @@ function isValue(e) {
39
49
  function isVariable(e) {
40
50
  return e.variable !== undefined;
41
51
  }
42
- function getOperandName(operands) {
43
- const op = operands.find(o => o.hasOwnProperty("name"));
52
+ function getOperandVariable(operands) {
53
+ const op = operands.find((o) => o.hasOwnProperty("name"));
44
54
  if (!op)
45
55
  return;
46
56
  return op.name;
47
57
  }
48
58
  function getOperandValue(operands) {
49
- const op = operands.find(o => o.hasOwnProperty("value"));
59
+ const op = operands.find((o) => isValue(o));
50
60
  if (!op)
51
61
  return;
52
62
  return op.value;
53
63
  }
64
+ const OPERATORS = {
65
+ eq: {
66
+ relationalCondition: "is",
67
+ fieldCondition: "equals",
68
+ },
69
+ ne: {
70
+ relationalCondition: "isNot",
71
+ fieldCondition: "not",
72
+ },
73
+ in: {
74
+ relationalCondition: "some",
75
+ fieldCondition: "in",
76
+ },
77
+ lt: {
78
+ fieldCondition: "lt",
79
+ },
80
+ gt: {
81
+ fieldCondition: "gt",
82
+ },
83
+ le: {
84
+ fieldCondition: "lte",
85
+ },
86
+ ge: {
87
+ fieldCondition: "gte",
88
+ },
89
+ };
54
90
  function mapOperand(operand, getFieldName, getRelationName, output = {}) {
55
- if (isExpression(operand)) {
56
- const { operator, operands } = operand;
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
- }
82
- };
83
- }
84
- else if (fieldName) {
85
- output[fieldName] = {
86
- equals: opValue,
87
- };
88
- }
89
- }
90
- if (operator == "ne") {
91
- if (relation) {
92
- output[relation.relation] = {
93
- isNot: {
94
- [relation.field]: opValue
95
- }
96
- };
97
- }
98
- else if (fieldName) {
99
- output[fieldName] = {
100
- not: opValue,
101
- };
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
- };
91
+ if (!isExpression(operand))
92
+ throw Error(`Query plan did not contain an expression for operand ${operand}`);
93
+ const { operator, operands } = operand;
94
+ // HANDLE NESTING OPERATIONS: AND/OR/NOT
95
+ if (operator == "and") {
96
+ if (operands.length < 2)
97
+ throw Error("Expected atleast 2 operands");
98
+ output.AND = operands.map((o) => mapOperand(o, getFieldName, getRelationName, {}));
99
+ return output;
100
+ }
101
+ if (operator == "or") {
102
+ if (operands.length < 2)
103
+ throw Error("Expected atleast 2 operands");
104
+ output.OR = operands.map((o) => mapOperand(o, getFieldName, getRelationName, {}));
105
+ return output;
106
+ }
107
+ if (operator == "not") {
108
+ if (operands.length > 1)
109
+ throw Error("Expected only one operand");
110
+ output.NOT = operands.map((o) => mapOperand(o, getFieldName, getRelationName, {}))[0];
111
+ return output;
112
+ }
113
+ // get the operation parameters
114
+ const operation = OPERATORS[operator];
115
+ if (!operation)
116
+ throw Error(`Unsupported operator ${operator}`);
117
+ const opVariable = getOperandVariable(operands);
118
+ if (!opVariable)
119
+ throw Error(`Unexpected variable ${operands}`);
120
+ const opValue = getOperandValue(operands);
121
+ const relation = getRelationName(opVariable);
122
+ const fieldName = getFieldName(opVariable);
123
+ // There is a relational mapper for this variable
124
+ if (relation && operation.relationalCondition) {
125
+ output[relation.relation] = {
126
+ [operation.relationalCondition]: {
127
+ [relation.field]: opValue,
128
+ },
129
+ };
130
+ return output;
131
+ }
132
+ else if (fieldName && operation.fieldCondition) {
133
+ // There is a field mapper for this variable
134
+ const fieldPath = fieldName.split(".");
135
+ if (fieldPath.length > 1) {
136
+ output[fieldPath[0]] = convertPathToJSON(fieldPath.splice(1), {
137
+ [operation.fieldCondition]: opValue,
138
+ });
113
139
  }
114
- if (operator == "le" && fieldName) {
115
- output[fieldName] = {
116
- lte: opValue,
140
+ else {
141
+ output[fieldPath[0]] = {
142
+ [operation.fieldCondition]: opValue,
117
143
  };
118
144
  }
119
- if (operator == "ge" && fieldName) {
120
- output[fieldName] = {
121
- gte: opValue,
122
- };
145
+ return output;
146
+ }
147
+ else {
148
+ throw Error("Failed to map");
149
+ }
150
+ }
151
+ function convertPathToJSON(segments, value) {
152
+ const result = {};
153
+ let current = result;
154
+ for (let i = 0; i < segments.length; i++) {
155
+ const segment = segments[i];
156
+ if (i === segments.length - 1) {
157
+ current[segment] = value;
123
158
  }
124
- if (operator == "in") {
125
- if (relation) {
126
- output[relation.relation] = {
127
- some: {
128
- [relation.field]: opValue
129
- }
130
- };
131
- }
132
- else if (fieldName) {
133
- output[fieldName] = {
134
- in: opValue,
135
- };
136
- }
159
+ else {
160
+ current[segment] = {};
161
+ current = current[segment];
137
162
  }
138
163
  }
139
- return output;
164
+ return result;
140
165
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cerbos/orm-prisma",
3
- "version": "1.0.0-alpha.0",
3
+ "version": "1.1.0-alpha.1",
4
4
  "homepage": "https://cerbos.dev",
5
5
  "description": "",
6
6
  "private": false,
@@ -33,4 +33,4 @@
33
33
  "@cerbos/core": "^0.5.1",
34
34
  "@cerbos/grpc": "^0.8.0"
35
35
  }
36
- }
36
+ }