@roundtreasury/prisma-extension-soft-delete 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +993 -0
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/lib/createSoftDeleteExtension.d.ts +2 -0
- package/dist/esm/lib/createSoftDeleteExtension.js +102 -0
- package/dist/esm/lib/helpers/createParams.d.ts +26 -0
- package/dist/esm/lib/helpers/createParams.js +374 -0
- package/dist/esm/lib/helpers/modifyResult.d.ts +4 -0
- package/dist/esm/lib/helpers/modifyResult.js +12 -0
- package/dist/esm/lib/types.d.ts +11 -0
- package/dist/esm/lib/types.js +1 -0
- package/dist/esm/lib/utils/nestedReads.d.ts +5 -0
- package/dist/esm/lib/utils/nestedReads.js +26 -0
- package/dist/esm/lib/utils/resultFiltering.d.ts +5 -0
- package/dist/esm/lib/utils/resultFiltering.js +17 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +20 -0
- package/dist/lib/createSoftDeleteExtension.d.ts +2 -0
- package/dist/lib/createSoftDeleteExtension.js +106 -0
- package/dist/lib/helpers/createParams.d.ts +26 -0
- package/dist/lib/helpers/createParams.js +393 -0
- package/dist/lib/helpers/modifyResult.d.ts +4 -0
- package/dist/lib/helpers/modifyResult.js +16 -0
- package/dist/lib/types.d.ts +11 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/utils/nestedReads.d.ts +5 -0
- package/dist/lib/utils/nestedReads.js +31 -0
- package/dist/lib/utils/resultFiltering.d.ts +5 -0
- package/dist/lib/utils/resultFiltering.js +22 -0
- package/package.json +75 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { Config } from "./types";
|
|
2
|
+
export declare function createSoftDeleteExtension({ models, defaultConfig, }: Config): (client: any) => import("@prisma/client/extension").PrismaClientExtends<import("@prisma/client/runtime/library").InternalArgs<{}, {}, {}, {}> & import("@prisma/client/runtime/library").DefaultArgs>;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { Prisma as PrismaExtensions } from "@prisma/client/extension";
|
|
2
|
+
import { withNestedOperations, } from "prisma-extension-nested-operations";
|
|
3
|
+
import { createAggregateParams, createCountParams, createDeleteManyParams, createDeleteParams, createFindFirstParams, createFindFirstOrThrowParams, createFindManyParams, createFindUniqueParams, createFindUniqueOrThrowParams, createIncludeParams, createSelectParams, createUpdateManyParams, createUpdateParams, createUpsertParams, createWhereParams, createGroupByParams, } from "./helpers/createParams";
|
|
4
|
+
import { modifyReadResult } from "./helpers/modifyResult";
|
|
5
|
+
export function createSoftDeleteExtension({ models, defaultConfig = {
|
|
6
|
+
field: "deleted",
|
|
7
|
+
createValue: Boolean,
|
|
8
|
+
allowToOneUpdates: false,
|
|
9
|
+
allowCompoundUniqueIndexWhere: false,
|
|
10
|
+
}, }) {
|
|
11
|
+
if (!defaultConfig.field) {
|
|
12
|
+
throw new Error("prisma-extension-soft-delete: defaultConfig.field is required");
|
|
13
|
+
}
|
|
14
|
+
if (!defaultConfig.createValue) {
|
|
15
|
+
throw new Error("prisma-extension-soft-delete: defaultConfig.createValue is required");
|
|
16
|
+
}
|
|
17
|
+
const modelConfig = {};
|
|
18
|
+
Object.keys(models).forEach((model) => {
|
|
19
|
+
const modelName = model;
|
|
20
|
+
const config = models[modelName];
|
|
21
|
+
if (config) {
|
|
22
|
+
modelConfig[modelName] =
|
|
23
|
+
typeof config === "boolean" && config ? defaultConfig : config;
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
const createParamsByModel = Object.keys(modelConfig).reduce((acc, model) => {
|
|
27
|
+
const config = modelConfig[model];
|
|
28
|
+
return {
|
|
29
|
+
...acc,
|
|
30
|
+
[model]: {
|
|
31
|
+
delete: createDeleteParams.bind(null, config),
|
|
32
|
+
deleteMany: createDeleteManyParams.bind(null, config),
|
|
33
|
+
update: createUpdateParams.bind(null, config),
|
|
34
|
+
updateMany: createUpdateManyParams.bind(null, config),
|
|
35
|
+
upsert: createUpsertParams.bind(null, config),
|
|
36
|
+
findFirst: createFindFirstParams.bind(null, config),
|
|
37
|
+
findFirstOrThrow: createFindFirstOrThrowParams.bind(null, config),
|
|
38
|
+
findUnique: createFindUniqueParams.bind(null, config),
|
|
39
|
+
findUniqueOrThrow: createFindUniqueOrThrowParams.bind(null, config),
|
|
40
|
+
findMany: createFindManyParams.bind(null, config),
|
|
41
|
+
count: createCountParams.bind(null, config),
|
|
42
|
+
aggregate: createAggregateParams.bind(null, config),
|
|
43
|
+
where: createWhereParams.bind(null, config),
|
|
44
|
+
include: createIncludeParams.bind(null, config),
|
|
45
|
+
select: createSelectParams.bind(null, config),
|
|
46
|
+
groupBy: createGroupByParams.bind(null, config),
|
|
47
|
+
},
|
|
48
|
+
};
|
|
49
|
+
}, {});
|
|
50
|
+
const modifyResultByModel = Object.keys(modelConfig).reduce((acc, model) => {
|
|
51
|
+
const config = modelConfig[model];
|
|
52
|
+
return {
|
|
53
|
+
...acc,
|
|
54
|
+
[model]: {
|
|
55
|
+
include: modifyReadResult.bind(null, config),
|
|
56
|
+
select: modifyReadResult.bind(null, config),
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}, {});
|
|
60
|
+
// before handling root params generate deleted value so it is consistent
|
|
61
|
+
// for the query. Add it to root params and get it from scope?
|
|
62
|
+
return PrismaExtensions.defineExtension((client) => {
|
|
63
|
+
return client.$extends({
|
|
64
|
+
query: {
|
|
65
|
+
$allModels: {
|
|
66
|
+
// @ts-expect-error - we don't know what the client is
|
|
67
|
+
$allOperations: withNestedOperations({
|
|
68
|
+
async $rootOperation(initialParams) {
|
|
69
|
+
var _a, _b;
|
|
70
|
+
const createParams = (_a = createParamsByModel[initialParams.model || ""]) === null || _a === void 0 ? void 0 : _a[initialParams.operation];
|
|
71
|
+
if (!createParams)
|
|
72
|
+
return initialParams.query(initialParams.args);
|
|
73
|
+
const { params, ctx } = createParams(initialParams);
|
|
74
|
+
const { model } = params;
|
|
75
|
+
const operationChanged = params.operation !== initialParams.operation;
|
|
76
|
+
const result = operationChanged
|
|
77
|
+
? // @ts-expect-error - we don't know what the client is
|
|
78
|
+
await client[model[0].toLowerCase() + model.slice(1)][params.operation](params.args)
|
|
79
|
+
: await params.query(params.args);
|
|
80
|
+
const modifyResult = (_b = modifyResultByModel[params.model || ""]) === null || _b === void 0 ? void 0 : _b[params.operation];
|
|
81
|
+
if (!modifyResult)
|
|
82
|
+
return result;
|
|
83
|
+
return modifyResult(result, params, ctx);
|
|
84
|
+
},
|
|
85
|
+
async $allNestedOperations(initialParams) {
|
|
86
|
+
var _a, _b;
|
|
87
|
+
const createParams = (_a = createParamsByModel[initialParams.model || ""]) === null || _a === void 0 ? void 0 : _a[initialParams.operation];
|
|
88
|
+
if (!createParams)
|
|
89
|
+
return initialParams.query(initialParams.args);
|
|
90
|
+
const { params, ctx } = createParams(initialParams);
|
|
91
|
+
const result = await params.query(params.args, params.operation);
|
|
92
|
+
const modifyResult = (_b = modifyResultByModel[params.model || ""]) === null || _b === void 0 ? void 0 : _b[params.operation];
|
|
93
|
+
if (!modifyResult)
|
|
94
|
+
return result;
|
|
95
|
+
return modifyResult(result, params, ctx);
|
|
96
|
+
},
|
|
97
|
+
}),
|
|
98
|
+
},
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { NestedParams } from "prisma-extension-nested-operations";
|
|
2
|
+
import { ModelConfig } from "../types";
|
|
3
|
+
export type Params = Omit<NestedParams<any>, "operation"> & {
|
|
4
|
+
operation: string;
|
|
5
|
+
};
|
|
6
|
+
export type CreateParamsReturn = {
|
|
7
|
+
params: Params;
|
|
8
|
+
ctx?: any;
|
|
9
|
+
};
|
|
10
|
+
export type CreateParams = (config: ModelConfig, params: Params) => CreateParamsReturn;
|
|
11
|
+
export declare const createDeleteParams: CreateParams;
|
|
12
|
+
export declare const createDeleteManyParams: CreateParams;
|
|
13
|
+
export declare const createUpdateParams: CreateParams;
|
|
14
|
+
export declare const createUpdateManyParams: CreateParams;
|
|
15
|
+
export declare const createUpsertParams: CreateParams;
|
|
16
|
+
export declare const createFindUniqueParams: CreateParams;
|
|
17
|
+
export declare const createFindUniqueOrThrowParams: CreateParams;
|
|
18
|
+
export declare const createFindFirstParams: CreateParams;
|
|
19
|
+
export declare const createFindFirstOrThrowParams: CreateParams;
|
|
20
|
+
export declare const createFindManyParams: CreateParams;
|
|
21
|
+
export declare const createGroupByParams: CreateParams;
|
|
22
|
+
export declare const createCountParams: CreateParams;
|
|
23
|
+
export declare const createAggregateParams: CreateParams;
|
|
24
|
+
export declare const createWhereParams: CreateParams;
|
|
25
|
+
export declare const createIncludeParams: CreateParams;
|
|
26
|
+
export declare const createSelectParams: CreateParams;
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
import { Prisma } from "@prisma/client";
|
|
2
|
+
import { addDeletedToSelect } from "../utils/nestedReads";
|
|
3
|
+
const uniqueFieldsByModel = {};
|
|
4
|
+
const uniqueIndexFieldsByModel = {};
|
|
5
|
+
Prisma.dmmf.datamodel.models.forEach((model) => {
|
|
6
|
+
// add unique fields derived from indexes
|
|
7
|
+
const uniqueIndexFields = [];
|
|
8
|
+
model.uniqueFields.forEach((field) => {
|
|
9
|
+
uniqueIndexFields.push(field.join("_"));
|
|
10
|
+
});
|
|
11
|
+
uniqueIndexFieldsByModel[model.name] = uniqueIndexFields;
|
|
12
|
+
// add id field and unique fields from @unique decorator
|
|
13
|
+
const uniqueFields = [];
|
|
14
|
+
model.fields.forEach((field) => {
|
|
15
|
+
if (field.isId || field.isUnique) {
|
|
16
|
+
uniqueFields.push(field.name);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
uniqueFieldsByModel[model.name] = uniqueFields;
|
|
20
|
+
});
|
|
21
|
+
export const createDeleteParams = ({ field, createValue }, params) => {
|
|
22
|
+
var _a, _b;
|
|
23
|
+
if (!params.model ||
|
|
24
|
+
// do nothing for delete: false
|
|
25
|
+
(typeof params.args === "boolean" && !params.args) ||
|
|
26
|
+
// do nothing for root delete without where to allow Prisma to throw
|
|
27
|
+
(!params.scope && !((_a = params.args) === null || _a === void 0 ? void 0 : _a.where))) {
|
|
28
|
+
return {
|
|
29
|
+
params,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
if (typeof params.args === "boolean") {
|
|
33
|
+
return {
|
|
34
|
+
params: {
|
|
35
|
+
...params,
|
|
36
|
+
operation: "update",
|
|
37
|
+
args: {
|
|
38
|
+
__passUpdateThrough: true,
|
|
39
|
+
[field]: createValue(true),
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
params: {
|
|
46
|
+
...params,
|
|
47
|
+
operation: "update",
|
|
48
|
+
args: {
|
|
49
|
+
where: ((_b = params.args) === null || _b === void 0 ? void 0 : _b.where) || params.args,
|
|
50
|
+
data: {
|
|
51
|
+
[field]: createValue(true),
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
};
|
|
57
|
+
export const createDeleteManyParams = (config, params) => {
|
|
58
|
+
var _a;
|
|
59
|
+
if (!params.model)
|
|
60
|
+
return { params };
|
|
61
|
+
const where = ((_a = params.args) === null || _a === void 0 ? void 0 : _a.where) || params.args;
|
|
62
|
+
return {
|
|
63
|
+
params: {
|
|
64
|
+
...params,
|
|
65
|
+
operation: "updateMany",
|
|
66
|
+
args: {
|
|
67
|
+
where: {
|
|
68
|
+
...where,
|
|
69
|
+
[config.field]: config.createValue(false),
|
|
70
|
+
},
|
|
71
|
+
data: {
|
|
72
|
+
[config.field]: config.createValue(true),
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
};
|
|
78
|
+
export const createUpdateParams = (config, params) => {
|
|
79
|
+
var _a, _b, _c, _d;
|
|
80
|
+
if (((_a = params.scope) === null || _a === void 0 ? void 0 : _a.relations) &&
|
|
81
|
+
!params.scope.relations.to.isList &&
|
|
82
|
+
!config.allowToOneUpdates &&
|
|
83
|
+
!((_b = params.args) === null || _b === void 0 ? void 0 : _b.__passUpdateThrough)) {
|
|
84
|
+
throw new Error(`prisma-extension-soft-delete: update of model "${params.model}" through "${(_c = params.scope) === null || _c === void 0 ? void 0 : _c.parentParams.model}.${params.scope.relations.to.name}" found. Updates of soft deleted models through a toOne relation is not supported as it is possible to update a soft deleted record.`);
|
|
85
|
+
}
|
|
86
|
+
// remove __passUpdateThrough from args
|
|
87
|
+
if ((_d = params.args) === null || _d === void 0 ? void 0 : _d.__passUpdateThrough) {
|
|
88
|
+
delete params.args.__passUpdateThrough;
|
|
89
|
+
}
|
|
90
|
+
return { params };
|
|
91
|
+
};
|
|
92
|
+
export const createUpdateManyParams = (config, params) => {
|
|
93
|
+
var _a, _b, _c;
|
|
94
|
+
// do nothing if args are not defined to allow Prisma to throw an error
|
|
95
|
+
if (!params.args)
|
|
96
|
+
return { params };
|
|
97
|
+
return {
|
|
98
|
+
params: {
|
|
99
|
+
...params,
|
|
100
|
+
args: {
|
|
101
|
+
...params.args,
|
|
102
|
+
where: {
|
|
103
|
+
...(_a = params.args) === null || _a === void 0 ? void 0 : _a.where,
|
|
104
|
+
// allow overriding the deleted field in where
|
|
105
|
+
[config.field]: ((_c = (_b = params.args) === null || _b === void 0 ? void 0 : _b.where) === null || _c === void 0 ? void 0 : _c[config.field]) || config.createValue(false),
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
};
|
|
110
|
+
};
|
|
111
|
+
export const createUpsertParams = (_, params) => {
|
|
112
|
+
var _a, _b;
|
|
113
|
+
if (((_a = params.scope) === null || _a === void 0 ? void 0 : _a.relations) && !params.scope.relations.to.isList) {
|
|
114
|
+
throw new Error(`prisma-extension-soft-delete: upsert of model "${params.model}" through "${(_b = params.scope) === null || _b === void 0 ? void 0 : _b.parentParams.model}.${params.scope.relations.to.name}" found. Upserts of soft deleted models through a toOne relation is not supported as it is possible to update a soft deleted record.`);
|
|
115
|
+
}
|
|
116
|
+
return { params };
|
|
117
|
+
};
|
|
118
|
+
function validateFindUniqueParams(params, config) {
|
|
119
|
+
var _a;
|
|
120
|
+
const uniqueIndexFields = uniqueIndexFieldsByModel[params.model || ""] || [];
|
|
121
|
+
const uniqueIndexField = Object.keys(((_a = params.args) === null || _a === void 0 ? void 0 : _a.where) || {}).find((key) => uniqueIndexFields.includes(key));
|
|
122
|
+
// when unique index field is found it is not possible to use findFirst.
|
|
123
|
+
// Instead warn the user that soft-deleted models will not be excluded from
|
|
124
|
+
// this query unless warnForUniqueIndexes is false.
|
|
125
|
+
if (uniqueIndexField && !config.allowCompoundUniqueIndexWhere) {
|
|
126
|
+
throw new Error(`prisma-extension-soft-delete: query of model "${params.model}" through compound unique index field "${uniqueIndexField}" found. Queries of soft deleted models through a unique index are not supported. Set "allowCompoundUniqueIndexWhere" to true to override this behaviour.`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
function shouldPassFindUniqueParamsThrough(params, config) {
|
|
130
|
+
var _a, _b;
|
|
131
|
+
const uniqueFields = uniqueFieldsByModel[params.model || ""] || [];
|
|
132
|
+
const uniqueIndexFields = uniqueIndexFieldsByModel[params.model || ""] || [];
|
|
133
|
+
const uniqueIndexField = Object.keys(((_a = params.args) === null || _a === void 0 ? void 0 : _a.where) || {}).find((key) => uniqueIndexFields.includes(key));
|
|
134
|
+
// pass through invalid args so Prisma throws an error
|
|
135
|
+
return (
|
|
136
|
+
// findUnique must have a where object
|
|
137
|
+
!((_b = params.args) === null || _b === void 0 ? void 0 : _b.where) ||
|
|
138
|
+
typeof params.args.where !== "object" ||
|
|
139
|
+
// where object must have at least one defined unique field
|
|
140
|
+
!Object.entries(params.args.where).some(([key, val]) => (uniqueFields.includes(key) || uniqueIndexFields.includes(key)) &&
|
|
141
|
+
typeof val !== "undefined") ||
|
|
142
|
+
// pass through if where object has a unique index field and allowCompoundUniqueIndexWhere is true
|
|
143
|
+
!!(uniqueIndexField && config.allowCompoundUniqueIndexWhere));
|
|
144
|
+
}
|
|
145
|
+
export const createFindUniqueParams = (config, params) => {
|
|
146
|
+
var _a, _b, _c;
|
|
147
|
+
if (shouldPassFindUniqueParamsThrough(params, config)) {
|
|
148
|
+
return { params };
|
|
149
|
+
}
|
|
150
|
+
validateFindUniqueParams(params, config);
|
|
151
|
+
return {
|
|
152
|
+
params: {
|
|
153
|
+
...params,
|
|
154
|
+
operation: "findFirst",
|
|
155
|
+
args: {
|
|
156
|
+
...params.args,
|
|
157
|
+
where: {
|
|
158
|
+
...(_a = params.args) === null || _a === void 0 ? void 0 : _a.where,
|
|
159
|
+
// allow overriding the deleted field in where
|
|
160
|
+
[config.field]: ((_c = (_b = params.args) === null || _b === void 0 ? void 0 : _b.where) === null || _c === void 0 ? void 0 : _c[config.field]) || config.createValue(false),
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
};
|
|
166
|
+
export const createFindUniqueOrThrowParams = (config, params) => {
|
|
167
|
+
var _a, _b, _c;
|
|
168
|
+
if (shouldPassFindUniqueParamsThrough(params, config)) {
|
|
169
|
+
return { params };
|
|
170
|
+
}
|
|
171
|
+
validateFindUniqueParams(params, config);
|
|
172
|
+
return {
|
|
173
|
+
params: {
|
|
174
|
+
...params,
|
|
175
|
+
operation: "findFirstOrThrow",
|
|
176
|
+
args: {
|
|
177
|
+
...params.args,
|
|
178
|
+
where: {
|
|
179
|
+
...(_a = params.args) === null || _a === void 0 ? void 0 : _a.where,
|
|
180
|
+
// allow overriding the deleted field in where
|
|
181
|
+
[config.field]: ((_c = (_b = params.args) === null || _b === void 0 ? void 0 : _b.where) === null || _c === void 0 ? void 0 : _c[config.field]) || config.createValue(false),
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
};
|
|
187
|
+
export const createFindFirstParams = (config, params) => {
|
|
188
|
+
var _a, _b, _c;
|
|
189
|
+
return {
|
|
190
|
+
params: {
|
|
191
|
+
...params,
|
|
192
|
+
operation: "findFirst",
|
|
193
|
+
args: {
|
|
194
|
+
...params.args,
|
|
195
|
+
where: {
|
|
196
|
+
...(_a = params.args) === null || _a === void 0 ? void 0 : _a.where,
|
|
197
|
+
// allow overriding the deleted field in where
|
|
198
|
+
[config.field]: ((_c = (_b = params.args) === null || _b === void 0 ? void 0 : _b.where) === null || _c === void 0 ? void 0 : _c[config.field]) || config.createValue(false),
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
};
|
|
204
|
+
export const createFindFirstOrThrowParams = (config, params) => {
|
|
205
|
+
var _a, _b, _c;
|
|
206
|
+
return {
|
|
207
|
+
params: {
|
|
208
|
+
...params,
|
|
209
|
+
operation: "findFirstOrThrow",
|
|
210
|
+
args: {
|
|
211
|
+
...params.args,
|
|
212
|
+
where: {
|
|
213
|
+
...(_a = params.args) === null || _a === void 0 ? void 0 : _a.where,
|
|
214
|
+
// allow overriding the deleted field in where
|
|
215
|
+
[config.field]: ((_c = (_b = params.args) === null || _b === void 0 ? void 0 : _b.where) === null || _c === void 0 ? void 0 : _c[config.field]) || config.createValue(false),
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
};
|
|
220
|
+
};
|
|
221
|
+
export const createFindManyParams = (config, params) => {
|
|
222
|
+
var _a, _b, _c;
|
|
223
|
+
return {
|
|
224
|
+
params: {
|
|
225
|
+
...params,
|
|
226
|
+
operation: "findMany",
|
|
227
|
+
args: {
|
|
228
|
+
...params.args,
|
|
229
|
+
where: {
|
|
230
|
+
...(_a = params.args) === null || _a === void 0 ? void 0 : _a.where,
|
|
231
|
+
// allow overriding the deleted field in where
|
|
232
|
+
[config.field]: ((_c = (_b = params.args) === null || _b === void 0 ? void 0 : _b.where) === null || _c === void 0 ? void 0 : _c[config.field]) || config.createValue(false),
|
|
233
|
+
},
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
};
|
|
237
|
+
};
|
|
238
|
+
/*GroupBy */
|
|
239
|
+
export const createGroupByParams = (config, params) => {
|
|
240
|
+
var _a, _b, _c;
|
|
241
|
+
return {
|
|
242
|
+
params: {
|
|
243
|
+
...params,
|
|
244
|
+
operation: "groupBy",
|
|
245
|
+
args: {
|
|
246
|
+
...params.args,
|
|
247
|
+
where: {
|
|
248
|
+
...(_a = params.args) === null || _a === void 0 ? void 0 : _a.where,
|
|
249
|
+
// allow overriding the deleted field in where
|
|
250
|
+
[config.field]: ((_c = (_b = params.args) === null || _b === void 0 ? void 0 : _b.where) === null || _c === void 0 ? void 0 : _c[config.field]) || config.createValue(false),
|
|
251
|
+
},
|
|
252
|
+
},
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
export const createCountParams = (config, params) => {
|
|
257
|
+
const args = params.args || {};
|
|
258
|
+
const where = args.where || {};
|
|
259
|
+
return {
|
|
260
|
+
params: {
|
|
261
|
+
...params,
|
|
262
|
+
args: {
|
|
263
|
+
...args,
|
|
264
|
+
where: {
|
|
265
|
+
...where,
|
|
266
|
+
// allow overriding the deleted field in where
|
|
267
|
+
[config.field]: where[config.field] || config.createValue(false),
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
},
|
|
271
|
+
};
|
|
272
|
+
};
|
|
273
|
+
export const createAggregateParams = (config, params) => {
|
|
274
|
+
const args = params.args || {};
|
|
275
|
+
const where = args.where || {};
|
|
276
|
+
return {
|
|
277
|
+
params: {
|
|
278
|
+
...params,
|
|
279
|
+
args: {
|
|
280
|
+
...args,
|
|
281
|
+
where: {
|
|
282
|
+
...where,
|
|
283
|
+
// allow overriding the deleted field in where
|
|
284
|
+
[config.field]: where[config.field] || config.createValue(false),
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
};
|
|
289
|
+
};
|
|
290
|
+
export const createWhereParams = (config, params) => {
|
|
291
|
+
var _a;
|
|
292
|
+
if (!params.scope)
|
|
293
|
+
return { params };
|
|
294
|
+
// customise list queries with every modifier unless the deleted field is set
|
|
295
|
+
if (((_a = params.scope) === null || _a === void 0 ? void 0 : _a.modifier) === "every" && !params.args[config.field]) {
|
|
296
|
+
return {
|
|
297
|
+
params: {
|
|
298
|
+
...params,
|
|
299
|
+
args: {
|
|
300
|
+
OR: [
|
|
301
|
+
{ [config.field]: { not: config.createValue(false) } },
|
|
302
|
+
params.args,
|
|
303
|
+
],
|
|
304
|
+
},
|
|
305
|
+
},
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
params: {
|
|
310
|
+
...params,
|
|
311
|
+
args: {
|
|
312
|
+
...params.args,
|
|
313
|
+
[config.field]: params.args[config.field] || config.createValue(false),
|
|
314
|
+
},
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
};
|
|
318
|
+
export const createIncludeParams = (config, params) => {
|
|
319
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
320
|
+
// includes of toOne relation cannot filter deleted records using params
|
|
321
|
+
// instead ensure that the deleted field is selected and filter the results
|
|
322
|
+
if (((_b = (_a = params.scope) === null || _a === void 0 ? void 0 : _a.relations) === null || _b === void 0 ? void 0 : _b.to.isList) === false) {
|
|
323
|
+
if (((_c = params.args) === null || _c === void 0 ? void 0 : _c.select) && !((_d = params.args) === null || _d === void 0 ? void 0 : _d.select[config.field])) {
|
|
324
|
+
return {
|
|
325
|
+
params: addDeletedToSelect(params, config),
|
|
326
|
+
ctx: { deletedFieldAdded: true },
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
return { params };
|
|
330
|
+
}
|
|
331
|
+
return {
|
|
332
|
+
params: {
|
|
333
|
+
...params,
|
|
334
|
+
args: {
|
|
335
|
+
...params.args,
|
|
336
|
+
where: {
|
|
337
|
+
...(_e = params.args) === null || _e === void 0 ? void 0 : _e.where,
|
|
338
|
+
// allow overriding the deleted field in where
|
|
339
|
+
[config.field]: ((_g = (_f = params.args) === null || _f === void 0 ? void 0 : _f.where) === null || _g === void 0 ? void 0 : _g[config.field]) || config.createValue(false),
|
|
340
|
+
},
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
};
|
|
345
|
+
export const createSelectParams = (config, params) => {
|
|
346
|
+
var _a, _b, _c, _d, _e, _f, _g;
|
|
347
|
+
// selects in includes are handled by createIncludeParams
|
|
348
|
+
if (((_a = params.scope) === null || _a === void 0 ? void 0 : _a.parentParams.operation) === "include") {
|
|
349
|
+
return { params };
|
|
350
|
+
}
|
|
351
|
+
// selects of toOne relation cannot filter deleted records using params
|
|
352
|
+
if (((_c = (_b = params.scope) === null || _b === void 0 ? void 0 : _b.relations) === null || _c === void 0 ? void 0 : _c.to.isList) === false) {
|
|
353
|
+
if (((_d = params.args) === null || _d === void 0 ? void 0 : _d.select) && !params.args.select[config.field]) {
|
|
354
|
+
return {
|
|
355
|
+
params: addDeletedToSelect(params, config),
|
|
356
|
+
ctx: { deletedFieldAdded: true },
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
return { params };
|
|
360
|
+
}
|
|
361
|
+
return {
|
|
362
|
+
params: {
|
|
363
|
+
...params,
|
|
364
|
+
args: {
|
|
365
|
+
...params.args,
|
|
366
|
+
where: {
|
|
367
|
+
...(_e = params.args) === null || _e === void 0 ? void 0 : _e.where,
|
|
368
|
+
// allow overriding the deleted field in where
|
|
369
|
+
[config.field]: ((_g = (_f = params.args) === null || _f === void 0 ? void 0 : _f.where) === null || _g === void 0 ? void 0 : _g[config.field]) || config.createValue(false),
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
};
|
|
374
|
+
};
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { ModelConfig } from "../types";
|
|
2
|
+
import { CreateParamsReturn } from "./createParams";
|
|
3
|
+
export type ModifyResult = (config: ModelConfig, result: any, params: CreateParamsReturn["params"], ctx?: any) => any;
|
|
4
|
+
export declare function modifyReadResult(config: ModelConfig, result: any, params: CreateParamsReturn["params"], ctx?: any): CreateParamsReturn;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { stripDeletedFieldFromResults } from "../utils/nestedReads";
|
|
2
|
+
import { filterSoftDeletedResults, shouldFilterDeletedFromReadResult, } from "../utils/resultFiltering";
|
|
3
|
+
export function modifyReadResult(config, result, params, ctx) {
|
|
4
|
+
if (shouldFilterDeletedFromReadResult(params, config)) {
|
|
5
|
+
const filteredResults = filterSoftDeletedResults(result, config);
|
|
6
|
+
if (ctx === null || ctx === void 0 ? void 0 : ctx.deletedFieldAdded) {
|
|
7
|
+
stripDeletedFieldFromResults(filteredResults, config);
|
|
8
|
+
}
|
|
9
|
+
return filteredResults;
|
|
10
|
+
}
|
|
11
|
+
return result;
|
|
12
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Prisma } from "@prisma/client";
|
|
2
|
+
export type ModelConfig = {
|
|
3
|
+
field: string;
|
|
4
|
+
createValue: (deleted: boolean) => any;
|
|
5
|
+
allowToOneUpdates?: boolean;
|
|
6
|
+
allowCompoundUniqueIndexWhere?: boolean;
|
|
7
|
+
};
|
|
8
|
+
export type Config = {
|
|
9
|
+
models: Partial<Record<Prisma.ModelName, ModelConfig | boolean>>;
|
|
10
|
+
defaultConfig?: ModelConfig;
|
|
11
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function addDeletedToSelect(params, config) {
|
|
2
|
+
if (params.args.select && !params.args.select[config.field]) {
|
|
3
|
+
return {
|
|
4
|
+
...params,
|
|
5
|
+
args: {
|
|
6
|
+
...params.args,
|
|
7
|
+
select: {
|
|
8
|
+
...params.args.select,
|
|
9
|
+
[config.field]: true,
|
|
10
|
+
},
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
return params;
|
|
15
|
+
}
|
|
16
|
+
export function stripDeletedFieldFromResults(results, config) {
|
|
17
|
+
if (Array.isArray(results)) {
|
|
18
|
+
results === null || results === void 0 ? void 0 : results.forEach((item) => {
|
|
19
|
+
delete item[config.field];
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
else if (results) {
|
|
23
|
+
delete results[config.field];
|
|
24
|
+
}
|
|
25
|
+
return results;
|
|
26
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Maybe this should return true for non-list relations only?
|
|
2
|
+
export function shouldFilterDeletedFromReadResult(params, config) {
|
|
3
|
+
return (!params.args.where ||
|
|
4
|
+
typeof params.args.where[config.field] === "undefined" ||
|
|
5
|
+
!params.args.where[config.field]);
|
|
6
|
+
}
|
|
7
|
+
export function filterSoftDeletedResults(result, config) {
|
|
8
|
+
// filter out deleted records from array results
|
|
9
|
+
if (result && Array.isArray(result)) {
|
|
10
|
+
return result.filter((item) => !item[config.field]);
|
|
11
|
+
}
|
|
12
|
+
// if the result is deleted return null
|
|
13
|
+
if (result && result[config.field]) {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
return result;
|
|
17
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.createSoftDeleteExtension = void 0;
|
|
18
|
+
__exportStar(require("./lib/types"), exports);
|
|
19
|
+
var createSoftDeleteExtension_1 = require("./lib/createSoftDeleteExtension");
|
|
20
|
+
Object.defineProperty(exports, "createSoftDeleteExtension", { enumerable: true, get: function () { return createSoftDeleteExtension_1.createSoftDeleteExtension; } });
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { Config } from "./types";
|
|
2
|
+
export declare function createSoftDeleteExtension({ models, defaultConfig, }: Config): (client: any) => import("@prisma/client/extension").PrismaClientExtends<import("@prisma/client/runtime/library").InternalArgs<{}, {}, {}, {}> & import("@prisma/client/runtime/library").DefaultArgs>;
|