@zenstackhq/runtime 2.15.1 → 3.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/LICENSE +1 -1
- package/dist/client.cjs +6094 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +19 -0
- package/dist/client.d.ts +19 -0
- package/dist/client.js +6060 -0
- package/dist/client.js.map +1 -0
- package/dist/contract-DguafRNB.d.cts +1272 -0
- package/dist/contract-DguafRNB.d.ts +1272 -0
- package/dist/index.cjs +6088 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +14 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.js +6057 -0
- package/dist/index.js.map +1 -0
- package/dist/plugins/policy.cjs +2343 -0
- package/dist/plugins/policy.cjs.map +1 -0
- package/dist/plugins/policy.d.cts +24 -0
- package/dist/plugins/policy.d.ts +24 -0
- package/dist/plugins/policy.js +2307 -0
- package/dist/plugins/policy.js.map +1 -0
- package/dist/schema.cjs +110 -0
- package/dist/schema.cjs.map +1 -0
- package/dist/schema.d.cts +29 -0
- package/dist/schema.d.ts +29 -0
- package/dist/schema.js +85 -0
- package/dist/schema.js.map +1 -0
- package/dist/utils/pg-utils.cjs +39 -0
- package/dist/utils/pg-utils.cjs.map +1 -0
- package/dist/utils/pg-utils.d.cts +8 -0
- package/dist/utils/pg-utils.d.ts +8 -0
- package/dist/utils/pg-utils.js +16 -0
- package/dist/utils/pg-utils.js.map +1 -0
- package/{browser/index.js → dist/utils/sqlite-utils.cjs} +21 -37
- package/dist/utils/sqlite-utils.cjs.map +1 -0
- package/dist/utils/sqlite-utils.d.cts +8 -0
- package/dist/utils/sqlite-utils.d.ts +8 -0
- package/dist/utils/sqlite-utils.js +22 -0
- package/dist/utils/sqlite-utils.js.map +1 -0
- package/package.json +105 -114
- package/README.md +0 -5
- package/browser/index.d.mts +0 -13
- package/browser/index.d.ts +0 -13
- package/browser/index.js.map +0 -1
- package/browser/index.mjs +0 -33
- package/browser/index.mjs.map +0 -1
- package/constants.d.ts +0 -62
- package/constants.js +0 -76
- package/constants.js.map +0 -1
- package/cross/index.d.mts +0 -379
- package/cross/index.d.ts +0 -379
- package/cross/index.js +0 -903
- package/cross/index.js.map +0 -1
- package/cross/index.mjs +0 -861
- package/cross/index.mjs.map +0 -1
- package/edge.d.ts +0 -1
- package/edge.js +0 -18
- package/edge.js.map +0 -1
- package/encryption/index.d.ts +0 -25
- package/encryption/index.js +0 -74
- package/encryption/index.js.map +0 -1
- package/encryption/utils.d.ts +0 -9
- package/encryption/utils.js +0 -99
- package/encryption/utils.js.map +0 -1
- package/enhance-edge.d.ts +0 -1
- package/enhance-edge.js +0 -10
- package/enhance.d.ts +0 -1
- package/enhance.js +0 -10
- package/enhancements/edge/create-enhancement.d.ts +0 -42
- package/enhancements/edge/create-enhancement.js +0 -102
- package/enhancements/edge/create-enhancement.js.map +0 -1
- package/enhancements/edge/default-auth.d.ts +0 -8
- package/enhancements/edge/default-auth.js +0 -180
- package/enhancements/edge/default-auth.js.map +0 -1
- package/enhancements/edge/delegate.d.ts +0 -77
- package/enhancements/edge/delegate.js +0 -1294
- package/enhancements/edge/delegate.js.map +0 -1
- package/enhancements/edge/encryption.d.ts +0 -7
- package/enhancements/edge/encryption.js +0 -150
- package/enhancements/edge/encryption.js.map +0 -1
- package/enhancements/edge/index.d.ts +0 -4
- package/enhancements/edge/index.js +0 -21
- package/enhancements/edge/index.js.map +0 -1
- package/enhancements/edge/json-processor.d.ts +0 -7
- package/enhancements/edge/json-processor.js +0 -89
- package/enhancements/edge/json-processor.js.map +0 -1
- package/enhancements/edge/logger.d.ts +0 -29
- package/enhancements/edge/logger.js +0 -65
- package/enhancements/edge/logger.js.map +0 -1
- package/enhancements/edge/omit.d.ts +0 -7
- package/enhancements/edge/omit.js +0 -96
- package/enhancements/edge/omit.js.map +0 -1
- package/enhancements/edge/password.d.ts +0 -7
- package/enhancements/edge/password.js +0 -64
- package/enhancements/edge/password.js.map +0 -1
- package/enhancements/edge/policy/check-utils.d.ts +0 -5
- package/enhancements/edge/policy/check-utils.js +0 -20
- package/enhancements/edge/policy/check-utils.js.map +0 -1
- package/enhancements/edge/policy/handler.d.ts +0 -100
- package/enhancements/edge/policy/handler.js +0 -1442
- package/enhancements/edge/policy/handler.js.map +0 -1
- package/enhancements/edge/policy/index.d.ts +0 -19
- package/enhancements/edge/policy/index.js +0 -65
- package/enhancements/edge/policy/index.js.map +0 -1
- package/enhancements/edge/policy/policy-utils.d.ts +0 -181
- package/enhancements/edge/policy/policy-utils.js +0 -1357
- package/enhancements/edge/policy/policy-utils.js.map +0 -1
- package/enhancements/edge/promise.d.ts +0 -15
- package/enhancements/edge/promise.js +0 -99
- package/enhancements/edge/promise.js.map +0 -1
- package/enhancements/edge/proxy.d.ts +0 -120
- package/enhancements/edge/proxy.js +0 -287
- package/enhancements/edge/proxy.js.map +0 -1
- package/enhancements/edge/query-utils.d.ts +0 -53
- package/enhancements/edge/query-utils.js +0 -256
- package/enhancements/edge/query-utils.js.map +0 -1
- package/enhancements/edge/types.d.ts +0 -238
- package/enhancements/edge/types.js +0 -3
- package/enhancements/edge/types.js.map +0 -1
- package/enhancements/edge/utils.d.ts +0 -11
- package/enhancements/edge/utils.js +0 -49
- package/enhancements/edge/utils.js.map +0 -1
- package/enhancements/edge/where-visitor.d.ts +0 -32
- package/enhancements/edge/where-visitor.js +0 -86
- package/enhancements/edge/where-visitor.js.map +0 -1
- package/enhancements/node/create-enhancement.d.ts +0 -42
- package/enhancements/node/create-enhancement.js +0 -102
- package/enhancements/node/create-enhancement.js.map +0 -1
- package/enhancements/node/default-auth.d.ts +0 -8
- package/enhancements/node/default-auth.js +0 -180
- package/enhancements/node/default-auth.js.map +0 -1
- package/enhancements/node/delegate.d.ts +0 -77
- package/enhancements/node/delegate.js +0 -1294
- package/enhancements/node/delegate.js.map +0 -1
- package/enhancements/node/encryption.d.ts +0 -7
- package/enhancements/node/encryption.js +0 -150
- package/enhancements/node/encryption.js.map +0 -1
- package/enhancements/node/index.d.ts +0 -4
- package/enhancements/node/index.js +0 -21
- package/enhancements/node/index.js.map +0 -1
- package/enhancements/node/json-processor.d.ts +0 -7
- package/enhancements/node/json-processor.js +0 -89
- package/enhancements/node/json-processor.js.map +0 -1
- package/enhancements/node/logger.d.ts +0 -29
- package/enhancements/node/logger.js +0 -65
- package/enhancements/node/logger.js.map +0 -1
- package/enhancements/node/omit.d.ts +0 -7
- package/enhancements/node/omit.js +0 -96
- package/enhancements/node/omit.js.map +0 -1
- package/enhancements/node/password.d.ts +0 -7
- package/enhancements/node/password.js +0 -64
- package/enhancements/node/password.js.map +0 -1
- package/enhancements/node/policy/check-utils.d.ts +0 -5
- package/enhancements/node/policy/check-utils.js +0 -87
- package/enhancements/node/policy/check-utils.js.map +0 -1
- package/enhancements/node/policy/constraint-solver.d.ts +0 -27
- package/enhancements/node/policy/constraint-solver.js +0 -164
- package/enhancements/node/policy/constraint-solver.js.map +0 -1
- package/enhancements/node/policy/handler.d.ts +0 -100
- package/enhancements/node/policy/handler.js +0 -1442
- package/enhancements/node/policy/handler.js.map +0 -1
- package/enhancements/node/policy/index.d.ts +0 -19
- package/enhancements/node/policy/index.js +0 -65
- package/enhancements/node/policy/index.js.map +0 -1
- package/enhancements/node/policy/policy-utils.d.ts +0 -181
- package/enhancements/node/policy/policy-utils.js +0 -1357
- package/enhancements/node/policy/policy-utils.js.map +0 -1
- package/enhancements/node/promise.d.ts +0 -15
- package/enhancements/node/promise.js +0 -99
- package/enhancements/node/promise.js.map +0 -1
- package/enhancements/node/proxy.d.ts +0 -120
- package/enhancements/node/proxy.js +0 -287
- package/enhancements/node/proxy.js.map +0 -1
- package/enhancements/node/query-utils.d.ts +0 -53
- package/enhancements/node/query-utils.js +0 -256
- package/enhancements/node/query-utils.js.map +0 -1
- package/enhancements/node/types.d.ts +0 -238
- package/enhancements/node/types.js +0 -3
- package/enhancements/node/types.js.map +0 -1
- package/enhancements/node/utils.d.ts +0 -11
- package/enhancements/node/utils.js +0 -49
- package/enhancements/node/utils.js.map +0 -1
- package/enhancements/node/where-visitor.d.ts +0 -32
- package/enhancements/node/where-visitor.js +0 -86
- package/enhancements/node/where-visitor.js.map +0 -1
- package/error.d.ts +0 -11
- package/error.js +0 -22
- package/error.js.map +0 -1
- package/index.d.ts +0 -7
- package/index.js +0 -24
- package/index.js.map +0 -1
- package/model-meta.d.ts +0 -1
- package/model-meta.js +0 -10
- package/models.d.ts +0 -1
- package/models.js +0 -1
- package/types.d.ts +0 -180
- package/types.js +0 -4
- package/types.js.map +0 -1
- package/validation.d.ts +0 -24
- package/validation.js +0 -52
- package/validation.js.map +0 -1
- package/version.d.ts +0 -5
- package/version.js +0 -35
- package/version.js.map +0 -1
- package/zod/index.d.ts +0 -3
- package/zod/index.js +0 -5
- package/zod/input.d.ts +0 -1
- package/zod/input.js +0 -8
- package/zod/models.d.ts +0 -1
- package/zod/models.js +0 -8
- package/zod/objects.d.ts +0 -1
- package/zod/objects.js +0 -8
- package/zod-utils.d.ts +0 -12
- package/zod-utils.js +0 -97
- package/zod-utils.js.map +0 -1
|
@@ -1,1442 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
3
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
4
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
5
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
6
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
7
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
8
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
9
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
10
|
-
});
|
|
11
|
-
};
|
|
12
|
-
var __rest = (this && this.__rest) || function (s, e) {
|
|
13
|
-
var t = {};
|
|
14
|
-
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
15
|
-
t[p] = s[p];
|
|
16
|
-
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
17
|
-
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
18
|
-
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
19
|
-
t[p[i]] = s[p[i]];
|
|
20
|
-
}
|
|
21
|
-
return t;
|
|
22
|
-
};
|
|
23
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
24
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
25
|
-
};
|
|
26
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
27
|
-
exports.PolicyProxyHandler = void 0;
|
|
28
|
-
const deepmerge_1 = __importDefault(require("deepmerge"));
|
|
29
|
-
const lower_case_first_1 = require("lower-case-first");
|
|
30
|
-
const tiny_invariant_1 = __importDefault(require("tiny-invariant"));
|
|
31
|
-
const upper_case_first_1 = require("upper-case-first");
|
|
32
|
-
const zod_validation_error_1 = require("zod-validation-error");
|
|
33
|
-
const constants_1 = require("../../../constants");
|
|
34
|
-
const cross_1 = require("../../../cross");
|
|
35
|
-
const logger_1 = require("../logger");
|
|
36
|
-
const promise_1 = require("../promise");
|
|
37
|
-
const query_utils_1 = require("../query-utils");
|
|
38
|
-
const utils_1 = require("../utils");
|
|
39
|
-
const check_utils_1 = require("./check-utils");
|
|
40
|
-
const policy_utils_1 = require("./policy-utils");
|
|
41
|
-
/**
|
|
42
|
-
* Prisma proxy handler for injecting access policy check.
|
|
43
|
-
*/
|
|
44
|
-
class PolicyProxyHandler {
|
|
45
|
-
constructor(prisma, model, options, context) {
|
|
46
|
-
this.prisma = prisma;
|
|
47
|
-
this.options = options;
|
|
48
|
-
this.context = context;
|
|
49
|
-
this.logger = new logger_1.Logger(prisma);
|
|
50
|
-
this.model = (0, lower_case_first_1.lowerCaseFirst)(model);
|
|
51
|
-
({ modelMeta: this.modelMeta, prismaModule: this.prismaModule } = options);
|
|
52
|
-
this.policyUtils = new policy_utils_1.PolicyUtil(prisma, options, context, this.shouldLogQuery);
|
|
53
|
-
this.queryUtils = new query_utils_1.QueryUtils(prisma, options);
|
|
54
|
-
}
|
|
55
|
-
get modelClient() {
|
|
56
|
-
return this.prisma[this.model];
|
|
57
|
-
}
|
|
58
|
-
//#region Find
|
|
59
|
-
// find operations behaves as if the entities that don't match access policies don't exist
|
|
60
|
-
findUnique(args) {
|
|
61
|
-
if (!args) {
|
|
62
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
|
|
63
|
-
}
|
|
64
|
-
if (!args.where) {
|
|
65
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'where field is required in query argument');
|
|
66
|
-
}
|
|
67
|
-
return this.findWithFluent('findUnique', args, () => null);
|
|
68
|
-
}
|
|
69
|
-
findUniqueOrThrow(args) {
|
|
70
|
-
if (!args) {
|
|
71
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
|
|
72
|
-
}
|
|
73
|
-
if (!args.where) {
|
|
74
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'where field is required in query argument');
|
|
75
|
-
}
|
|
76
|
-
return this.findWithFluent('findUniqueOrThrow', args, () => {
|
|
77
|
-
throw this.policyUtils.notFound(this.model);
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
findFirst(args) {
|
|
81
|
-
return this.findWithFluent('findFirst', args, () => null);
|
|
82
|
-
}
|
|
83
|
-
findFirstOrThrow(args) {
|
|
84
|
-
return this.findWithFluent('findFirstOrThrow', args, () => {
|
|
85
|
-
throw this.policyUtils.notFound(this.model);
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
findMany(args) {
|
|
89
|
-
return (0, promise_1.createDeferredPromise)(() => this.doFind(args, 'findMany', () => []));
|
|
90
|
-
}
|
|
91
|
-
// make a find query promise with fluent API call stubs installed
|
|
92
|
-
findWithFluent(method, args, handleRejection) {
|
|
93
|
-
args = this.policyUtils.safeClone(args);
|
|
94
|
-
return (0, promise_1.createFluentPromise)(() => this.doFind(args, method, handleRejection), args, this.options.modelMeta, this.model);
|
|
95
|
-
}
|
|
96
|
-
doFind(args, actionName, handleRejection) {
|
|
97
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
98
|
-
const origArgs = args;
|
|
99
|
-
const _args = this.policyUtils.safeClone(args);
|
|
100
|
-
if (!this.policyUtils.injectForRead(this.prisma, this.model, _args)) {
|
|
101
|
-
if (this.shouldLogQuery) {
|
|
102
|
-
this.logger.info(`[policy] \`${actionName}\` ${this.model}: unconditionally denied`);
|
|
103
|
-
}
|
|
104
|
-
return handleRejection();
|
|
105
|
-
}
|
|
106
|
-
this.policyUtils.injectReadCheckSelect(this.model, _args);
|
|
107
|
-
if (this.shouldLogQuery) {
|
|
108
|
-
this.logger.info(`[policy] \`${actionName}\` ${this.model}:\n${(0, utils_1.formatObject)(_args)}`);
|
|
109
|
-
}
|
|
110
|
-
const result = yield this.modelClient[actionName](_args);
|
|
111
|
-
return this.policyUtils.postProcessForRead(result, this.model, origArgs);
|
|
112
|
-
});
|
|
113
|
-
}
|
|
114
|
-
//#endregion
|
|
115
|
-
//#region Create
|
|
116
|
-
create(args) {
|
|
117
|
-
if (!args) {
|
|
118
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
|
|
119
|
-
}
|
|
120
|
-
if (!args.data) {
|
|
121
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'data field is required in query argument');
|
|
122
|
-
}
|
|
123
|
-
return (0, promise_1.createDeferredPromise)(() => __awaiter(this, void 0, void 0, function* () {
|
|
124
|
-
this.policyUtils.tryReject(this.prisma, this.model, 'create');
|
|
125
|
-
const origArgs = args;
|
|
126
|
-
args = this.policyUtils.safeClone(args);
|
|
127
|
-
// static input policy check for top-level create data
|
|
128
|
-
const inputCheck = this.policyUtils.checkInputGuard(this.model, args.data, 'create');
|
|
129
|
-
if (inputCheck === false) {
|
|
130
|
-
throw this.policyUtils.deniedByPolicy(this.model, 'create', undefined, constants_1.CrudFailureReason.ACCESS_POLICY_VIOLATION);
|
|
131
|
-
}
|
|
132
|
-
const hasNestedCreateOrConnect = yield this.hasNestedCreateOrConnect(args);
|
|
133
|
-
const { result, error } = yield this.queryUtils.transaction(this.prisma, (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
134
|
-
if (
|
|
135
|
-
// MUST check true here since inputCheck can be undefined (meaning static input check not possible)
|
|
136
|
-
inputCheck === true &&
|
|
137
|
-
// simple create: no nested create/connect
|
|
138
|
-
!hasNestedCreateOrConnect) {
|
|
139
|
-
// there's no nested write and we've passed input check, proceed with the create directly
|
|
140
|
-
// validate zod schema if any
|
|
141
|
-
args.data = this.validateCreateInputSchema(this.model, args.data);
|
|
142
|
-
// make a create args only containing data and ID selection
|
|
143
|
-
const createArgs = { data: args.data, select: this.policyUtils.makeIdSelection(this.model) };
|
|
144
|
-
if (this.shouldLogQuery) {
|
|
145
|
-
this.logger.info(`[policy] \`create\` ${this.model}: ${(0, utils_1.formatObject)(createArgs)}`);
|
|
146
|
-
}
|
|
147
|
-
const result = yield tx[this.model].create(createArgs);
|
|
148
|
-
// filter the read-back data
|
|
149
|
-
return this.policyUtils.readBack(tx, this.model, 'create', args, result);
|
|
150
|
-
}
|
|
151
|
-
else {
|
|
152
|
-
// proceed with a complex create and collect post-write checks
|
|
153
|
-
const { result, postWriteChecks } = yield this.doCreate(this.model, args, tx);
|
|
154
|
-
// execute post-write checks
|
|
155
|
-
yield this.runPostWriteChecks(postWriteChecks, tx);
|
|
156
|
-
// filter the read-back data
|
|
157
|
-
return this.policyUtils.readBack(tx, this.model, 'create', origArgs, result);
|
|
158
|
-
}
|
|
159
|
-
}));
|
|
160
|
-
if (error) {
|
|
161
|
-
throw error;
|
|
162
|
-
}
|
|
163
|
-
else {
|
|
164
|
-
return result;
|
|
165
|
-
}
|
|
166
|
-
}));
|
|
167
|
-
}
|
|
168
|
-
// create with nested write
|
|
169
|
-
doCreate(model, args, db) {
|
|
170
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
171
|
-
// record id fields involved in the nesting context
|
|
172
|
-
const idSelections = [];
|
|
173
|
-
const pushIdFields = (model, context) => {
|
|
174
|
-
const idFields = (0, cross_1.getIdFields)(this.modelMeta, model);
|
|
175
|
-
idSelections.push({
|
|
176
|
-
path: context.nestingPath.map((p) => p.field).filter((f) => !!f),
|
|
177
|
-
ids: idFields.map((f) => f.name),
|
|
178
|
-
});
|
|
179
|
-
};
|
|
180
|
-
// create a string key that uniquely identifies an entity
|
|
181
|
-
const getEntityKey = (model, ids) => `${(0, upper_case_first_1.upperCaseFirst)(model)}#${Object.keys(ids)
|
|
182
|
-
.sort()
|
|
183
|
-
.map((f) => { var _a; return `${f}:${(_a = ids[f]) === null || _a === void 0 ? void 0 : _a.toString()}`; })
|
|
184
|
-
.join('_')}`;
|
|
185
|
-
// record keys of entities that are connected instead of created
|
|
186
|
-
const connectedEntities = new Set();
|
|
187
|
-
// visit the create payload
|
|
188
|
-
const visitor = new cross_1.NestedWriteVisitor(this.modelMeta, {
|
|
189
|
-
create: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
|
|
190
|
-
const validateResult = this.validateCreateInputSchema(model, args);
|
|
191
|
-
if (validateResult !== args) {
|
|
192
|
-
this.policyUtils.replace(args, validateResult);
|
|
193
|
-
}
|
|
194
|
-
pushIdFields(model, context);
|
|
195
|
-
}),
|
|
196
|
-
createMany: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
|
|
197
|
-
(0, cross_1.enumerate)(args.data).forEach((item) => {
|
|
198
|
-
const r = this.validateCreateInputSchema(model, item);
|
|
199
|
-
if (r !== item) {
|
|
200
|
-
this.policyUtils.replace(item, r);
|
|
201
|
-
}
|
|
202
|
-
});
|
|
203
|
-
pushIdFields(model, context);
|
|
204
|
-
}),
|
|
205
|
-
connectOrCreate: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
|
|
206
|
-
var _a, _b;
|
|
207
|
-
if (!args.where) {
|
|
208
|
-
throw this.policyUtils.validationError(`'where' field is required for connectOrCreate`);
|
|
209
|
-
}
|
|
210
|
-
if (args.create) {
|
|
211
|
-
args.create = this.validateCreateInputSchema(model, args.create);
|
|
212
|
-
}
|
|
213
|
-
const existing = yield this.policyUtils.checkExistence(db, model, args.where);
|
|
214
|
-
if (existing) {
|
|
215
|
-
// connect case
|
|
216
|
-
if ((_a = context.field) === null || _a === void 0 ? void 0 : _a.backLink) {
|
|
217
|
-
const backLinkField = (0, cross_1.resolveField)(this.modelMeta, model, context.field.backLink);
|
|
218
|
-
if (backLinkField === null || backLinkField === void 0 ? void 0 : backLinkField.isRelationOwner) {
|
|
219
|
-
// "connect" is actually "update" to foreign keys, so we need to map the "connect" payload
|
|
220
|
-
// to "update" payload by translating pk to fks, and use that to check update policies
|
|
221
|
-
const fieldsToUpdate = Object.values((_b = backLinkField.foreignKeyMapping) !== null && _b !== void 0 ? _b : {});
|
|
222
|
-
yield this.policyUtils.checkPolicyForUnique(model, args.where, 'update', db, fieldsToUpdate);
|
|
223
|
-
}
|
|
224
|
-
}
|
|
225
|
-
this.mergeToParent(context.parent, 'connect', args.where);
|
|
226
|
-
// record the key of connected entities so we can avoid validating them later
|
|
227
|
-
connectedEntities.add(getEntityKey(model, existing));
|
|
228
|
-
}
|
|
229
|
-
else {
|
|
230
|
-
// create case
|
|
231
|
-
pushIdFields(model, context);
|
|
232
|
-
// create a new "create" clause at the parent level
|
|
233
|
-
this.mergeToParent(context.parent, 'create', args.create);
|
|
234
|
-
}
|
|
235
|
-
// remove the connectOrCreate clause
|
|
236
|
-
this.removeFromParent(context.parent, 'connectOrCreate', args);
|
|
237
|
-
// return false to prevent visiting the nested payload
|
|
238
|
-
return false;
|
|
239
|
-
}),
|
|
240
|
-
connect: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
|
|
241
|
-
var _a, _b;
|
|
242
|
-
if (!args || typeof args !== 'object' || Object.keys(args).length === 0) {
|
|
243
|
-
throw this.policyUtils.validationError(`'connect' field must be an non-empty object`);
|
|
244
|
-
}
|
|
245
|
-
if ((_a = context.field) === null || _a === void 0 ? void 0 : _a.backLink) {
|
|
246
|
-
const backLinkField = (0, cross_1.resolveField)(this.modelMeta, model, context.field.backLink);
|
|
247
|
-
if (backLinkField === null || backLinkField === void 0 ? void 0 : backLinkField.isRelationOwner) {
|
|
248
|
-
// check existence
|
|
249
|
-
yield this.policyUtils.checkExistence(db, model, args, true);
|
|
250
|
-
// the target side of relation owns the relation, check if it's updatable
|
|
251
|
-
// "connect" is actually "update" to foreign keys, so we need to map the "connect" payload
|
|
252
|
-
// to "update" payload by translating pk to fks, and use that to check update policies
|
|
253
|
-
const fieldsToUpdate = Object.values((_b = backLinkField.foreignKeyMapping) !== null && _b !== void 0 ? _b : {});
|
|
254
|
-
yield this.policyUtils.checkPolicyForUnique(model, args, 'update', db, fieldsToUpdate);
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
}),
|
|
258
|
-
});
|
|
259
|
-
yield visitor.visit(model, 'create', args);
|
|
260
|
-
// build the final "select" clause including all nested ID fields
|
|
261
|
-
let select = undefined;
|
|
262
|
-
if (idSelections.length > 0) {
|
|
263
|
-
select = {};
|
|
264
|
-
idSelections.forEach(({ path, ids }) => {
|
|
265
|
-
let curr = select;
|
|
266
|
-
for (const p of path) {
|
|
267
|
-
if (!curr[p.name]) {
|
|
268
|
-
curr[p.name] = { select: {} };
|
|
269
|
-
}
|
|
270
|
-
curr = curr[p.name].select;
|
|
271
|
-
}
|
|
272
|
-
Object.assign(curr, ...ids.map((f) => ({ [f]: true })));
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
// proceed with the create
|
|
276
|
-
const createArgs = { data: args.data, select };
|
|
277
|
-
if (this.shouldLogQuery) {
|
|
278
|
-
this.logger.info(`[policy] \`create\` ${model}: ${(0, utils_1.formatObject)(createArgs)}`);
|
|
279
|
-
}
|
|
280
|
-
const result = yield db[model].create(createArgs);
|
|
281
|
-
// post create policy check for the top-level and nested creates
|
|
282
|
-
const postCreateChecks = new Map();
|
|
283
|
-
// visit the create result and collect entities that need to be post-checked
|
|
284
|
-
const modelDataVisitor = new cross_1.ModelDataVisitor(this.modelMeta);
|
|
285
|
-
modelDataVisitor.visit(model, result, (model, _data, scalarData) => {
|
|
286
|
-
const key = getEntityKey(model, scalarData);
|
|
287
|
-
// only check if entity is created, not connected
|
|
288
|
-
if (!connectedEntities.has(key) && !postCreateChecks.has(key)) {
|
|
289
|
-
const idFields = this.policyUtils.getIdFieldValues(model, scalarData);
|
|
290
|
-
postCreateChecks.set(key, { model, operation: 'create', uniqueFilter: idFields });
|
|
291
|
-
}
|
|
292
|
-
});
|
|
293
|
-
// return only the ids of the top-level entity
|
|
294
|
-
const ids = this.policyUtils.getEntityIds(model, result);
|
|
295
|
-
return { result: ids, postWriteChecks: [...postCreateChecks.values()] };
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
// Checks if the given create payload has nested create or connect
|
|
299
|
-
hasNestedCreateOrConnect(args) {
|
|
300
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
301
|
-
let hasNestedCreateOrConnect = false;
|
|
302
|
-
const visitor = new cross_1.NestedWriteVisitor(this.modelMeta, {
|
|
303
|
-
create(_model, _args, context) {
|
|
304
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
305
|
-
if (context.field) {
|
|
306
|
-
hasNestedCreateOrConnect = true;
|
|
307
|
-
return false;
|
|
308
|
-
}
|
|
309
|
-
else {
|
|
310
|
-
return true;
|
|
311
|
-
}
|
|
312
|
-
});
|
|
313
|
-
},
|
|
314
|
-
connect() {
|
|
315
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
316
|
-
hasNestedCreateOrConnect = true;
|
|
317
|
-
return false;
|
|
318
|
-
});
|
|
319
|
-
},
|
|
320
|
-
connectOrCreate() {
|
|
321
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
322
|
-
hasNestedCreateOrConnect = true;
|
|
323
|
-
return false;
|
|
324
|
-
});
|
|
325
|
-
},
|
|
326
|
-
createMany() {
|
|
327
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
328
|
-
hasNestedCreateOrConnect = true;
|
|
329
|
-
return false;
|
|
330
|
-
});
|
|
331
|
-
},
|
|
332
|
-
});
|
|
333
|
-
yield visitor.visit(this.model, 'create', args);
|
|
334
|
-
return hasNestedCreateOrConnect;
|
|
335
|
-
});
|
|
336
|
-
}
|
|
337
|
-
// Validates the given create payload against Zod schema if any
|
|
338
|
-
validateCreateInputSchema(model, data) {
|
|
339
|
-
if (!data) {
|
|
340
|
-
return data;
|
|
341
|
-
}
|
|
342
|
-
return this.policyUtils.validateZodSchema(model, 'create', data, false, (err) => {
|
|
343
|
-
throw this.policyUtils.deniedByPolicy(model, 'create', `input failed validation: ${(0, zod_validation_error_1.fromZodError)(err)}`, constants_1.CrudFailureReason.DATA_VALIDATION_VIOLATION, err);
|
|
344
|
-
});
|
|
345
|
-
}
|
|
346
|
-
createMany(args) {
|
|
347
|
-
if (!args) {
|
|
348
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
|
|
349
|
-
}
|
|
350
|
-
if (!args.data) {
|
|
351
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'data field is required in query argument');
|
|
352
|
-
}
|
|
353
|
-
return (0, promise_1.createDeferredPromise)(() => __awaiter(this, void 0, void 0, function* () {
|
|
354
|
-
this.policyUtils.tryReject(this.prisma, this.model, 'create');
|
|
355
|
-
args = this.policyUtils.safeClone(args);
|
|
356
|
-
// `createManyAndReturn` may need to be converted to regular `create`s
|
|
357
|
-
const shouldConvertToCreate = this.preprocessCreateManyPayload(args);
|
|
358
|
-
if (!shouldConvertToCreate) {
|
|
359
|
-
// direct `createMany`
|
|
360
|
-
return this.modelClient.createMany(args);
|
|
361
|
-
}
|
|
362
|
-
else {
|
|
363
|
-
// create entities in a transaction with post-create checks
|
|
364
|
-
return this.queryUtils.transaction(this.prisma, (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
365
|
-
const { result, postWriteChecks } = yield this.doCreateMany(this.model, args, tx, 'createMany');
|
|
366
|
-
// post-create check
|
|
367
|
-
yield this.runPostWriteChecks(postWriteChecks, tx);
|
|
368
|
-
return { count: result.length };
|
|
369
|
-
}));
|
|
370
|
-
}
|
|
371
|
-
}));
|
|
372
|
-
}
|
|
373
|
-
createManyAndReturn(args) {
|
|
374
|
-
if (!args) {
|
|
375
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
|
|
376
|
-
}
|
|
377
|
-
if (!args.data) {
|
|
378
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'data field is required in query argument');
|
|
379
|
-
}
|
|
380
|
-
return (0, promise_1.createDeferredPromise)(() => __awaiter(this, void 0, void 0, function* () {
|
|
381
|
-
var _a;
|
|
382
|
-
this.policyUtils.tryReject(this.prisma, this.model, 'create');
|
|
383
|
-
const origArgs = args;
|
|
384
|
-
args = this.policyUtils.safeClone(args);
|
|
385
|
-
// `createManyAndReturn` may need to be converted to regular `create`s
|
|
386
|
-
const shouldConvertToCreate = this.preprocessCreateManyPayload(args);
|
|
387
|
-
let result;
|
|
388
|
-
if (!shouldConvertToCreate) {
|
|
389
|
-
// direct `createManyAndReturn`, make sure we only select id fields for return
|
|
390
|
-
// so we can use the results directly for read-back check
|
|
391
|
-
const updatedArgs = Object.assign(Object.assign({}, args), { select: this.policyUtils.makeIdSelection(this.model), include: undefined });
|
|
392
|
-
const created = yield this.modelClient.createManyAndReturn(updatedArgs);
|
|
393
|
-
// process read-back
|
|
394
|
-
result = yield Promise.all(created.map((item) => this.policyUtils.readBack(this.prisma, this.model, 'create', origArgs, item)));
|
|
395
|
-
}
|
|
396
|
-
else {
|
|
397
|
-
// create entities in a transaction with post-create checks
|
|
398
|
-
result = yield this.queryUtils.transaction(this.prisma, (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
399
|
-
const { result: created, postWriteChecks } = yield this.doCreateMany(this.model, args, tx, 'createManyAndReturn');
|
|
400
|
-
// post-create check
|
|
401
|
-
yield this.runPostWriteChecks(postWriteChecks, tx);
|
|
402
|
-
// process read-back
|
|
403
|
-
return Promise.all(created.map((item) => this.policyUtils.readBack(tx, this.model, 'create', origArgs, item)));
|
|
404
|
-
}));
|
|
405
|
-
}
|
|
406
|
-
// throw read-back error if any of the create result read-back fails
|
|
407
|
-
const error = (_a = result.find((r) => !!r.error)) === null || _a === void 0 ? void 0 : _a.error;
|
|
408
|
-
if (error) {
|
|
409
|
-
throw error;
|
|
410
|
-
}
|
|
411
|
-
else {
|
|
412
|
-
return result.map((r) => r.result);
|
|
413
|
-
}
|
|
414
|
-
}));
|
|
415
|
-
}
|
|
416
|
-
/**
|
|
417
|
-
* Preprocess the payload of `createMany` and `createManyAndReturn` and update in place if needed.
|
|
418
|
-
* @returns `true` if the operation should be converted to regular `create`s; false otherwise.
|
|
419
|
-
*/
|
|
420
|
-
preprocessCreateManyPayload(args) {
|
|
421
|
-
if (!args) {
|
|
422
|
-
return false;
|
|
423
|
-
}
|
|
424
|
-
// if post-create check is needed
|
|
425
|
-
const needPostCreateCheck = this.validateCreateInput(args);
|
|
426
|
-
// if the payload has any relation fields. Note that other enhancements (`withDefaultInAuth` for now)
|
|
427
|
-
// can introduce relation fields into the payload
|
|
428
|
-
let hasRelationFields = false;
|
|
429
|
-
if (args.data) {
|
|
430
|
-
hasRelationFields = this.hasRelationFieldsInPayload(this.model, args.data);
|
|
431
|
-
}
|
|
432
|
-
return needPostCreateCheck || hasRelationFields;
|
|
433
|
-
}
|
|
434
|
-
hasRelationFieldsInPayload(model, payload) {
|
|
435
|
-
const modelInfo = (0, cross_1.getModelInfo)(this.modelMeta, model);
|
|
436
|
-
if (!modelInfo) {
|
|
437
|
-
return false;
|
|
438
|
-
}
|
|
439
|
-
for (const item of (0, cross_1.enumerate)(payload)) {
|
|
440
|
-
for (const field of Object.keys(item)) {
|
|
441
|
-
const fieldInfo = (0, cross_1.resolveField)(this.modelMeta, model, field);
|
|
442
|
-
if (fieldInfo === null || fieldInfo === void 0 ? void 0 : fieldInfo.isDataModel) {
|
|
443
|
-
return true;
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
}
|
|
447
|
-
return false;
|
|
448
|
-
}
|
|
449
|
-
validateCreateInput(args) {
|
|
450
|
-
let needPostCreateCheck = false;
|
|
451
|
-
for (const item of (0, cross_1.enumerate)(args.data)) {
|
|
452
|
-
const validationResult = this.validateCreateInputSchema(this.model, item);
|
|
453
|
-
if (validationResult !== item) {
|
|
454
|
-
this.policyUtils.replace(item, validationResult);
|
|
455
|
-
}
|
|
456
|
-
const inputCheck = this.policyUtils.checkInputGuard(this.model, item, 'create');
|
|
457
|
-
if (inputCheck === false) {
|
|
458
|
-
// unconditionally deny
|
|
459
|
-
throw this.policyUtils.deniedByPolicy(this.model, 'create', undefined, constants_1.CrudFailureReason.ACCESS_POLICY_VIOLATION);
|
|
460
|
-
}
|
|
461
|
-
else if (inputCheck === true) {
|
|
462
|
-
// unconditionally allow
|
|
463
|
-
}
|
|
464
|
-
else if (inputCheck === undefined) {
|
|
465
|
-
// static policy check is not possible, need to do post-create check
|
|
466
|
-
needPostCreateCheck = true;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
return needPostCreateCheck;
|
|
470
|
-
}
|
|
471
|
-
doCreateMany(model, args, db, action) {
|
|
472
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
473
|
-
// We can't call the native "createMany" because we can't get back what was created
|
|
474
|
-
// for post-create checks. Instead, do a "create" for each item and collect the results.
|
|
475
|
-
let createResult = yield Promise.all((0, cross_1.enumerate)(args.data).map((item) => __awaiter(this, void 0, void 0, function* () {
|
|
476
|
-
if (args.skipDuplicates) {
|
|
477
|
-
if (yield this.hasDuplicatedUniqueConstraint(model, item, undefined, db)) {
|
|
478
|
-
if (this.shouldLogQuery) {
|
|
479
|
-
this.logger.info(`[policy] \`createMany\` skipping duplicate ${(0, utils_1.formatObject)(item)}`);
|
|
480
|
-
}
|
|
481
|
-
return undefined;
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
if (this.shouldLogQuery) {
|
|
485
|
-
this.logger.info(`[policy] \`create\` for \`${action}\` ${model}: ${(0, utils_1.formatObject)(item)}`);
|
|
486
|
-
}
|
|
487
|
-
return yield db[model].create({ select: this.policyUtils.makeIdSelection(model), data: item });
|
|
488
|
-
})));
|
|
489
|
-
// filter undefined values due to skipDuplicates
|
|
490
|
-
createResult = createResult.filter((p) => !!p);
|
|
491
|
-
return {
|
|
492
|
-
result: createResult,
|
|
493
|
-
postWriteChecks: createResult.map((item) => ({
|
|
494
|
-
model,
|
|
495
|
-
operation: 'create',
|
|
496
|
-
uniqueFilter: item,
|
|
497
|
-
})),
|
|
498
|
-
};
|
|
499
|
-
});
|
|
500
|
-
}
|
|
501
|
-
hasDuplicatedUniqueConstraint(model, createData, upstreamQuery, db) {
|
|
502
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
503
|
-
// check unique constraint conflicts
|
|
504
|
-
// we can't rely on try/catch/ignore constraint violation error: https://github.com/prisma/prisma/issues/20496
|
|
505
|
-
// TODO: for simple cases we should be able to translate it to an `upsert` with empty `update` payload
|
|
506
|
-
var _a;
|
|
507
|
-
// for each unique constraint, check if the input item has all fields set, and if so, check if
|
|
508
|
-
// an entity already exists, and ignore accordingly
|
|
509
|
-
const uniqueConstraints = this.policyUtils.getUniqueConstraints(model);
|
|
510
|
-
for (const constraint of Object.values(uniqueConstraints)) {
|
|
511
|
-
// the unique filter used to check existence
|
|
512
|
-
const uniqueFilter = {};
|
|
513
|
-
// unique constraint fields not covered yet
|
|
514
|
-
const remainingConstraintFields = new Set(constraint.fields);
|
|
515
|
-
// collect constraint fields from the create data
|
|
516
|
-
for (const [k, v] of Object.entries(createData)) {
|
|
517
|
-
if (v === undefined) {
|
|
518
|
-
continue;
|
|
519
|
-
}
|
|
520
|
-
if (remainingConstraintFields.has(k)) {
|
|
521
|
-
uniqueFilter[k] = v;
|
|
522
|
-
remainingConstraintFields.delete(k);
|
|
523
|
-
}
|
|
524
|
-
}
|
|
525
|
-
// collect constraint fields from the upstream query
|
|
526
|
-
if (upstreamQuery) {
|
|
527
|
-
for (const [k, v] of Object.entries(upstreamQuery)) {
|
|
528
|
-
if (v === undefined) {
|
|
529
|
-
continue;
|
|
530
|
-
}
|
|
531
|
-
if (remainingConstraintFields.has(k)) {
|
|
532
|
-
uniqueFilter[k] = v;
|
|
533
|
-
remainingConstraintFields.delete(k);
|
|
534
|
-
continue;
|
|
535
|
-
}
|
|
536
|
-
// check if the upstream query contains a relation field which covers
|
|
537
|
-
// a foreign key field constraint
|
|
538
|
-
const fieldInfo = (0, cross_1.requireField)(this.modelMeta, model, k);
|
|
539
|
-
if (!fieldInfo.isDataModel) {
|
|
540
|
-
// only care about relation fields
|
|
541
|
-
continue;
|
|
542
|
-
}
|
|
543
|
-
// merge the upstream query into the unique filter
|
|
544
|
-
uniqueFilter[k] = v;
|
|
545
|
-
// mark the corresponding foreign key fields as covered
|
|
546
|
-
const fkMapping = (_a = fieldInfo.foreignKeyMapping) !== null && _a !== void 0 ? _a : {};
|
|
547
|
-
for (const fk of Object.values(fkMapping)) {
|
|
548
|
-
remainingConstraintFields.delete(fk);
|
|
549
|
-
}
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
if (remainingConstraintFields.size === 0) {
|
|
553
|
-
// all constraint fields set, check existence
|
|
554
|
-
const existing = yield this.policyUtils.checkExistence(db, model, uniqueFilter);
|
|
555
|
-
if (existing) {
|
|
556
|
-
return true;
|
|
557
|
-
}
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
return false;
|
|
561
|
-
});
|
|
562
|
-
}
|
|
563
|
-
//#endregion
|
|
564
|
-
//#region Update & Upsert
|
|
565
|
-
// "update" and "upsert" work against unique entity, so we actively rejects the request if the
|
|
566
|
-
// entity fails policy check
|
|
567
|
-
//
|
|
568
|
-
// "updateMany" works against a set of entities, entities not passing policy check are silently
|
|
569
|
-
// ignored
|
|
570
|
-
update(args) {
|
|
571
|
-
if (!args) {
|
|
572
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
|
|
573
|
-
}
|
|
574
|
-
if (!args.where) {
|
|
575
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'where field is required in query argument');
|
|
576
|
-
}
|
|
577
|
-
if (!args.data) {
|
|
578
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'data field is required in query argument');
|
|
579
|
-
}
|
|
580
|
-
return (0, promise_1.createDeferredPromise)(() => __awaiter(this, void 0, void 0, function* () {
|
|
581
|
-
args = this.policyUtils.safeClone(args);
|
|
582
|
-
const { result, error } = yield this.queryUtils.transaction(this.prisma, (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
583
|
-
// proceed with nested writes and collect post-write checks
|
|
584
|
-
const { result, postWriteChecks } = yield this.doUpdate(args, tx);
|
|
585
|
-
// post-write check
|
|
586
|
-
yield this.runPostWriteChecks(postWriteChecks, tx);
|
|
587
|
-
// filter the read-back data
|
|
588
|
-
return this.policyUtils.readBack(tx, this.model, 'update', args, result);
|
|
589
|
-
}));
|
|
590
|
-
if (error) {
|
|
591
|
-
throw error;
|
|
592
|
-
}
|
|
593
|
-
else {
|
|
594
|
-
return result;
|
|
595
|
-
}
|
|
596
|
-
}));
|
|
597
|
-
}
|
|
598
|
-
doUpdate(args, db) {
|
|
599
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
600
|
-
// collected post-update checks
|
|
601
|
-
const postWriteChecks = [];
|
|
602
|
-
// registers a post-update check task
|
|
603
|
-
const _registerPostUpdateCheck = (model, preUpdateLookupFilter, postUpdateLookupFilter) => __awaiter(this, void 0, void 0, function* () {
|
|
604
|
-
// both "post-update" rules and Zod schemas require a post-update check
|
|
605
|
-
if (this.policyUtils.hasAuthGuard(model, 'postUpdate') || this.policyUtils.getZodSchema(model)) {
|
|
606
|
-
// select pre-update field values
|
|
607
|
-
let preValue;
|
|
608
|
-
const preValueSelect = this.policyUtils.getPreValueSelect(model);
|
|
609
|
-
if (preValueSelect && Object.keys(preValueSelect).length > 0) {
|
|
610
|
-
preValue = yield db[model].findFirst({ where: preUpdateLookupFilter, select: preValueSelect });
|
|
611
|
-
}
|
|
612
|
-
postWriteChecks.push({
|
|
613
|
-
model,
|
|
614
|
-
operation: 'postUpdate',
|
|
615
|
-
uniqueFilter: postUpdateLookupFilter,
|
|
616
|
-
preValue,
|
|
617
|
-
});
|
|
618
|
-
}
|
|
619
|
-
});
|
|
620
|
-
// We can't let the native "update" to handle nested "create" because we can't get back what
|
|
621
|
-
// was created for doing post-update checks.
|
|
622
|
-
// Instead, handle nested create inside update as an atomic operation that creates an entire
|
|
623
|
-
// subtree (containing nested creates/connects)
|
|
624
|
-
const _create = (model, args, context) => __awaiter(this, void 0, void 0, function* () {
|
|
625
|
-
var _a;
|
|
626
|
-
let createData = args;
|
|
627
|
-
if ((_a = context.field) === null || _a === void 0 ? void 0 : _a.backLink) {
|
|
628
|
-
// Check if the create payload contains any "unsafe" assignment:
|
|
629
|
-
// assign id or foreign key fields.
|
|
630
|
-
//
|
|
631
|
-
// The reason why we need to do that is Prisma's mutations payload
|
|
632
|
-
// structure has two mutually exclusive forms for safe and unsafe
|
|
633
|
-
// operations. E.g.:
|
|
634
|
-
// - safe: { data: { user: { connect: { id: 1 }} } }
|
|
635
|
-
// - unsafe: { data: { userId: 1 } }
|
|
636
|
-
const unsafe = (0, utils_1.isUnsafeMutate)(model, args, this.modelMeta);
|
|
637
|
-
// handles the connection to upstream entity
|
|
638
|
-
const reversedQuery = yield this.policyUtils.buildReversedQuery(db, context, true, unsafe);
|
|
639
|
-
if ((!unsafe || context.field.isRelationOwner) && reversedQuery[context.field.backLink]) {
|
|
640
|
-
// if mutation is safe, or current field owns the relation (so the other side has no fk),
|
|
641
|
-
// and the reverse query contains the back link, then we can build a "connect" with it
|
|
642
|
-
createData = Object.assign(Object.assign({}, createData), { [context.field.backLink]: {
|
|
643
|
-
connect: reversedQuery[context.field.backLink],
|
|
644
|
-
} });
|
|
645
|
-
}
|
|
646
|
-
else {
|
|
647
|
-
// otherwise, the reverse query should be translated to foreign key setting
|
|
648
|
-
// and merged to the create data
|
|
649
|
-
const backLinkField = this.requireBackLink(context.field);
|
|
650
|
-
(0, tiny_invariant_1.default)(backLinkField.foreignKeyMapping);
|
|
651
|
-
// try to extract foreign key values from the reverse query
|
|
652
|
-
let fkValues = Object.values(backLinkField.foreignKeyMapping).reduce((obj, fk) => {
|
|
653
|
-
obj[fk] = reversedQuery[fk];
|
|
654
|
-
return obj;
|
|
655
|
-
}, {});
|
|
656
|
-
if (Object.values(fkValues).every((v) => v !== undefined)) {
|
|
657
|
-
// all foreign key values are available, merge them to the create data
|
|
658
|
-
createData = Object.assign(Object.assign({}, createData), fkValues);
|
|
659
|
-
}
|
|
660
|
-
else {
|
|
661
|
-
// some foreign key values are missing, need to look up the upstream entity,
|
|
662
|
-
// this can happen when the upstream entity doesn't have a unique where clause,
|
|
663
|
-
// for example when it's nested inside a one-to-one update
|
|
664
|
-
const upstreamQuery = {
|
|
665
|
-
where: reversedQuery[backLinkField.name],
|
|
666
|
-
select: this.policyUtils.makeIdSelection(backLinkField.type),
|
|
667
|
-
};
|
|
668
|
-
// fetch the upstream entity
|
|
669
|
-
if (this.shouldLogQuery) {
|
|
670
|
-
this.logger.info(`[policy] \`findUniqueOrThrow\` ${model}: looking up upstream entity of ${backLinkField.type}, ${(0, utils_1.formatObject)(upstreamQuery)}`);
|
|
671
|
-
}
|
|
672
|
-
const upstreamEntity = yield this.prisma[backLinkField.type].findUniqueOrThrow(upstreamQuery);
|
|
673
|
-
// map ids to foreign keys
|
|
674
|
-
fkValues = Object.entries(backLinkField.foreignKeyMapping).reduce((obj, [id, fk]) => {
|
|
675
|
-
obj[fk] = upstreamEntity[id];
|
|
676
|
-
return obj;
|
|
677
|
-
}, {});
|
|
678
|
-
// merge them to the create data
|
|
679
|
-
createData = Object.assign(Object.assign({}, createData), fkValues);
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
// proceed with the create and collect post-create checks
|
|
684
|
-
const { postWriteChecks: checks, result } = yield this.doCreate(model, { data: createData }, db);
|
|
685
|
-
postWriteChecks.push(...checks);
|
|
686
|
-
return result;
|
|
687
|
-
});
|
|
688
|
-
const _createMany = (model, args, context) => __awaiter(this, void 0, void 0, function* () {
|
|
689
|
-
for (const item of (0, cross_1.enumerate)(args.data)) {
|
|
690
|
-
if (args.skipDuplicates) {
|
|
691
|
-
// get a reversed query to include fields inherited from upstream mutation,
|
|
692
|
-
// it'll be merged with the create payload for unique constraint checking
|
|
693
|
-
const upstreamQuery = yield this.policyUtils.buildReversedQuery(db, context);
|
|
694
|
-
if (yield this.hasDuplicatedUniqueConstraint(model, item, upstreamQuery, db)) {
|
|
695
|
-
if (this.shouldLogQuery) {
|
|
696
|
-
this.logger.info(`[policy] \`createMany\` skipping duplicate ${(0, utils_1.formatObject)(item)}`);
|
|
697
|
-
}
|
|
698
|
-
continue;
|
|
699
|
-
}
|
|
700
|
-
}
|
|
701
|
-
yield _create(model, item, context);
|
|
702
|
-
}
|
|
703
|
-
});
|
|
704
|
-
const _connectDisconnect = (model, args, context, operation) => __awaiter(this, void 0, void 0, function* () {
|
|
705
|
-
var _a, _b;
|
|
706
|
-
if ((_a = context.field) === null || _a === void 0 ? void 0 : _a.backLink) {
|
|
707
|
-
const backLinkField = this.policyUtils.getModelField(model, context.field.backLink);
|
|
708
|
-
if (backLinkField === null || backLinkField === void 0 ? void 0 : backLinkField.isRelationOwner) {
|
|
709
|
-
let uniqueFilter = args;
|
|
710
|
-
if (operation === 'disconnect') {
|
|
711
|
-
// disconnect filter is not unique, need to build a reversed query to
|
|
712
|
-
// locate the entity and use its id fields as unique filter
|
|
713
|
-
const reversedQuery = yield this.policyUtils.buildReversedQuery(db, context);
|
|
714
|
-
const found = yield db[model].findUnique({
|
|
715
|
-
where: reversedQuery,
|
|
716
|
-
select: this.policyUtils.makeIdSelection(model),
|
|
717
|
-
});
|
|
718
|
-
uniqueFilter = found && this.policyUtils.getIdFieldValues(model, found);
|
|
719
|
-
}
|
|
720
|
-
// update happens on the related model, require updatable,
|
|
721
|
-
// `uniqueFilter` can be undefined if the entity to be disconnected doesn't exist
|
|
722
|
-
if (uniqueFilter) {
|
|
723
|
-
// check for update, "connect" and "disconnect" are actually "update" to foreign keys
|
|
724
|
-
const fieldsToUpdate = Object.values((_b = backLinkField.foreignKeyMapping) !== null && _b !== void 0 ? _b : {});
|
|
725
|
-
yield this.policyUtils.checkPolicyForUnique(model, uniqueFilter, 'update', db, fieldsToUpdate);
|
|
726
|
-
// register post-update check
|
|
727
|
-
yield _registerPostUpdateCheck(model, uniqueFilter, uniqueFilter);
|
|
728
|
-
}
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
});
|
|
732
|
-
// visit nested writes
|
|
733
|
-
const visitor = new cross_1.NestedWriteVisitor(this.modelMeta, {
|
|
734
|
-
update: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
|
|
735
|
-
var _a;
|
|
736
|
-
// build a unique query including upstream conditions
|
|
737
|
-
const uniqueFilter = yield this.policyUtils.buildReversedQuery(db, context);
|
|
738
|
-
// handle not-found
|
|
739
|
-
const existing = yield this.policyUtils.checkExistence(db, model, uniqueFilter, true);
|
|
740
|
-
// check if the update actually writes to this model
|
|
741
|
-
let thisModelUpdate = false;
|
|
742
|
-
const updatePayload = (_a = args.data) !== null && _a !== void 0 ? _a : args;
|
|
743
|
-
const validatedPayload = this.validateUpdateInputSchema(model, updatePayload);
|
|
744
|
-
if (validatedPayload !== updatePayload) {
|
|
745
|
-
this.policyUtils.replace(updatePayload, validatedPayload);
|
|
746
|
-
}
|
|
747
|
-
if (updatePayload) {
|
|
748
|
-
for (const key of Object.keys(updatePayload)) {
|
|
749
|
-
const field = (0, cross_1.resolveField)(this.modelMeta, model, key);
|
|
750
|
-
if (field) {
|
|
751
|
-
if (!field.isDataModel) {
|
|
752
|
-
// scalar field, require this model to be updatable
|
|
753
|
-
thisModelUpdate = true;
|
|
754
|
-
break;
|
|
755
|
-
}
|
|
756
|
-
else if (field.isRelationOwner) {
|
|
757
|
-
// relation is being updated and this model owns foreign key, require updatable
|
|
758
|
-
thisModelUpdate = true;
|
|
759
|
-
break;
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
}
|
|
763
|
-
}
|
|
764
|
-
if (thisModelUpdate) {
|
|
765
|
-
this.policyUtils.tryReject(db, this.model, 'update');
|
|
766
|
-
// check pre-update guard
|
|
767
|
-
yield this.policyUtils.checkPolicyForUnique(model, uniqueFilter, 'update', db, this.queryUtils.getFieldsWithDefinedValues(updatePayload));
|
|
768
|
-
// handle the case where id fields are updated
|
|
769
|
-
const _args = args;
|
|
770
|
-
const checkPayload = _args.data && typeof _args.data === 'object' ? _args.data : _args;
|
|
771
|
-
const postUpdateIds = this.calculatePostUpdateIds(model, existing, checkPayload);
|
|
772
|
-
// register post-update check
|
|
773
|
-
yield _registerPostUpdateCheck(model, existing, postUpdateIds);
|
|
774
|
-
}
|
|
775
|
-
}),
|
|
776
|
-
updateMany: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
|
|
777
|
-
// prepare for post-update check
|
|
778
|
-
if (this.policyUtils.hasAuthGuard(model, 'postUpdate') || this.policyUtils.getZodSchema(model)) {
|
|
779
|
-
let select = this.policyUtils.makeIdSelection(model);
|
|
780
|
-
const preValueSelect = this.policyUtils.getPreValueSelect(model);
|
|
781
|
-
if (preValueSelect) {
|
|
782
|
-
select = Object.assign(Object.assign({}, select), preValueSelect);
|
|
783
|
-
}
|
|
784
|
-
const reversedQuery = yield this.policyUtils.buildReversedQuery(db, context);
|
|
785
|
-
const currentSetQuery = { select, where: reversedQuery };
|
|
786
|
-
this.policyUtils.injectAuthGuardAsWhere(db, currentSetQuery, model, 'read');
|
|
787
|
-
if (this.shouldLogQuery) {
|
|
788
|
-
this.logger.info(`[policy] \`findMany\` for post update check ${model}:\n${(0, utils_1.formatObject)(currentSetQuery)}`);
|
|
789
|
-
}
|
|
790
|
-
const currentSet = yield db[model].findMany(currentSetQuery);
|
|
791
|
-
postWriteChecks.push(...currentSet.map((preValue) => ({
|
|
792
|
-
model,
|
|
793
|
-
operation: 'postUpdate',
|
|
794
|
-
uniqueFilter: preValue,
|
|
795
|
-
preValue: preValueSelect ? preValue : undefined,
|
|
796
|
-
})));
|
|
797
|
-
}
|
|
798
|
-
args.data = this.validateUpdateInputSchema(model, args.data);
|
|
799
|
-
const updateGuard = this.policyUtils.getAuthGuard(db, model, 'update');
|
|
800
|
-
if (this.policyUtils.isTrue(updateGuard) || this.policyUtils.isFalse(updateGuard)) {
|
|
801
|
-
// injects simple auth guard into where clause
|
|
802
|
-
this.policyUtils.injectAuthGuardAsWhere(db, args, model, 'update');
|
|
803
|
-
}
|
|
804
|
-
else {
|
|
805
|
-
// we have to process `updateMany` separately because the guard may contain
|
|
806
|
-
// filters using relation fields which are not allowed in nested `updateMany`
|
|
807
|
-
const reversedQuery = yield this.policyUtils.buildReversedQuery(db, context);
|
|
808
|
-
const updateWhere = this.policyUtils.and(reversedQuery, updateGuard);
|
|
809
|
-
if (this.shouldLogQuery) {
|
|
810
|
-
this.logger.info(`[policy] \`updateMany\` ${model}:\n${(0, utils_1.formatObject)({
|
|
811
|
-
where: updateWhere,
|
|
812
|
-
data: args.data,
|
|
813
|
-
})}`);
|
|
814
|
-
}
|
|
815
|
-
yield db[model].updateMany({ where: updateWhere, data: args.data });
|
|
816
|
-
delete context.parent.updateMany;
|
|
817
|
-
}
|
|
818
|
-
}),
|
|
819
|
-
create: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
|
|
820
|
-
// process the entire create subtree separately
|
|
821
|
-
yield _create(model, args, context);
|
|
822
|
-
// remove it from the update payload
|
|
823
|
-
this.removeFromParent(context.parent, 'create', args);
|
|
824
|
-
// don't visit payload
|
|
825
|
-
return false;
|
|
826
|
-
}),
|
|
827
|
-
createMany: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
|
|
828
|
-
// process createMany separately
|
|
829
|
-
yield _createMany(model, args, context);
|
|
830
|
-
// remove it from the update payload
|
|
831
|
-
delete context.parent.createMany;
|
|
832
|
-
// don't visit payload
|
|
833
|
-
return false;
|
|
834
|
-
}),
|
|
835
|
-
upsert: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
|
|
836
|
-
var _a;
|
|
837
|
-
// build a unique query including upstream conditions
|
|
838
|
-
const uniqueFilter = yield this.policyUtils.buildReversedQuery(db, context);
|
|
839
|
-
// branch based on if the update target exists
|
|
840
|
-
const existing = yield this.policyUtils.checkExistence(db, model, uniqueFilter);
|
|
841
|
-
if (existing) {
|
|
842
|
-
// update case
|
|
843
|
-
// check pre-update guard
|
|
844
|
-
yield this.policyUtils.checkPolicyForUnique(model, existing, 'update', db, this.queryUtils.getFieldsWithDefinedValues(args.update));
|
|
845
|
-
// handle the case where id fields are updated
|
|
846
|
-
const postUpdateIds = this.calculatePostUpdateIds(model, existing, args.update);
|
|
847
|
-
// register post-update check
|
|
848
|
-
yield _registerPostUpdateCheck(model, existing, postUpdateIds);
|
|
849
|
-
// convert upsert to update
|
|
850
|
-
const convertedUpdate = {
|
|
851
|
-
where: (_a = args.where) !== null && _a !== void 0 ? _a : {},
|
|
852
|
-
data: this.validateUpdateInputSchema(model, args.update),
|
|
853
|
-
};
|
|
854
|
-
this.mergeToParent(context.parent, 'update', convertedUpdate);
|
|
855
|
-
this.removeFromParent(context.parent, 'upsert', args);
|
|
856
|
-
// continue visiting the new payload
|
|
857
|
-
return convertedUpdate;
|
|
858
|
-
}
|
|
859
|
-
else {
|
|
860
|
-
// create case
|
|
861
|
-
// process the entire create subtree separately
|
|
862
|
-
yield _create(model, args.create, context);
|
|
863
|
-
// remove it from the update payload
|
|
864
|
-
this.removeFromParent(context.parent, 'upsert', args);
|
|
865
|
-
// don't visit payload
|
|
866
|
-
return false;
|
|
867
|
-
}
|
|
868
|
-
}),
|
|
869
|
-
connect: (model, args, context) => __awaiter(this, void 0, void 0, function* () { return _connectDisconnect(model, args, context, 'connect'); }),
|
|
870
|
-
connectOrCreate: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
|
|
871
|
-
// the where condition is already unique, so we can use it to check if the target exists
|
|
872
|
-
const existing = yield this.policyUtils.checkExistence(db, model, args.where);
|
|
873
|
-
if (existing) {
|
|
874
|
-
// connect
|
|
875
|
-
yield _connectDisconnect(model, args.where, context, 'connect');
|
|
876
|
-
return true;
|
|
877
|
-
}
|
|
878
|
-
else {
|
|
879
|
-
// create
|
|
880
|
-
const created = yield _create(model, args.create, context);
|
|
881
|
-
const upperContext = context.nestingPath[context.nestingPath.length - 2];
|
|
882
|
-
if ((upperContext === null || upperContext === void 0 ? void 0 : upperContext.where) && context.field) {
|
|
883
|
-
// check if the where clause of the upper context references the id
|
|
884
|
-
// of the connected entity, if so, we need to update it
|
|
885
|
-
this.overrideForeignKeyFields(upperContext.model, upperContext.where, context.field, created);
|
|
886
|
-
}
|
|
887
|
-
// remove the payload from the parent
|
|
888
|
-
this.removeFromParent(context.parent, 'connectOrCreate', args);
|
|
889
|
-
return false;
|
|
890
|
-
}
|
|
891
|
-
}),
|
|
892
|
-
disconnect: (model, args, context) => __awaiter(this, void 0, void 0, function* () { return _connectDisconnect(model, args, context, 'disconnect'); }),
|
|
893
|
-
set: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
|
|
894
|
-
// find the set of items to be replaced
|
|
895
|
-
const reversedQuery = yield this.policyUtils.buildReversedQuery(db, context);
|
|
896
|
-
const findCurrSetArgs = {
|
|
897
|
-
select: this.policyUtils.makeIdSelection(model),
|
|
898
|
-
where: reversedQuery,
|
|
899
|
-
};
|
|
900
|
-
if (this.shouldLogQuery) {
|
|
901
|
-
this.logger.info(`[policy] \`findMany\` ${model}:\n${(0, utils_1.formatObject)(findCurrSetArgs)}`);
|
|
902
|
-
}
|
|
903
|
-
const currentSet = yield db[model].findMany(findCurrSetArgs);
|
|
904
|
-
// register current set for update (foreign key)
|
|
905
|
-
yield Promise.all(currentSet.map((item) => _connectDisconnect(model, item, context, 'disconnect')));
|
|
906
|
-
// proceed with connecting the new set
|
|
907
|
-
yield Promise.all((0, cross_1.enumerate)(args).map((item) => _connectDisconnect(model, item, context, 'connect')));
|
|
908
|
-
}),
|
|
909
|
-
delete: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
|
|
910
|
-
// build a unique query including upstream conditions
|
|
911
|
-
const uniqueFilter = yield this.policyUtils.buildReversedQuery(db, context);
|
|
912
|
-
// handle not-found
|
|
913
|
-
yield this.policyUtils.checkExistence(db, model, uniqueFilter, true);
|
|
914
|
-
// check delete guard
|
|
915
|
-
yield this.policyUtils.checkPolicyForUnique(model, uniqueFilter, 'delete', db, []);
|
|
916
|
-
}),
|
|
917
|
-
deleteMany: (model, args, context) => __awaiter(this, void 0, void 0, function* () {
|
|
918
|
-
const guard = yield this.policyUtils.getAuthGuard(db, model, 'delete');
|
|
919
|
-
if (this.policyUtils.isTrue(guard) || this.policyUtils.isFalse(guard)) {
|
|
920
|
-
// inject simple auth guard
|
|
921
|
-
context.parent.deleteMany = this.policyUtils.and(args, guard);
|
|
922
|
-
}
|
|
923
|
-
else {
|
|
924
|
-
// we have to process `deleteMany` separately because the guard may contain
|
|
925
|
-
// filters using relation fields which are not allowed in nested `deleteMany`
|
|
926
|
-
const reversedQuery = yield this.policyUtils.buildReversedQuery(db, context);
|
|
927
|
-
const deleteWhere = this.policyUtils.and(reversedQuery, guard);
|
|
928
|
-
if (this.shouldLogQuery) {
|
|
929
|
-
this.logger.info(`[policy] \`deleteMany\` ${model}:\n${(0, utils_1.formatObject)({ where: deleteWhere })}`);
|
|
930
|
-
}
|
|
931
|
-
yield db[model].deleteMany({ where: deleteWhere });
|
|
932
|
-
delete context.parent.deleteMany;
|
|
933
|
-
}
|
|
934
|
-
}),
|
|
935
|
-
});
|
|
936
|
-
yield visitor.visit(this.model, 'update', args);
|
|
937
|
-
// finally proceed with the update
|
|
938
|
-
if (this.shouldLogQuery) {
|
|
939
|
-
this.logger.info(`[policy] \`update\` ${this.model}: ${(0, utils_1.formatObject)(args)}`);
|
|
940
|
-
}
|
|
941
|
-
const result = yield db[this.model].update({
|
|
942
|
-
where: args.where,
|
|
943
|
-
data: args.data,
|
|
944
|
-
select: this.policyUtils.makeIdSelection(this.model),
|
|
945
|
-
});
|
|
946
|
-
return { result, postWriteChecks };
|
|
947
|
-
});
|
|
948
|
-
}
|
|
949
|
-
// calculate id fields used for post-update check given an update payload
|
|
950
|
-
calculatePostUpdateIds(_model, currentIds, updatePayload) {
|
|
951
|
-
const result = this.policyUtils.safeClone(currentIds);
|
|
952
|
-
for (const key of Object.keys(currentIds)) {
|
|
953
|
-
const updateValue = updatePayload[key];
|
|
954
|
-
if (typeof updateValue === 'string' || typeof updateValue === 'number' || typeof updateValue === 'bigint') {
|
|
955
|
-
result[key] = updateValue;
|
|
956
|
-
}
|
|
957
|
-
}
|
|
958
|
-
return result;
|
|
959
|
-
}
|
|
960
|
-
// updates foreign key fields inside `payload` based on relation id fields in `newIds`
|
|
961
|
-
overrideForeignKeyFields(model, payload, relation, newIds) {
|
|
962
|
-
if (!relation.foreignKeyMapping || Object.keys(relation.foreignKeyMapping).length === 0) {
|
|
963
|
-
return;
|
|
964
|
-
}
|
|
965
|
-
// override foreign key values
|
|
966
|
-
for (const [id, fk] of Object.entries(relation.foreignKeyMapping)) {
|
|
967
|
-
if (payload[fk] !== undefined && newIds[id] !== undefined) {
|
|
968
|
-
payload[fk] = newIds[id];
|
|
969
|
-
}
|
|
970
|
-
}
|
|
971
|
-
// deal with compound id fields
|
|
972
|
-
const uniqueConstraints = this.policyUtils.getUniqueConstraints(model);
|
|
973
|
-
for (const [name, constraint] of Object.entries(uniqueConstraints)) {
|
|
974
|
-
if (constraint.fields.length > 1) {
|
|
975
|
-
const target = payload[name];
|
|
976
|
-
if (target) {
|
|
977
|
-
for (const [id, fk] of Object.entries(relation.foreignKeyMapping)) {
|
|
978
|
-
if (target[fk] !== undefined && newIds[id] !== undefined) {
|
|
979
|
-
target[fk] = newIds[id];
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
// Validates the given update payload against Zod schema if any
|
|
987
|
-
validateUpdateInputSchema(model, data) {
|
|
988
|
-
if (!data) {
|
|
989
|
-
return data;
|
|
990
|
-
}
|
|
991
|
-
// update payload can contain non-literal fields, like:
|
|
992
|
-
// { x: { increment: 1 } }
|
|
993
|
-
// we should only validate literal fields
|
|
994
|
-
const literalData = Object.entries(data).reduce((acc, [k, v]) => (Object.assign(Object.assign({}, acc), (typeof v !== 'object' ? { [k]: v } : {}))), {});
|
|
995
|
-
const validatedData = this.policyUtils.validateZodSchema(model, 'update', literalData, false, (err) => {
|
|
996
|
-
throw this.policyUtils.deniedByPolicy(model, 'update', `input failed validation: ${(0, zod_validation_error_1.fromZodError)(err)}`, constants_1.CrudFailureReason.DATA_VALIDATION_VIOLATION, err);
|
|
997
|
-
});
|
|
998
|
-
// schema may have transformed field values, use it to overwrite the original data
|
|
999
|
-
return Object.assign(Object.assign({}, data), validatedData);
|
|
1000
|
-
}
|
|
1001
|
-
updateMany(args) {
|
|
1002
|
-
return this.doUpdateMany(args, 'updateMany');
|
|
1003
|
-
}
|
|
1004
|
-
updateManyAndReturn(args) {
|
|
1005
|
-
return this.doUpdateMany(args, 'updateManyAndReturn');
|
|
1006
|
-
}
|
|
1007
|
-
doUpdateMany(args, action) {
|
|
1008
|
-
if (!args) {
|
|
1009
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
|
|
1010
|
-
}
|
|
1011
|
-
if (!args.data) {
|
|
1012
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'data field is required in query argument');
|
|
1013
|
-
}
|
|
1014
|
-
return (0, promise_1.createDeferredPromise)(() => __awaiter(this, void 0, void 0, function* () {
|
|
1015
|
-
var _a, _b;
|
|
1016
|
-
this.policyUtils.tryReject(this.prisma, this.model, 'update');
|
|
1017
|
-
const origArgs = args;
|
|
1018
|
-
args = this.policyUtils.safeClone(args);
|
|
1019
|
-
this.policyUtils.injectAuthGuardAsWhere(this.prisma, args, this.model, 'update');
|
|
1020
|
-
args.data = this.validateUpdateInputSchema(this.model, args.data);
|
|
1021
|
-
const entityChecker = this.policyUtils.getEntityChecker(this.model, 'update');
|
|
1022
|
-
const canProceedWithoutTransaction =
|
|
1023
|
-
// no post-update rules
|
|
1024
|
-
!this.policyUtils.hasAuthGuard(this.model, 'postUpdate') &&
|
|
1025
|
-
// no Zod schema
|
|
1026
|
-
!this.policyUtils.getZodSchema(this.model) &&
|
|
1027
|
-
// no entity checker
|
|
1028
|
-
!entityChecker;
|
|
1029
|
-
if (canProceedWithoutTransaction) {
|
|
1030
|
-
// proceed without a transaction
|
|
1031
|
-
if (this.shouldLogQuery) {
|
|
1032
|
-
this.logger.info(`[policy] \`updateMany\` ${this.model}: ${(0, utils_1.formatObject)(args)}`);
|
|
1033
|
-
}
|
|
1034
|
-
if (action === 'updateMany') {
|
|
1035
|
-
return this.modelClient.updateMany(args);
|
|
1036
|
-
}
|
|
1037
|
-
else {
|
|
1038
|
-
// make sure only id fields are returned so we can directly use the result
|
|
1039
|
-
// for read-back check
|
|
1040
|
-
const updatedArg = Object.assign(Object.assign({}, args), { select: this.policyUtils.makeIdSelection(this.model), include: undefined });
|
|
1041
|
-
const updated = yield this.modelClient.updateManyAndReturn(updatedArg);
|
|
1042
|
-
// process read-back
|
|
1043
|
-
const result = yield Promise.all(updated.map((item) => this.policyUtils.readBack(this.prisma, this.model, 'update', origArgs, item)));
|
|
1044
|
-
// throw read-back error if any of create result read-back fails
|
|
1045
|
-
const error = (_a = result.find((r) => !!r.error)) === null || _a === void 0 ? void 0 : _a.error;
|
|
1046
|
-
if (error) {
|
|
1047
|
-
throw error;
|
|
1048
|
-
}
|
|
1049
|
-
else {
|
|
1050
|
-
return result.map((r) => r.result);
|
|
1051
|
-
}
|
|
1052
|
-
}
|
|
1053
|
-
}
|
|
1054
|
-
// collect post-update checks
|
|
1055
|
-
const postWriteChecks = [];
|
|
1056
|
-
const result = yield this.queryUtils.transaction(this.prisma, (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
1057
|
-
// collect pre-update values
|
|
1058
|
-
let select = this.policyUtils.makeIdSelection(this.model);
|
|
1059
|
-
const preValueSelect = this.policyUtils.getPreValueSelect(this.model);
|
|
1060
|
-
if (preValueSelect) {
|
|
1061
|
-
select = Object.assign(Object.assign({}, select), preValueSelect);
|
|
1062
|
-
}
|
|
1063
|
-
// merge selection required for running additional checker
|
|
1064
|
-
const entityChecker = this.policyUtils.getEntityChecker(this.model, 'update');
|
|
1065
|
-
if (entityChecker === null || entityChecker === void 0 ? void 0 : entityChecker.selector) {
|
|
1066
|
-
select = (0, deepmerge_1.default)(select, entityChecker.selector);
|
|
1067
|
-
}
|
|
1068
|
-
const currentSetQuery = { select, where: args.where };
|
|
1069
|
-
this.policyUtils.injectAuthGuardAsWhere(tx, currentSetQuery, this.model, 'update');
|
|
1070
|
-
if (this.shouldLogQuery) {
|
|
1071
|
-
this.logger.info(`[policy] \`findMany\` ${this.model}: ${(0, utils_1.formatObject)(currentSetQuery)}`);
|
|
1072
|
-
}
|
|
1073
|
-
let candidates = yield tx[this.model].findMany(currentSetQuery);
|
|
1074
|
-
if (entityChecker) {
|
|
1075
|
-
// filter candidates with additional checker and build an id filter
|
|
1076
|
-
const r = this.buildIdFilterWithEntityChecker(candidates, entityChecker.func);
|
|
1077
|
-
candidates = r.filteredCandidates;
|
|
1078
|
-
// merge id filter into update's where clause
|
|
1079
|
-
args.where = args.where ? { AND: [args.where, r.idFilter] } : r.idFilter;
|
|
1080
|
-
}
|
|
1081
|
-
postWriteChecks.push(...candidates.map((preValue) => ({
|
|
1082
|
-
model: this.model,
|
|
1083
|
-
operation: 'postUpdate',
|
|
1084
|
-
uniqueFilter: this.policyUtils.getEntityIds(this.model, preValue),
|
|
1085
|
-
preValue: preValueSelect ? preValue : undefined,
|
|
1086
|
-
})));
|
|
1087
|
-
// proceed with the update
|
|
1088
|
-
if (this.shouldLogQuery) {
|
|
1089
|
-
this.logger.info(`[policy] \`updateMany\` in tx for ${this.model}: ${(0, utils_1.formatObject)(args)}`);
|
|
1090
|
-
}
|
|
1091
|
-
if (action === 'updateMany') {
|
|
1092
|
-
const result = yield tx[this.model].updateMany(args);
|
|
1093
|
-
// run post-write checks
|
|
1094
|
-
yield this.runPostWriteChecks(postWriteChecks, tx);
|
|
1095
|
-
return result;
|
|
1096
|
-
}
|
|
1097
|
-
else {
|
|
1098
|
-
// make sure only id fields are returned so we can directly use the result
|
|
1099
|
-
// for read-back check
|
|
1100
|
-
const updatedArg = Object.assign(Object.assign({}, args), { select: this.policyUtils.makeIdSelection(this.model), include: undefined });
|
|
1101
|
-
const result = yield tx[this.model].updateManyAndReturn(updatedArg);
|
|
1102
|
-
// run post-write checks
|
|
1103
|
-
yield this.runPostWriteChecks(postWriteChecks, tx);
|
|
1104
|
-
return result;
|
|
1105
|
-
}
|
|
1106
|
-
}));
|
|
1107
|
-
if (action === 'updateMany') {
|
|
1108
|
-
// no further processing needed
|
|
1109
|
-
return result;
|
|
1110
|
-
}
|
|
1111
|
-
else {
|
|
1112
|
-
// process read-back
|
|
1113
|
-
const readBackResult = yield Promise.all(result.map((item) => this.policyUtils.readBack(this.prisma, this.model, 'update', origArgs, item)));
|
|
1114
|
-
// throw read-back error if any of the update result read-back fails
|
|
1115
|
-
const error = (_b = readBackResult.find((r) => !!r.error)) === null || _b === void 0 ? void 0 : _b.error;
|
|
1116
|
-
if (error) {
|
|
1117
|
-
throw error;
|
|
1118
|
-
}
|
|
1119
|
-
else {
|
|
1120
|
-
return readBackResult.map((r) => r.result);
|
|
1121
|
-
}
|
|
1122
|
-
}
|
|
1123
|
-
}));
|
|
1124
|
-
}
|
|
1125
|
-
upsert(args) {
|
|
1126
|
-
if (!args) {
|
|
1127
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
|
|
1128
|
-
}
|
|
1129
|
-
if (!args.where) {
|
|
1130
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'where field is required in query argument');
|
|
1131
|
-
}
|
|
1132
|
-
if (!args.create) {
|
|
1133
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'create field is required in query argument');
|
|
1134
|
-
}
|
|
1135
|
-
if (!args.update) {
|
|
1136
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'update field is required in query argument');
|
|
1137
|
-
}
|
|
1138
|
-
return (0, promise_1.createDeferredPromise)(() => __awaiter(this, void 0, void 0, function* () {
|
|
1139
|
-
this.policyUtils.tryReject(this.prisma, this.model, 'create');
|
|
1140
|
-
this.policyUtils.tryReject(this.prisma, this.model, 'update');
|
|
1141
|
-
args = this.policyUtils.safeClone(args);
|
|
1142
|
-
// We can call the native "upsert" because we can't tell if an entity was created or updated
|
|
1143
|
-
// for doing post-write check accordingly. Instead, decompose it into create or update.
|
|
1144
|
-
const { result, error } = yield this.queryUtils.transaction(this.prisma, (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
1145
|
-
const { where, create, update } = args, rest = __rest(args, ["where", "create", "update"]);
|
|
1146
|
-
const existing = yield this.policyUtils.checkExistence(tx, this.model, where);
|
|
1147
|
-
if (existing) {
|
|
1148
|
-
// update case
|
|
1149
|
-
const { result, postWriteChecks } = yield this.doUpdate(Object.assign({ where: this.policyUtils.composeCompoundUniqueField(this.model, existing), data: update }, rest), tx);
|
|
1150
|
-
yield this.runPostWriteChecks(postWriteChecks, tx);
|
|
1151
|
-
return this.policyUtils.readBack(tx, this.model, 'update', args, result);
|
|
1152
|
-
}
|
|
1153
|
-
else {
|
|
1154
|
-
// create case
|
|
1155
|
-
const { result, postWriteChecks } = yield this.doCreate(this.model, Object.assign({ data: create }, rest), tx);
|
|
1156
|
-
yield this.runPostWriteChecks(postWriteChecks, tx);
|
|
1157
|
-
return this.policyUtils.readBack(tx, this.model, 'create', args, result);
|
|
1158
|
-
}
|
|
1159
|
-
}));
|
|
1160
|
-
if (error) {
|
|
1161
|
-
throw error;
|
|
1162
|
-
}
|
|
1163
|
-
else {
|
|
1164
|
-
return result;
|
|
1165
|
-
}
|
|
1166
|
-
}));
|
|
1167
|
-
}
|
|
1168
|
-
//#endregion
|
|
1169
|
-
//#region Delete
|
|
1170
|
-
// "delete" works against a single entity, and is rejected if the entity fails policy check.
|
|
1171
|
-
// "deleteMany" works against a set of entities, entities that fail policy check are filtered out.
|
|
1172
|
-
delete(args) {
|
|
1173
|
-
if (!args) {
|
|
1174
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
|
|
1175
|
-
}
|
|
1176
|
-
if (!args.where) {
|
|
1177
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'where field is required in query argument');
|
|
1178
|
-
}
|
|
1179
|
-
return (0, promise_1.createDeferredPromise)(() => __awaiter(this, void 0, void 0, function* () {
|
|
1180
|
-
this.policyUtils.tryReject(this.prisma, this.model, 'delete');
|
|
1181
|
-
const { result, error } = yield this.queryUtils.transaction(this.prisma, (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
1182
|
-
// do a read-back before delete
|
|
1183
|
-
const r = yield this.policyUtils.readBack(tx, this.model, 'delete', args, args.where);
|
|
1184
|
-
const error = r.error;
|
|
1185
|
-
const read = r.result;
|
|
1186
|
-
// check existence
|
|
1187
|
-
yield this.policyUtils.checkExistence(tx, this.model, args.where, true);
|
|
1188
|
-
// inject delete guard
|
|
1189
|
-
yield this.policyUtils.checkPolicyForUnique(this.model, args.where, 'delete', tx, []);
|
|
1190
|
-
// proceed with the deletion
|
|
1191
|
-
if (this.shouldLogQuery) {
|
|
1192
|
-
this.logger.info(`[policy] \`delete\` ${this.model}:\n${(0, utils_1.formatObject)(args)}`);
|
|
1193
|
-
}
|
|
1194
|
-
yield tx[this.model].delete(args);
|
|
1195
|
-
return { result: read, error };
|
|
1196
|
-
}));
|
|
1197
|
-
if (error) {
|
|
1198
|
-
throw error;
|
|
1199
|
-
}
|
|
1200
|
-
else {
|
|
1201
|
-
return result;
|
|
1202
|
-
}
|
|
1203
|
-
}));
|
|
1204
|
-
}
|
|
1205
|
-
deleteMany(args) {
|
|
1206
|
-
return (0, promise_1.createDeferredPromise)(() => {
|
|
1207
|
-
this.policyUtils.tryReject(this.prisma, this.model, 'delete');
|
|
1208
|
-
// inject policy conditions
|
|
1209
|
-
args = this.policyUtils.safeClone(args);
|
|
1210
|
-
this.policyUtils.injectAuthGuardAsWhere(this.prisma, args, this.model, 'delete');
|
|
1211
|
-
const entityChecker = this.policyUtils.getEntityChecker(this.model, 'delete');
|
|
1212
|
-
if (entityChecker) {
|
|
1213
|
-
// additional checker exists, need to run deletion inside a transaction
|
|
1214
|
-
return this.queryUtils.transaction(this.prisma, (tx) => __awaiter(this, void 0, void 0, function* () {
|
|
1215
|
-
var _a, _b;
|
|
1216
|
-
// find the delete candidates, selecting id fields and fields needed for
|
|
1217
|
-
// running the additional checker
|
|
1218
|
-
let candidateSelect = this.policyUtils.makeIdSelection(this.model);
|
|
1219
|
-
if (entityChecker.selector) {
|
|
1220
|
-
candidateSelect = (0, deepmerge_1.default)(candidateSelect, entityChecker.selector);
|
|
1221
|
-
}
|
|
1222
|
-
if (this.shouldLogQuery) {
|
|
1223
|
-
this.logger.info(`[policy] \`findMany\` ${this.model}: ${(0, utils_1.formatObject)({
|
|
1224
|
-
where: (_a = args.where) !== null && _a !== void 0 ? _a : {},
|
|
1225
|
-
select: candidateSelect,
|
|
1226
|
-
})}`);
|
|
1227
|
-
}
|
|
1228
|
-
const candidates = yield tx[this.model].findMany({
|
|
1229
|
-
where: (_b = args.where) !== null && _b !== void 0 ? _b : {},
|
|
1230
|
-
select: candidateSelect,
|
|
1231
|
-
});
|
|
1232
|
-
// build a ID filter based on id values filtered by the additional checker
|
|
1233
|
-
const { idFilter } = this.buildIdFilterWithEntityChecker(candidates, entityChecker.func);
|
|
1234
|
-
// merge the ID filter into the where clause
|
|
1235
|
-
args.where = args.where ? { AND: [args.where, idFilter] } : idFilter;
|
|
1236
|
-
// finally, conduct the deletion with the combined where clause
|
|
1237
|
-
if (this.shouldLogQuery) {
|
|
1238
|
-
this.logger.info(`[policy] \`deleteMany\` in tx for ${this.model}:\n${(0, utils_1.formatObject)(args)}`);
|
|
1239
|
-
}
|
|
1240
|
-
return tx[this.model].deleteMany(args);
|
|
1241
|
-
}));
|
|
1242
|
-
}
|
|
1243
|
-
else {
|
|
1244
|
-
// conduct the deletion directly
|
|
1245
|
-
if (this.shouldLogQuery) {
|
|
1246
|
-
this.logger.info(`[policy] \`deleteMany\` ${this.model}:\n${(0, utils_1.formatObject)(args)}`);
|
|
1247
|
-
}
|
|
1248
|
-
return this.modelClient.deleteMany(args);
|
|
1249
|
-
}
|
|
1250
|
-
});
|
|
1251
|
-
}
|
|
1252
|
-
//#endregion
|
|
1253
|
-
//#region Aggregation
|
|
1254
|
-
aggregate(args) {
|
|
1255
|
-
if (!args) {
|
|
1256
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
|
|
1257
|
-
}
|
|
1258
|
-
return (0, promise_1.createDeferredPromise)(() => {
|
|
1259
|
-
args = this.policyUtils.safeClone(args);
|
|
1260
|
-
// inject policy conditions
|
|
1261
|
-
this.policyUtils.injectAuthGuardAsWhere(this.prisma, args, this.model, 'read');
|
|
1262
|
-
if (this.shouldLogQuery) {
|
|
1263
|
-
this.logger.info(`[policy] \`aggregate\` ${this.model}:\n${(0, utils_1.formatObject)(args)}`);
|
|
1264
|
-
}
|
|
1265
|
-
return this.modelClient.aggregate(args);
|
|
1266
|
-
});
|
|
1267
|
-
}
|
|
1268
|
-
groupBy(args) {
|
|
1269
|
-
if (!args) {
|
|
1270
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'query argument is required');
|
|
1271
|
-
}
|
|
1272
|
-
return (0, promise_1.createDeferredPromise)(() => {
|
|
1273
|
-
args = this.policyUtils.safeClone(args);
|
|
1274
|
-
// inject policy conditions
|
|
1275
|
-
this.policyUtils.injectAuthGuardAsWhere(this.prisma, args, this.model, 'read');
|
|
1276
|
-
if (this.shouldLogQuery) {
|
|
1277
|
-
this.logger.info(`[policy] \`groupBy\` ${this.model}:\n${(0, utils_1.formatObject)(args)}`);
|
|
1278
|
-
}
|
|
1279
|
-
return this.modelClient.groupBy(args);
|
|
1280
|
-
});
|
|
1281
|
-
}
|
|
1282
|
-
count(args) {
|
|
1283
|
-
return (0, promise_1.createDeferredPromise)(() => {
|
|
1284
|
-
// inject policy conditions
|
|
1285
|
-
args = args ? this.policyUtils.safeClone(args) : {};
|
|
1286
|
-
this.policyUtils.injectAuthGuardAsWhere(this.prisma, args, this.model, 'read');
|
|
1287
|
-
if (this.shouldLogQuery) {
|
|
1288
|
-
this.logger.info(`[policy] \`count\` ${this.model}:\n${(0, utils_1.formatObject)(args)}`);
|
|
1289
|
-
}
|
|
1290
|
-
return this.modelClient.count(args);
|
|
1291
|
-
});
|
|
1292
|
-
}
|
|
1293
|
-
//#endregion
|
|
1294
|
-
//#region Prisma Pulse
|
|
1295
|
-
subscribe(args) {
|
|
1296
|
-
return this.handleSubscribeStream('subscribe', args);
|
|
1297
|
-
}
|
|
1298
|
-
stream(args) {
|
|
1299
|
-
return this.handleSubscribeStream('stream', args);
|
|
1300
|
-
}
|
|
1301
|
-
handleSubscribeStream(action, args) {
|
|
1302
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1303
|
-
if (!args) {
|
|
1304
|
-
// include all
|
|
1305
|
-
args = { create: {}, update: {}, delete: {} };
|
|
1306
|
-
}
|
|
1307
|
-
else {
|
|
1308
|
-
if (typeof args !== 'object') {
|
|
1309
|
-
throw (0, utils_1.prismaClientValidationError)(this.prisma, this.prismaModule, 'argument must be an object');
|
|
1310
|
-
}
|
|
1311
|
-
args = this.policyUtils.safeClone(args);
|
|
1312
|
-
}
|
|
1313
|
-
// inject read guard as subscription filter
|
|
1314
|
-
for (const key of ['create', 'update', 'delete']) {
|
|
1315
|
-
if (args[key] === undefined) {
|
|
1316
|
-
continue;
|
|
1317
|
-
}
|
|
1318
|
-
// "update" has an extra layer of "after"
|
|
1319
|
-
const payload = key === 'update' ? args[key].after : args[key];
|
|
1320
|
-
const toInject = { where: payload };
|
|
1321
|
-
this.policyUtils.injectForRead(this.prisma, this.model, toInject);
|
|
1322
|
-
if (key === 'update') {
|
|
1323
|
-
// "update" has an extra layer of "after"
|
|
1324
|
-
args[key].after = toInject.where;
|
|
1325
|
-
}
|
|
1326
|
-
else {
|
|
1327
|
-
args[key] = toInject.where;
|
|
1328
|
-
}
|
|
1329
|
-
}
|
|
1330
|
-
if (this.shouldLogQuery) {
|
|
1331
|
-
this.logger.info(`[policy] \`${action}\` ${this.model}:\n${(0, utils_1.formatObject)(args)}`);
|
|
1332
|
-
}
|
|
1333
|
-
// Prisma Pulse returns an async iterable, which we need to wrap
|
|
1334
|
-
// and post-process the iteration results
|
|
1335
|
-
const iterable = yield this.modelClient[action](args);
|
|
1336
|
-
return {
|
|
1337
|
-
[Symbol.asyncIterator]: () => {
|
|
1338
|
-
const iter = iterable[Symbol.asyncIterator].bind(iterable)();
|
|
1339
|
-
return {
|
|
1340
|
-
next: () => __awaiter(this, void 0, void 0, function* () {
|
|
1341
|
-
const { done, value } = yield iter.next();
|
|
1342
|
-
let processedValue = value;
|
|
1343
|
-
if (value && 'action' in value) {
|
|
1344
|
-
switch (value.action) {
|
|
1345
|
-
case 'create':
|
|
1346
|
-
if ('created' in value) {
|
|
1347
|
-
processedValue = Object.assign(Object.assign({}, value), { created: this.policyUtils.postProcessForRead(value.created, this.model, {}) });
|
|
1348
|
-
}
|
|
1349
|
-
break;
|
|
1350
|
-
case 'update':
|
|
1351
|
-
if ('before' in value) {
|
|
1352
|
-
processedValue = Object.assign(Object.assign({}, value), { before: this.policyUtils.postProcessForRead(value.before, this.model, {}) });
|
|
1353
|
-
}
|
|
1354
|
-
if ('after' in value) {
|
|
1355
|
-
processedValue = Object.assign(Object.assign({}, value), { after: this.policyUtils.postProcessForRead(value.after, this.model, {}) });
|
|
1356
|
-
}
|
|
1357
|
-
break;
|
|
1358
|
-
case 'delete':
|
|
1359
|
-
if ('deleted' in value) {
|
|
1360
|
-
processedValue = Object.assign(Object.assign({}, value), { deleted: this.policyUtils.postProcessForRead(value.deleted, this.model, {}) });
|
|
1361
|
-
}
|
|
1362
|
-
break;
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
return { done, value: processedValue };
|
|
1366
|
-
}),
|
|
1367
|
-
return: () => { var _a; return (_a = iter.return) === null || _a === void 0 ? void 0 : _a.call(iter); },
|
|
1368
|
-
throw: () => { var _a; return (_a = iter.throw) === null || _a === void 0 ? void 0 : _a.call(iter); },
|
|
1369
|
-
};
|
|
1370
|
-
},
|
|
1371
|
-
};
|
|
1372
|
-
});
|
|
1373
|
-
}
|
|
1374
|
-
//#endregion
|
|
1375
|
-
//#region Check
|
|
1376
|
-
/**
|
|
1377
|
-
* Checks if the given operation is possibly allowed by the policy, without querying the database.
|
|
1378
|
-
* @param operation The CRUD operation.
|
|
1379
|
-
* @param fieldValues Extra field value filters to be combined with the policy constraints.
|
|
1380
|
-
*/
|
|
1381
|
-
check(args) {
|
|
1382
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1383
|
-
return (0, check_utils_1.checkPermission)(this.model, args, this.modelMeta, this.policyUtils, this.prisma, this.prismaModule);
|
|
1384
|
-
});
|
|
1385
|
-
}
|
|
1386
|
-
//#endregion
|
|
1387
|
-
//#region Utils
|
|
1388
|
-
get shouldLogQuery() {
|
|
1389
|
-
var _a;
|
|
1390
|
-
return !!((_a = this.options) === null || _a === void 0 ? void 0 : _a.logPrismaQuery) && this.logger.enabled('info');
|
|
1391
|
-
}
|
|
1392
|
-
runPostWriteChecks(postWriteChecks, db) {
|
|
1393
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
1394
|
-
yield Promise.all(postWriteChecks.map((_a) => __awaiter(this, [_a], void 0, function* ({ model, operation, uniqueFilter, preValue }) { return this.policyUtils.checkPolicyForUnique(model, uniqueFilter, operation, db, [], preValue); })));
|
|
1395
|
-
});
|
|
1396
|
-
}
|
|
1397
|
-
requireBackLink(fieldInfo) {
|
|
1398
|
-
(0, tiny_invariant_1.default)(fieldInfo.backLink, `back link not found for field ${fieldInfo.name}`);
|
|
1399
|
-
return (0, cross_1.requireField)(this.modelMeta, fieldInfo.type, fieldInfo.backLink);
|
|
1400
|
-
}
|
|
1401
|
-
mergeToParent(parent, key, value) {
|
|
1402
|
-
if (parent[key]) {
|
|
1403
|
-
if (Array.isArray(parent[key])) {
|
|
1404
|
-
parent[key].push(value);
|
|
1405
|
-
}
|
|
1406
|
-
else {
|
|
1407
|
-
parent[key] = [parent[key], value];
|
|
1408
|
-
}
|
|
1409
|
-
}
|
|
1410
|
-
else {
|
|
1411
|
-
parent[key] = value;
|
|
1412
|
-
}
|
|
1413
|
-
}
|
|
1414
|
-
removeFromParent(parent, key, data) {
|
|
1415
|
-
if (parent[key] === data) {
|
|
1416
|
-
delete parent[key];
|
|
1417
|
-
}
|
|
1418
|
-
else if (Array.isArray(parent[key])) {
|
|
1419
|
-
const idx = parent[key].indexOf(data);
|
|
1420
|
-
if (idx >= 0) {
|
|
1421
|
-
parent[key].splice(idx, 1);
|
|
1422
|
-
if (parent[key].length === 0) {
|
|
1423
|
-
delete parent[key];
|
|
1424
|
-
}
|
|
1425
|
-
}
|
|
1426
|
-
}
|
|
1427
|
-
}
|
|
1428
|
-
buildIdFilterWithEntityChecker(candidates, entityChecker) {
|
|
1429
|
-
const filteredCandidates = candidates.filter((value) => { var _a; return entityChecker(value, { user: (_a = this.context) === null || _a === void 0 ? void 0 : _a.user }); });
|
|
1430
|
-
const idFields = this.policyUtils.getIdFields(this.model);
|
|
1431
|
-
let idFilter;
|
|
1432
|
-
if (idFields.length === 1) {
|
|
1433
|
-
idFilter = { [idFields[0].name]: { in: filteredCandidates.map((x) => x[idFields[0].name]) } };
|
|
1434
|
-
}
|
|
1435
|
-
else {
|
|
1436
|
-
idFilter = { AND: filteredCandidates.map((x) => this.policyUtils.getIdFieldValues(this.model, x)) };
|
|
1437
|
-
}
|
|
1438
|
-
return { filteredCandidates, idFilter };
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
|
-
exports.PolicyProxyHandler = PolicyProxyHandler;
|
|
1442
|
-
//# sourceMappingURL=handler.js.map
|