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