@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.
Files changed (215) hide show
  1. package/LICENSE +1 -1
  2. package/dist/client.cjs +6094 -0
  3. package/dist/client.cjs.map +1 -0
  4. package/dist/client.d.cts +19 -0
  5. package/dist/client.d.ts +19 -0
  6. package/dist/client.js +6060 -0
  7. package/dist/client.js.map +1 -0
  8. package/dist/contract-DguafRNB.d.cts +1272 -0
  9. package/dist/contract-DguafRNB.d.ts +1272 -0
  10. package/dist/index.cjs +6088 -0
  11. package/dist/index.cjs.map +1 -0
  12. package/dist/index.d.cts +14 -0
  13. package/dist/index.d.ts +14 -0
  14. package/dist/index.js +6057 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/plugins/policy.cjs +2343 -0
  17. package/dist/plugins/policy.cjs.map +1 -0
  18. package/dist/plugins/policy.d.cts +24 -0
  19. package/dist/plugins/policy.d.ts +24 -0
  20. package/dist/plugins/policy.js +2307 -0
  21. package/dist/plugins/policy.js.map +1 -0
  22. package/dist/schema.cjs +110 -0
  23. package/dist/schema.cjs.map +1 -0
  24. package/dist/schema.d.cts +29 -0
  25. package/dist/schema.d.ts +29 -0
  26. package/dist/schema.js +85 -0
  27. package/dist/schema.js.map +1 -0
  28. package/dist/utils/pg-utils.cjs +39 -0
  29. package/dist/utils/pg-utils.cjs.map +1 -0
  30. package/dist/utils/pg-utils.d.cts +8 -0
  31. package/dist/utils/pg-utils.d.ts +8 -0
  32. package/dist/utils/pg-utils.js +16 -0
  33. package/dist/utils/pg-utils.js.map +1 -0
  34. package/{browser/index.js → dist/utils/sqlite-utils.cjs} +21 -37
  35. package/dist/utils/sqlite-utils.cjs.map +1 -0
  36. package/dist/utils/sqlite-utils.d.cts +8 -0
  37. package/dist/utils/sqlite-utils.d.ts +8 -0
  38. package/dist/utils/sqlite-utils.js +22 -0
  39. package/dist/utils/sqlite-utils.js.map +1 -0
  40. package/package.json +105 -114
  41. package/README.md +0 -5
  42. package/browser/index.d.mts +0 -13
  43. package/browser/index.d.ts +0 -13
  44. package/browser/index.js.map +0 -1
  45. package/browser/index.mjs +0 -33
  46. package/browser/index.mjs.map +0 -1
  47. package/constants.d.ts +0 -62
  48. package/constants.js +0 -76
  49. package/constants.js.map +0 -1
  50. package/cross/index.d.mts +0 -379
  51. package/cross/index.d.ts +0 -379
  52. package/cross/index.js +0 -903
  53. package/cross/index.js.map +0 -1
  54. package/cross/index.mjs +0 -861
  55. package/cross/index.mjs.map +0 -1
  56. package/edge.d.ts +0 -1
  57. package/edge.js +0 -18
  58. package/edge.js.map +0 -1
  59. package/encryption/index.d.ts +0 -25
  60. package/encryption/index.js +0 -74
  61. package/encryption/index.js.map +0 -1
  62. package/encryption/utils.d.ts +0 -9
  63. package/encryption/utils.js +0 -99
  64. package/encryption/utils.js.map +0 -1
  65. package/enhance-edge.d.ts +0 -1
  66. package/enhance-edge.js +0 -10
  67. package/enhance.d.ts +0 -1
  68. package/enhance.js +0 -10
  69. package/enhancements/edge/create-enhancement.d.ts +0 -42
  70. package/enhancements/edge/create-enhancement.js +0 -102
  71. package/enhancements/edge/create-enhancement.js.map +0 -1
  72. package/enhancements/edge/default-auth.d.ts +0 -8
  73. package/enhancements/edge/default-auth.js +0 -180
  74. package/enhancements/edge/default-auth.js.map +0 -1
  75. package/enhancements/edge/delegate.d.ts +0 -77
  76. package/enhancements/edge/delegate.js +0 -1294
  77. package/enhancements/edge/delegate.js.map +0 -1
  78. package/enhancements/edge/encryption.d.ts +0 -7
  79. package/enhancements/edge/encryption.js +0 -150
  80. package/enhancements/edge/encryption.js.map +0 -1
  81. package/enhancements/edge/index.d.ts +0 -4
  82. package/enhancements/edge/index.js +0 -21
  83. package/enhancements/edge/index.js.map +0 -1
  84. package/enhancements/edge/json-processor.d.ts +0 -7
  85. package/enhancements/edge/json-processor.js +0 -89
  86. package/enhancements/edge/json-processor.js.map +0 -1
  87. package/enhancements/edge/logger.d.ts +0 -29
  88. package/enhancements/edge/logger.js +0 -65
  89. package/enhancements/edge/logger.js.map +0 -1
  90. package/enhancements/edge/omit.d.ts +0 -7
  91. package/enhancements/edge/omit.js +0 -96
  92. package/enhancements/edge/omit.js.map +0 -1
  93. package/enhancements/edge/password.d.ts +0 -7
  94. package/enhancements/edge/password.js +0 -64
  95. package/enhancements/edge/password.js.map +0 -1
  96. package/enhancements/edge/policy/check-utils.d.ts +0 -5
  97. package/enhancements/edge/policy/check-utils.js +0 -20
  98. package/enhancements/edge/policy/check-utils.js.map +0 -1
  99. package/enhancements/edge/policy/handler.d.ts +0 -100
  100. package/enhancements/edge/policy/handler.js +0 -1442
  101. package/enhancements/edge/policy/handler.js.map +0 -1
  102. package/enhancements/edge/policy/index.d.ts +0 -19
  103. package/enhancements/edge/policy/index.js +0 -65
  104. package/enhancements/edge/policy/index.js.map +0 -1
  105. package/enhancements/edge/policy/policy-utils.d.ts +0 -181
  106. package/enhancements/edge/policy/policy-utils.js +0 -1357
  107. package/enhancements/edge/policy/policy-utils.js.map +0 -1
  108. package/enhancements/edge/promise.d.ts +0 -15
  109. package/enhancements/edge/promise.js +0 -99
  110. package/enhancements/edge/promise.js.map +0 -1
  111. package/enhancements/edge/proxy.d.ts +0 -120
  112. package/enhancements/edge/proxy.js +0 -287
  113. package/enhancements/edge/proxy.js.map +0 -1
  114. package/enhancements/edge/query-utils.d.ts +0 -53
  115. package/enhancements/edge/query-utils.js +0 -256
  116. package/enhancements/edge/query-utils.js.map +0 -1
  117. package/enhancements/edge/types.d.ts +0 -238
  118. package/enhancements/edge/types.js +0 -3
  119. package/enhancements/edge/types.js.map +0 -1
  120. package/enhancements/edge/utils.d.ts +0 -11
  121. package/enhancements/edge/utils.js +0 -49
  122. package/enhancements/edge/utils.js.map +0 -1
  123. package/enhancements/edge/where-visitor.d.ts +0 -32
  124. package/enhancements/edge/where-visitor.js +0 -86
  125. package/enhancements/edge/where-visitor.js.map +0 -1
  126. package/enhancements/node/create-enhancement.d.ts +0 -42
  127. package/enhancements/node/create-enhancement.js +0 -102
  128. package/enhancements/node/create-enhancement.js.map +0 -1
  129. package/enhancements/node/default-auth.d.ts +0 -8
  130. package/enhancements/node/default-auth.js +0 -180
  131. package/enhancements/node/default-auth.js.map +0 -1
  132. package/enhancements/node/delegate.d.ts +0 -77
  133. package/enhancements/node/delegate.js +0 -1294
  134. package/enhancements/node/delegate.js.map +0 -1
  135. package/enhancements/node/encryption.d.ts +0 -7
  136. package/enhancements/node/encryption.js +0 -150
  137. package/enhancements/node/encryption.js.map +0 -1
  138. package/enhancements/node/index.d.ts +0 -4
  139. package/enhancements/node/index.js +0 -21
  140. package/enhancements/node/index.js.map +0 -1
  141. package/enhancements/node/json-processor.d.ts +0 -7
  142. package/enhancements/node/json-processor.js +0 -89
  143. package/enhancements/node/json-processor.js.map +0 -1
  144. package/enhancements/node/logger.d.ts +0 -29
  145. package/enhancements/node/logger.js +0 -65
  146. package/enhancements/node/logger.js.map +0 -1
  147. package/enhancements/node/omit.d.ts +0 -7
  148. package/enhancements/node/omit.js +0 -96
  149. package/enhancements/node/omit.js.map +0 -1
  150. package/enhancements/node/password.d.ts +0 -7
  151. package/enhancements/node/password.js +0 -64
  152. package/enhancements/node/password.js.map +0 -1
  153. package/enhancements/node/policy/check-utils.d.ts +0 -5
  154. package/enhancements/node/policy/check-utils.js +0 -87
  155. package/enhancements/node/policy/check-utils.js.map +0 -1
  156. package/enhancements/node/policy/constraint-solver.d.ts +0 -27
  157. package/enhancements/node/policy/constraint-solver.js +0 -164
  158. package/enhancements/node/policy/constraint-solver.js.map +0 -1
  159. package/enhancements/node/policy/handler.d.ts +0 -100
  160. package/enhancements/node/policy/handler.js +0 -1442
  161. package/enhancements/node/policy/handler.js.map +0 -1
  162. package/enhancements/node/policy/index.d.ts +0 -19
  163. package/enhancements/node/policy/index.js +0 -65
  164. package/enhancements/node/policy/index.js.map +0 -1
  165. package/enhancements/node/policy/policy-utils.d.ts +0 -181
  166. package/enhancements/node/policy/policy-utils.js +0 -1357
  167. package/enhancements/node/policy/policy-utils.js.map +0 -1
  168. package/enhancements/node/promise.d.ts +0 -15
  169. package/enhancements/node/promise.js +0 -99
  170. package/enhancements/node/promise.js.map +0 -1
  171. package/enhancements/node/proxy.d.ts +0 -120
  172. package/enhancements/node/proxy.js +0 -287
  173. package/enhancements/node/proxy.js.map +0 -1
  174. package/enhancements/node/query-utils.d.ts +0 -53
  175. package/enhancements/node/query-utils.js +0 -256
  176. package/enhancements/node/query-utils.js.map +0 -1
  177. package/enhancements/node/types.d.ts +0 -238
  178. package/enhancements/node/types.js +0 -3
  179. package/enhancements/node/types.js.map +0 -1
  180. package/enhancements/node/utils.d.ts +0 -11
  181. package/enhancements/node/utils.js +0 -49
  182. package/enhancements/node/utils.js.map +0 -1
  183. package/enhancements/node/where-visitor.d.ts +0 -32
  184. package/enhancements/node/where-visitor.js +0 -86
  185. package/enhancements/node/where-visitor.js.map +0 -1
  186. package/error.d.ts +0 -11
  187. package/error.js +0 -22
  188. package/error.js.map +0 -1
  189. package/index.d.ts +0 -7
  190. package/index.js +0 -24
  191. package/index.js.map +0 -1
  192. package/model-meta.d.ts +0 -1
  193. package/model-meta.js +0 -10
  194. package/models.d.ts +0 -1
  195. package/models.js +0 -1
  196. package/types.d.ts +0 -180
  197. package/types.js +0 -4
  198. package/types.js.map +0 -1
  199. package/validation.d.ts +0 -24
  200. package/validation.js +0 -52
  201. package/validation.js.map +0 -1
  202. package/version.d.ts +0 -5
  203. package/version.js +0 -35
  204. package/version.js.map +0 -1
  205. package/zod/index.d.ts +0 -3
  206. package/zod/index.js +0 -5
  207. package/zod/input.d.ts +0 -1
  208. package/zod/input.js +0 -8
  209. package/zod/models.d.ts +0 -1
  210. package/zod/models.js +0 -8
  211. package/zod/objects.d.ts +0 -1
  212. package/zod/objects.js +0 -8
  213. package/zod-utils.d.ts +0 -12
  214. package/zod-utils.js +0 -97
  215. 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