@tsed/mongoose 8.0.1 → 8.0.2
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/package.json +15 -14
- package/src/MongooseModule.ts +32 -0
- package/src/constants/constants.ts +20 -0
- package/src/decorators/auto.spec.ts +30 -0
- package/src/decorators/auto.ts +24 -0
- package/src/decorators/discriminatorKey.ts +1 -0
- package/src/decorators/dynamicRef.spec.ts +46 -0
- package/src/decorators/dynamicRef.ts +76 -0
- package/src/decorators/excludeIndexes.spec.ts +30 -0
- package/src/decorators/excludeIndexes.ts +24 -0
- package/src/decorators/expires.spec.ts +18 -0
- package/src/decorators/expires.ts +24 -0
- package/src/decorators/immutable.spec.ts +30 -0
- package/src/decorators/immutable.ts +26 -0
- package/src/decorators/indexed.spec.ts +19 -0
- package/src/decorators/indexed.ts +24 -0
- package/src/decorators/lowercase.spec.ts +30 -0
- package/src/decorators/lowercase.ts +24 -0
- package/src/decorators/model.ts +69 -0
- package/src/decorators/mongooseIndex.spec.ts +26 -0
- package/src/decorators/mongooseIndex.ts +35 -0
- package/src/decorators/mongooseIndexes.spec.ts +35 -0
- package/src/decorators/mongooseIndexes.ts +35 -0
- package/src/decorators/mongoosePlugin.spec.ts +25 -0
- package/src/decorators/mongoosePlugin.ts +20 -0
- package/src/decorators/numberDecimal.spec.ts +266 -0
- package/src/decorators/numberDecimal.ts +84 -0
- package/src/decorators/objectID.spec.ts +24 -0
- package/src/decorators/objectID.ts +39 -0
- package/src/decorators/postHook.spec.ts +44 -0
- package/src/decorators/postHook.ts +72 -0
- package/src/decorators/preHook.spec.ts +75 -0
- package/src/decorators/preHook.ts +70 -0
- package/src/decorators/ref.spec.ts +361 -0
- package/src/decorators/ref.ts +111 -0
- package/src/decorators/schema.ts +79 -0
- package/src/decorators/schemaIgnore.spec.ts +18 -0
- package/src/decorators/schemaIgnore.ts +24 -0
- package/src/decorators/select.spec.ts +18 -0
- package/src/decorators/select.ts +24 -0
- package/src/decorators/sparse.spec.ts +30 -0
- package/src/decorators/sparse.ts +26 -0
- package/src/decorators/text.spec.ts +30 -0
- package/src/decorators/text.ts +25 -0
- package/src/decorators/trim.spec.ts +18 -0
- package/src/decorators/trim.ts +23 -0
- package/src/decorators/unique.spec.ts +18 -0
- package/src/decorators/unique.ts +23 -0
- package/src/decorators/uppercase.spec.ts +30 -0
- package/src/decorators/uppercase.ts +24 -0
- package/src/decorators/versionKey.spec.ts +19 -0
- package/src/decorators/versionKey.ts +9 -0
- package/src/decorators/virtualRef.spec.ts +383 -0
- package/src/decorators/virtualRef.ts +73 -0
- package/src/index.ts +47 -0
- package/src/interfaces/MongooseConnectionOptions.ts +10 -0
- package/src/interfaces/MongooseDocument.ts +3 -0
- package/src/interfaces/MongooseModel.ts +10 -0
- package/src/interfaces/MongooseModelOptions.ts +8 -0
- package/src/interfaces/MongooseSchemaOptions.ts +51 -0
- package/src/interfaces/MongooseSchemaTypes.ts +5 -0
- package/src/interfaces/MongooseVirtualRefOptions.ts +10 -0
- package/src/interfaces/interfaces.ts +9 -0
- package/src/registries/MongooseModels.ts +3 -0
- package/src/services/MongooseConnection.spec.ts +70 -0
- package/src/services/MongooseConnections.ts +58 -0
- package/src/services/MongooseService.spec.ts +73 -0
- package/src/services/MongooseService.ts +77 -0
- package/src/utils/buildMongooseSchema.spec.ts +67 -0
- package/src/utils/createModel.spec.ts +49 -0
- package/src/utils/createModel.ts +54 -0
- package/src/utils/createSchema.spec.ts +771 -0
- package/src/utils/createSchema.ts +198 -0
- package/src/utils/resolveRefType.spec.ts +30 -0
- package/src/utils/resolveRefType.ts +18 -0
- package/src/utils/schemaOptions.spec.ts +68 -0
- package/src/utils/schemaOptions.ts +74 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import {MongooseNextCB, PreHook, schemaOptions} from "../../src/index.js";
|
|
2
|
+
|
|
3
|
+
describe("@PreHook()", () => {
|
|
4
|
+
describe("when decorator is used as class decorator", () => {
|
|
5
|
+
it("should call applySchemaOptions", () => {
|
|
6
|
+
// GIVEN
|
|
7
|
+
const fn = vi.fn();
|
|
8
|
+
|
|
9
|
+
// WHEN
|
|
10
|
+
@PreHook("method", fn, {query: true})
|
|
11
|
+
class Test {}
|
|
12
|
+
|
|
13
|
+
// THEN
|
|
14
|
+
const options = schemaOptions(Test);
|
|
15
|
+
|
|
16
|
+
expect(options).toEqual({
|
|
17
|
+
pre: [
|
|
18
|
+
{
|
|
19
|
+
method: "method",
|
|
20
|
+
fn,
|
|
21
|
+
options: {
|
|
22
|
+
query: true
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
it("should call applySchemaOptions with more options", () => {
|
|
29
|
+
// GIVEN
|
|
30
|
+
const fn: any = vi.fn((instance: any, next: MongooseNextCB, options: any) => {
|
|
31
|
+
return instance;
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// WHEN
|
|
35
|
+
@PreHook("method", fn, {query: true})
|
|
36
|
+
class Test {}
|
|
37
|
+
|
|
38
|
+
// THEN
|
|
39
|
+
const options = schemaOptions(Test);
|
|
40
|
+
|
|
41
|
+
expect(options).toEqual({
|
|
42
|
+
pre: [
|
|
43
|
+
{
|
|
44
|
+
method: "method",
|
|
45
|
+
fn,
|
|
46
|
+
options: {
|
|
47
|
+
query: true
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe("when decorator is used as method decorator", () => {
|
|
56
|
+
it("should call applySchemaOptions", () => {
|
|
57
|
+
class Test {
|
|
58
|
+
@PreHook("save", {
|
|
59
|
+
query: true
|
|
60
|
+
})
|
|
61
|
+
static method() {}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const {
|
|
65
|
+
pre: [options]
|
|
66
|
+
} = schemaOptions(Test);
|
|
67
|
+
|
|
68
|
+
expect(options.method).toBe("save");
|
|
69
|
+
expect(options.fn).toBeInstanceOf(Function);
|
|
70
|
+
expect(options.options).toEqual({
|
|
71
|
+
query: true
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
});
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import {decoratorTypeOf, DecoratorTypes} from "@tsed/core";
|
|
2
|
+
|
|
3
|
+
import {MongooseHookOptions, MongoosePreHookCB} from "../interfaces/MongooseSchemaOptions.js";
|
|
4
|
+
import {schemaOptions} from "../utils/schemaOptions.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* We can simply attach a `@PreHook` decorator to your model class and
|
|
9
|
+
* define the hook function like you normally would in Mongoose.
|
|
10
|
+
*
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import {Ignore, Required} from "@tsed/platform-http";
|
|
13
|
+
* import {PreHook, Model} from "@tsed/mongoose";
|
|
14
|
+
*
|
|
15
|
+
* @Model()
|
|
16
|
+
* @PreHook("save", (car: CarModel, next) => {
|
|
17
|
+
* if (car.model === 'Tesla') {
|
|
18
|
+
* car.isFast = true;
|
|
19
|
+
* }
|
|
20
|
+
* next();
|
|
21
|
+
*})
|
|
22
|
+
* export class CarModel {
|
|
23
|
+
*
|
|
24
|
+
* @Ignore()
|
|
25
|
+
* _id: string;
|
|
26
|
+
*
|
|
27
|
+
* @Required()
|
|
28
|
+
* model: string;
|
|
29
|
+
*
|
|
30
|
+
* @Required()
|
|
31
|
+
* isFast: boolean;
|
|
32
|
+
*
|
|
33
|
+
* // or Prehook on static method
|
|
34
|
+
* @PreHook("save")
|
|
35
|
+
* static preSave(car: CarModel, next) {
|
|
36
|
+
* if (car.model === 'Tesla') {
|
|
37
|
+
* car.isFast = true;
|
|
38
|
+
* }
|
|
39
|
+
* next();
|
|
40
|
+
* }
|
|
41
|
+
* }
|
|
42
|
+
* ```
|
|
43
|
+
*
|
|
44
|
+
* This will execute the pre-save hook each time a `CarModel` document is saved.
|
|
45
|
+
*
|
|
46
|
+
* @param {string} method
|
|
47
|
+
* @param fn
|
|
48
|
+
* @param options
|
|
49
|
+
* @returns {Function}
|
|
50
|
+
* @decorator
|
|
51
|
+
* @class
|
|
52
|
+
*/
|
|
53
|
+
export function PreHook<T = any>(method: string, fn?: MongoosePreHookCB<T> | MongooseHookOptions, options?: MongooseHookOptions): Function {
|
|
54
|
+
return (...args: any[]) => {
|
|
55
|
+
if (decoratorTypeOf(args) === DecoratorTypes.METHOD_STC) {
|
|
56
|
+
options = fn as MongooseHookOptions;
|
|
57
|
+
fn = args[0][args[1]].bind(args[0]);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
schemaOptions(args[0], {
|
|
61
|
+
pre: [
|
|
62
|
+
{
|
|
63
|
+
method,
|
|
64
|
+
fn: fn as MongoosePreHookCB<T>,
|
|
65
|
+
options
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
}
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
import {catchError, Store} from "@tsed/core";
|
|
2
|
+
import {getJsonSchema, Property} from "@tsed/schema";
|
|
3
|
+
import {Schema} from "mongoose";
|
|
4
|
+
|
|
5
|
+
import {MONGOOSE_MODEL_NAME, MONGOOSE_SCHEMA} from "../constants/constants.js";
|
|
6
|
+
import {MongooseModels} from "../registries/MongooseModels.js";
|
|
7
|
+
import {Ref} from "./ref.js";
|
|
8
|
+
|
|
9
|
+
describe("@Ref()", () => {
|
|
10
|
+
describe("type is a class", () => {
|
|
11
|
+
it("should set metadata and catch error", () => {
|
|
12
|
+
const error = catchError(() => {
|
|
13
|
+
class Model {
|
|
14
|
+
@Ref(undefined)
|
|
15
|
+
num: number[];
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
expect(error?.message).toEqual(
|
|
20
|
+
"A model is required on `@Ref(model)` decorator. Please give a model or wrap it inside an arrow function if you have a circular reference."
|
|
21
|
+
);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it("should set metadata", () => {
|
|
25
|
+
class RefTest {
|
|
26
|
+
@Property()
|
|
27
|
+
id: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
Store.from(RefTest).set(MONGOOSE_MODEL_NAME, "RefTest");
|
|
31
|
+
|
|
32
|
+
class Test {
|
|
33
|
+
@Ref(RefTest)
|
|
34
|
+
test: Ref<RefTest>;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const store = Store.from(Test, "test");
|
|
38
|
+
const schema = getJsonSchema(Test);
|
|
39
|
+
|
|
40
|
+
expect(schema).toEqual({
|
|
41
|
+
definitions: {
|
|
42
|
+
RefTest: {
|
|
43
|
+
type: "object",
|
|
44
|
+
properties: {
|
|
45
|
+
id: {
|
|
46
|
+
type: "string"
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
properties: {
|
|
52
|
+
test: {
|
|
53
|
+
oneOf: [
|
|
54
|
+
{
|
|
55
|
+
description: "A reference ObjectID",
|
|
56
|
+
examples: ["5ce7ad3028890bd71749d477"],
|
|
57
|
+
type: "string"
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
$ref: "#/definitions/RefTest"
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
type: "object"
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
expect(store.get(MONGOOSE_SCHEMA)).toEqual({
|
|
69
|
+
type: Schema.Types.ObjectId,
|
|
70
|
+
ref: RefTest
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
describe("type is a map of class", () => {
|
|
75
|
+
it("should set metadata", () => {
|
|
76
|
+
class RefTest {
|
|
77
|
+
@Property()
|
|
78
|
+
id: string;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
Store.from(RefTest).set(MONGOOSE_MODEL_NAME, "RefTest");
|
|
82
|
+
MongooseModels.set("RefTest", RefTest);
|
|
83
|
+
|
|
84
|
+
class Test {
|
|
85
|
+
@Ref(RefTest)
|
|
86
|
+
test: Map<string, Ref<RefTest>>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const store = Store.from(Test, "test");
|
|
90
|
+
const schema = getJsonSchema(Test);
|
|
91
|
+
|
|
92
|
+
expect(schema).toEqual({
|
|
93
|
+
definitions: {
|
|
94
|
+
RefTest: {
|
|
95
|
+
properties: {
|
|
96
|
+
id: {
|
|
97
|
+
type: "string"
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
type: "object"
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
properties: {
|
|
104
|
+
test: {
|
|
105
|
+
additionalProperties: {
|
|
106
|
+
oneOf: [
|
|
107
|
+
{
|
|
108
|
+
description: "A reference ObjectID",
|
|
109
|
+
examples: ["5ce7ad3028890bd71749d477"],
|
|
110
|
+
type: "string"
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
$ref: "#/definitions/RefTest"
|
|
114
|
+
}
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
type: "object"
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
type: "object"
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
expect(store.get(MONGOOSE_SCHEMA)).toEqual({
|
|
124
|
+
type: Schema.Types.ObjectId,
|
|
125
|
+
ref: RefTest
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
describe("type is a Function", () => {
|
|
130
|
+
it("should set metadata", () => {
|
|
131
|
+
class RefTest {
|
|
132
|
+
@Property()
|
|
133
|
+
id: string;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
Store.from(RefTest).set(MONGOOSE_MODEL_NAME, "RefTest");
|
|
137
|
+
const arrow = () => RefTest;
|
|
138
|
+
|
|
139
|
+
class Test {
|
|
140
|
+
@Ref(arrow)
|
|
141
|
+
test: Ref<RefTest>;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const store = Store.from(Test, "test");
|
|
145
|
+
const schema = getJsonSchema(Test);
|
|
146
|
+
|
|
147
|
+
expect(schema).toEqual({
|
|
148
|
+
definitions: {
|
|
149
|
+
RefTest: {
|
|
150
|
+
type: "object",
|
|
151
|
+
properties: {
|
|
152
|
+
id: {
|
|
153
|
+
type: "string"
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
properties: {
|
|
159
|
+
test: {
|
|
160
|
+
oneOf: [
|
|
161
|
+
{
|
|
162
|
+
description: "A reference ObjectID",
|
|
163
|
+
examples: ["5ce7ad3028890bd71749d477"],
|
|
164
|
+
type: "string"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
$ref: "#/definitions/RefTest"
|
|
168
|
+
}
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
type: "object"
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
expect(store.get(MONGOOSE_SCHEMA)).toEqual({
|
|
176
|
+
type: Schema.Types.ObjectId,
|
|
177
|
+
ref: arrow
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
describe("type is a string (deprecated)", () => {
|
|
182
|
+
it("should set metadata", () => {
|
|
183
|
+
class RefTest {}
|
|
184
|
+
|
|
185
|
+
class Test {
|
|
186
|
+
@Ref("RefTest")
|
|
187
|
+
test: Ref<RefTest>;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
MongooseModels.set("RefTest", RefTest);
|
|
191
|
+
const store = Store.from(Test, "test");
|
|
192
|
+
|
|
193
|
+
expect(getJsonSchema(Test)).toEqual({
|
|
194
|
+
definitions: {
|
|
195
|
+
RefTest: {
|
|
196
|
+
properties: {
|
|
197
|
+
id: {
|
|
198
|
+
type: "string"
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
type: "object"
|
|
202
|
+
}
|
|
203
|
+
},
|
|
204
|
+
properties: {
|
|
205
|
+
test: {
|
|
206
|
+
oneOf: [
|
|
207
|
+
{
|
|
208
|
+
description: "A reference ObjectID",
|
|
209
|
+
examples: ["5ce7ad3028890bd71749d477"],
|
|
210
|
+
type: "string"
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
$ref: "#/definitions/RefTest"
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
type: "object"
|
|
219
|
+
});
|
|
220
|
+
expect(store.get(MONGOOSE_SCHEMA)).toEqual({
|
|
221
|
+
type: Schema.Types.ObjectId,
|
|
222
|
+
ref: "RefTest"
|
|
223
|
+
});
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
describe("JsonSchema gets generated based on populated groups", () => {
|
|
227
|
+
class MyChildModel {
|
|
228
|
+
@Property()
|
|
229
|
+
test: string;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
class MyParentModel {
|
|
233
|
+
@Property()
|
|
234
|
+
id: string;
|
|
235
|
+
|
|
236
|
+
@Ref(MyChildModel, {populatedGroups: ["group1", "group2"]})
|
|
237
|
+
child1: Ref<MyChildModel>;
|
|
238
|
+
|
|
239
|
+
@Ref(MyChildModel, {populatedGroups: ["group2"]})
|
|
240
|
+
child2: Ref<MyChildModel>;
|
|
241
|
+
|
|
242
|
+
@Ref(MyChildModel)
|
|
243
|
+
child3: Ref<MyChildModel>;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
it("should reflect the populated groups options in the schema (with given groups)", () => {
|
|
247
|
+
const spec = getJsonSchema(MyParentModel, {
|
|
248
|
+
groups: ["group1", "group3"]
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
expect(spec).toEqual({
|
|
252
|
+
definitions: {
|
|
253
|
+
MyChildModel: {
|
|
254
|
+
properties: {
|
|
255
|
+
test: {
|
|
256
|
+
type: "string"
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
type: "object"
|
|
260
|
+
},
|
|
261
|
+
MyChildModelGroup1Group3: {
|
|
262
|
+
properties: {
|
|
263
|
+
test: {
|
|
264
|
+
type: "string"
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
type: "object"
|
|
268
|
+
}
|
|
269
|
+
},
|
|
270
|
+
properties: {
|
|
271
|
+
child1: {
|
|
272
|
+
oneOf: [
|
|
273
|
+
{
|
|
274
|
+
$ref: "#/definitions/MyChildModelGroup1Group3"
|
|
275
|
+
}
|
|
276
|
+
]
|
|
277
|
+
},
|
|
278
|
+
child2: {
|
|
279
|
+
oneOf: [
|
|
280
|
+
{
|
|
281
|
+
description: "A reference ObjectID",
|
|
282
|
+
examples: ["5ce7ad3028890bd71749d477"],
|
|
283
|
+
type: "string"
|
|
284
|
+
}
|
|
285
|
+
]
|
|
286
|
+
},
|
|
287
|
+
child3: {
|
|
288
|
+
oneOf: [
|
|
289
|
+
{
|
|
290
|
+
description: "A reference ObjectID",
|
|
291
|
+
examples: ["5ce7ad3028890bd71749d477"],
|
|
292
|
+
type: "string"
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
$ref: "#/definitions/MyChildModel"
|
|
296
|
+
}
|
|
297
|
+
]
|
|
298
|
+
},
|
|
299
|
+
id: {
|
|
300
|
+
type: "string"
|
|
301
|
+
}
|
|
302
|
+
},
|
|
303
|
+
type: "object"
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
it("should reflect the populated groups options in the schema (without given groups)", () => {
|
|
308
|
+
const spec = getJsonSchema(MyParentModel, {
|
|
309
|
+
groups: []
|
|
310
|
+
});
|
|
311
|
+
expect(spec).toEqual({
|
|
312
|
+
definitions: {
|
|
313
|
+
MyChildModel: {
|
|
314
|
+
properties: {
|
|
315
|
+
test: {
|
|
316
|
+
type: "string"
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
type: "object"
|
|
320
|
+
}
|
|
321
|
+
},
|
|
322
|
+
properties: {
|
|
323
|
+
child1: {
|
|
324
|
+
oneOf: [
|
|
325
|
+
{
|
|
326
|
+
description: "A reference ObjectID",
|
|
327
|
+
examples: ["5ce7ad3028890bd71749d477"],
|
|
328
|
+
type: "string"
|
|
329
|
+
}
|
|
330
|
+
]
|
|
331
|
+
},
|
|
332
|
+
child2: {
|
|
333
|
+
oneOf: [
|
|
334
|
+
{
|
|
335
|
+
description: "A reference ObjectID",
|
|
336
|
+
examples: ["5ce7ad3028890bd71749d477"],
|
|
337
|
+
type: "string"
|
|
338
|
+
}
|
|
339
|
+
]
|
|
340
|
+
},
|
|
341
|
+
child3: {
|
|
342
|
+
oneOf: [
|
|
343
|
+
{
|
|
344
|
+
description: "A reference ObjectID",
|
|
345
|
+
examples: ["5ce7ad3028890bd71749d477"],
|
|
346
|
+
type: "string"
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
$ref: "#/definitions/MyChildModel"
|
|
350
|
+
}
|
|
351
|
+
]
|
|
352
|
+
},
|
|
353
|
+
id: {
|
|
354
|
+
type: "string"
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
type: "object"
|
|
358
|
+
});
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
});
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import {isArrowFn, isCollection, isObject, isObjectID, isString, StoreMerge, Type, useDecorators} from "@tsed/core";
|
|
2
|
+
import {deserialize, OnDeserialize, OnSerialize, serialize} from "@tsed/json-mapper";
|
|
3
|
+
import {ForwardGroups, JsonEntityFn, lazyRef, matchGroups, OneOf, Property, string} from "@tsed/schema";
|
|
4
|
+
import {Schema as MongooseSchema} from "mongoose";
|
|
5
|
+
|
|
6
|
+
import {MONGOOSE_SCHEMA} from "../constants/constants.js";
|
|
7
|
+
import {MongooseSchemaTypes} from "../interfaces/MongooseSchemaTypes.js";
|
|
8
|
+
import {MongooseModels} from "../registries/MongooseModels.js";
|
|
9
|
+
|
|
10
|
+
interface RefOptions {
|
|
11
|
+
type?: MongooseSchemaTypes;
|
|
12
|
+
populatedGroups?: string[];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function isRef(value: undefined | string | any) {
|
|
16
|
+
return isObjectID(value) || isString(value);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function PopulateGroups(populatedGroups: string[]) {
|
|
20
|
+
return useDecorators(
|
|
21
|
+
ForwardGroups(true),
|
|
22
|
+
JsonEntityFn((store) => {
|
|
23
|
+
store.schema.$hooks.on("oneOf", (obj: any[], givenGroups: string[]) => {
|
|
24
|
+
if (matchGroups(populatedGroups, givenGroups)) {
|
|
25
|
+
return obj.filter((x) => x.type === "string"); // keep the object id;
|
|
26
|
+
} else {
|
|
27
|
+
return obj.filter((x) => x.type !== "string"); // keep the ref definition
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
})
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Define a property as mongoose reference to other Model (decorated with @Model).
|
|
36
|
+
*
|
|
37
|
+
* ### Example
|
|
38
|
+
*
|
|
39
|
+
* ```typescript
|
|
40
|
+
*
|
|
41
|
+
* @Model()
|
|
42
|
+
* class FooModel {
|
|
43
|
+
*
|
|
44
|
+
* @Ref(Foo2Model)
|
|
45
|
+
* field: Ref<Foo2Model>
|
|
46
|
+
*
|
|
47
|
+
* @Ref(Foo2Model)
|
|
48
|
+
* list: Ref<Foo2Model>[]
|
|
49
|
+
* }
|
|
50
|
+
*
|
|
51
|
+
* @Model()
|
|
52
|
+
* class Foo2Model {
|
|
53
|
+
* }
|
|
54
|
+
* ```
|
|
55
|
+
*
|
|
56
|
+
* @param model
|
|
57
|
+
* @param options
|
|
58
|
+
* @returns {Function}
|
|
59
|
+
* @decorator
|
|
60
|
+
* @mongoose
|
|
61
|
+
* @property
|
|
62
|
+
*/
|
|
63
|
+
export function Ref(
|
|
64
|
+
model: string | (() => Type) | any,
|
|
65
|
+
options: RefOptions | MongooseSchemaTypes = MongooseSchemaTypes.OBJECT_ID
|
|
66
|
+
): PropertyDecorator {
|
|
67
|
+
if (!model) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
"A model is required on `@Ref(model)` decorator. Please give a model or wrap it inside an arrow function if you have a circular reference."
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const getType = () => (isString(model) ? MongooseModels.get(model) : isArrowFn(model) ? model() : model);
|
|
74
|
+
const populatedGroups = (isObject(options) && options.populatedGroups) || [];
|
|
75
|
+
|
|
76
|
+
return useDecorators(
|
|
77
|
+
Property(Object),
|
|
78
|
+
StoreMerge(MONGOOSE_SCHEMA, {
|
|
79
|
+
type: MongooseSchema.Types[isObject(options) ? options.type || MongooseSchemaTypes.OBJECT_ID : options],
|
|
80
|
+
ref: model
|
|
81
|
+
}),
|
|
82
|
+
OnDeserialize((value) => {
|
|
83
|
+
if (isRef(value)) {
|
|
84
|
+
return value.toString();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (isCollection(value) && isRef(value[0])) {
|
|
88
|
+
return value.map(String);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return deserialize(value, {type: getType(), useAlias: false});
|
|
92
|
+
}),
|
|
93
|
+
OnSerialize((value: any, ctx) => {
|
|
94
|
+
if (isRef(value)) {
|
|
95
|
+
return value.toString();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (isCollection(value) && isRef(value[0])) {
|
|
99
|
+
return value.map(String);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const type = getType();
|
|
103
|
+
|
|
104
|
+
return serialize(value, {...ctx, type});
|
|
105
|
+
}),
|
|
106
|
+
OneOf(string().example("5ce7ad3028890bd71749d477").description("A reference ObjectID"), lazyRef(getType)),
|
|
107
|
+
populatedGroups.length && PopulateGroups(populatedGroups)
|
|
108
|
+
) as PropertyDecorator;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export type Ref<T> = T | string;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import {decoratorTypeOf, StoreMerge, useDecorators} from "@tsed/core";
|
|
2
|
+
import {injectable} from "@tsed/di";
|
|
3
|
+
import {Property} from "@tsed/schema";
|
|
4
|
+
import {SchemaTypeOptions} from "mongoose";
|
|
5
|
+
|
|
6
|
+
import {MONGOOSE_SCHEMA} from "../constants/constants.js";
|
|
7
|
+
import {MongooseSchemaOptions} from "../interfaces/MongooseSchemaOptions.js";
|
|
8
|
+
import {getSchema, getSchemaToken} from "../utils/createSchema.js";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Define a class as a Mongoose Schema ready to be used to compose other schemes and models.
|
|
12
|
+
*
|
|
13
|
+
* ### Example
|
|
14
|
+
*
|
|
15
|
+
* ```typescript
|
|
16
|
+
* @MongooseSchema()
|
|
17
|
+
* export class EventSchema {
|
|
18
|
+
* @Property()
|
|
19
|
+
* field: string;
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*
|
|
23
|
+
* ### Options
|
|
24
|
+
*
|
|
25
|
+
* - `schemaOptions` (mongoose.SchemaOptions): Option to configure the schema behavior.
|
|
26
|
+
*
|
|
27
|
+
* @param {MongooseSchemaOptions | undefined} options
|
|
28
|
+
* @returns {(target: any) => void}
|
|
29
|
+
* @decorator
|
|
30
|
+
* @mongoose
|
|
31
|
+
* @property
|
|
32
|
+
* @class
|
|
33
|
+
*/
|
|
34
|
+
export function Schema(options?: MongooseSchemaOptions): (target: any) => void;
|
|
35
|
+
export function Schema(definition: SchemaTypeOptions<any>): Function;
|
|
36
|
+
export function Schema(options: MongooseSchemaOptions | SchemaTypeOptions<any> = {}) {
|
|
37
|
+
return (...parameters: any[]) => {
|
|
38
|
+
switch (decoratorTypeOf(parameters)) {
|
|
39
|
+
case "property":
|
|
40
|
+
return useDecorators(Property(), StoreMerge(MONGOOSE_SCHEMA, options))(parameters[0], parameters[1], parameters[2]);
|
|
41
|
+
|
|
42
|
+
case "class":
|
|
43
|
+
const {token} = getSchemaToken(parameters[0], options);
|
|
44
|
+
|
|
45
|
+
injectable(token).factory(() => getSchema(parameters[0], options as any));
|
|
46
|
+
break;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Define a class as a Mongoose Schema ready to be used to compose other schemes and models.
|
|
53
|
+
*
|
|
54
|
+
* ### Example
|
|
55
|
+
*
|
|
56
|
+
* ```typescript
|
|
57
|
+
* @MongooseSchema()
|
|
58
|
+
* export class EventSchema {
|
|
59
|
+
* @Property()
|
|
60
|
+
* field: string;
|
|
61
|
+
* }
|
|
62
|
+
* ```
|
|
63
|
+
*
|
|
64
|
+
* ### Options
|
|
65
|
+
*
|
|
66
|
+
* - `schemaOptions` (mongoose.SchemaOptions): Option to configure the schema behavior.
|
|
67
|
+
*
|
|
68
|
+
* @param {MongooseSchemaOptions | undefined} options
|
|
69
|
+
* @returns {(target: any) => void}
|
|
70
|
+
* @decorator
|
|
71
|
+
* @mongoose
|
|
72
|
+
* @property
|
|
73
|
+
* @class
|
|
74
|
+
*/
|
|
75
|
+
export function MongooseSchema(options?: MongooseSchemaOptions): (target: any) => void;
|
|
76
|
+
export function MongooseSchema(definition: SchemaTypeOptions<any>): Function;
|
|
77
|
+
export function MongooseSchema(options: MongooseSchemaOptions | SchemaTypeOptions<any> = {}) {
|
|
78
|
+
return Schema(options as any);
|
|
79
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {Store} from "@tsed/core";
|
|
2
|
+
|
|
3
|
+
import {MONGOOSE_SCHEMA} from "../constants/constants.js";
|
|
4
|
+
import {SchemaIgnore} from "./schemaIgnore.js";
|
|
5
|
+
|
|
6
|
+
describe("@SchemaIgnore()", () => {
|
|
7
|
+
it("should set metadata", () => {
|
|
8
|
+
class Test {
|
|
9
|
+
@SchemaIgnore()
|
|
10
|
+
test: any;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const store = Store.from(Test, "test");
|
|
14
|
+
expect(store.get(MONGOOSE_SCHEMA)).toEqual({
|
|
15
|
+
schemaIgnore: true
|
|
16
|
+
});
|
|
17
|
+
});
|
|
18
|
+
});
|