Skip to content
This repository was archived by the owner on Dec 27, 2024. It is now read-only.

Commit 57ccb02

Browse files
committed
Resolves #243
1 parent 2177ec7 commit 57ccb02

File tree

6 files changed

+87
-40
lines changed

6 files changed

+87
-40
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@sakuraapi/core",
3-
"version": "0.19.6",
3+
"version": "0.20.0",
44
"description": "MongoDB and TypeScript MEAN Stack Framework for NodeJS",
55
"main": "lib/index.js",
66
"typings": "lib/index.d.ts",

src/core/@model/id.spec.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,19 @@ describe('@Id', () => {
4040
});
4141
});
4242

43+
it('allows default ID on instantiation', () => {
44+
45+
@Model()
46+
class DefaultValue extends SapiModelMixin() {
47+
@Id()
48+
id: ObjectID = new ObjectID();
49+
}
50+
51+
const result = new DefaultValue();
52+
expect(result.id).toBeDefined();
53+
54+
});
55+
4356
describe('@Db', () => {
4457
it('maps fromDb', () => {
4558
const id = new ObjectID();

src/core/@model/model-operators/from-db.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,7 @@ export function fromDb<T = InstanceType<ReturnType<typeof SapiModelMixin>>>(json
5454

5555
function mapDbToModel(this: ReturnType<typeof SapiModelMixin>,
5656
json: { [key: string]: any },
57-
model: InstanceType<ReturnType<typeof SapiModelMixin>>,
58-
isChild = false) {
57+
model: InstanceType<ReturnType<typeof SapiModelMixin>>) {
5958

6059
model = model || {} as InstanceType<ReturnType<typeof SapiModelMixin>>;
6160

@@ -108,9 +107,9 @@ function mapDbToModel(this: ReturnType<typeof SapiModelMixin>,
108107
} else {
109108
if (subModel) {
110109
const newModel = new subModel();
111-
value = Object.assign(newModel, mapDbToModel.call(newModel.constructor, json[key], nextModel, true));
110+
value = Object.assign(newModel, mapDbToModel.call(newModel.constructor, json[key], nextModel));
112111
} else {
113-
value = mapDbToModel.call(this, json[key], nextModel, true);
112+
value = mapDbToModel.call(this, json[key], nextModel);
114113
}
115114
}
116115

src/core/@model/model-operators/to-db.spec.ts

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -207,5 +207,44 @@ describe('Model.toDb', () => {
207207
});
208208

209209
});
210+
211+
describe('#243 @Id fields should map to _id in dbo', () => {
212+
213+
@Model()
214+
class Child extends SapiModelMixin() {
215+
@Id()
216+
id: ObjectID = new ObjectID();
217+
}
218+
219+
@Model({
220+
dbConfig: {
221+
collection: 'users',
222+
db: 'userDb'
223+
}
224+
})
225+
class Parent extends SapiModelMixin() {
226+
@Id()
227+
id: ObjectID = new ObjectID();
228+
229+
@Db({model: Child})
230+
children: Child[];
231+
}
232+
233+
it('toDb', async () => {
234+
235+
const parent = new Parent();
236+
parent.children = [
237+
new Child(),
238+
new Child()
239+
];
240+
241+
const result = parent.toDb();
242+
243+
expect(ObjectID.isValid(result._id)).toBeTruthy();
244+
expect(ObjectID.isValid(result.children[0]._id)).toBeTruthy();
245+
expect(ObjectID.isValid(result.children[1]._id)).toBeTruthy();
246+
247+
});
248+
});
210249
});
211250
});

src/core/@model/model-operators/to-db.ts

Lines changed: 26 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { shouldRecurse } from '../../lib';
22
import { dbSymbols, IDbOptions } from '../db';
3+
import { idSymbols } from '../id';
34
import { modelSymbols } from '../model';
45
import { debug } from './index';
56

@@ -27,71 +28,59 @@ export function toDb(changeSet?: any): object {
2728
debug.normal(`.toDb called, target '${(constructor || {} as any).name}'`);
2829

2930
changeSet = changeSet || this;
30-
31-
const dbObj = mapModelToDb.call(this, changeSet);
32-
33-
delete (dbObj as any).id;
34-
if (!(dbObj as any)._id && this._id) {
35-
(dbObj as any)._id = this._id;
36-
}
37-
38-
return dbObj;
31+
return mapModelToDb.call(this, changeSet);
3932
}
4033

41-
function mapModelToDb(source, depth = 0) {
34+
function mapModelToDb(model) {
4235

43-
const result = {} as any;
44-
if (!source) {
36+
const dbo = {} as any;
37+
if (!model) {
4538
return;
4639
}
4740

48-
const dbOptionsByPropertyName: Map<string, IDbOptions> = Reflect.getMetadata(dbSymbols.dbByPropertyName, source);
41+
const dbOptionsByPropertyName: Map<string, IDbOptions> = Reflect.getMetadata(dbSymbols.dbByPropertyName, model);
4942

5043
// iterate over each property
51-
const keys = Object.getOwnPropertyNames(source);
44+
const keys = Object.getOwnPropertyNames(model);
5245

5346
for (const key of keys) {
5447

55-
const map = keyMapper.call(this, key, source[key], dbOptionsByPropertyName) || {} as any;
56-
const model = map.model;
48+
const map = keyMapper.call(this, key, model[key], dbOptionsByPropertyName) || {} as any;
49+
const subModel = map.model;
5750

5851
if (!map.newKey) {
59-
// field is not mapped with @Db, and model is not promiscuous, skip it
52+
// field is not mapped with @Db or @Id, and model is not promiscuous, skip it
6053
continue;
6154
}
6255

6356
let value;
64-
if (model || shouldRecurse(source[key])) {
57+
if (subModel || shouldRecurse(model[key])) {
6558

66-
if (Array.isArray(source[key])) {
67-
++depth;
59+
if (Array.isArray(model[key])) {
6860
const values = [];
69-
for (const src of source[key]) {
70-
values.push(mapModelToDb.call(this, src, depth));
61+
for (const src of model[key]) {
62+
values.push(mapModelToDb.call(this, src));
7163
}
7264
value = values;
7365

7466
} else if (map.newKey !== undefined) {
7567

76-
value = mapModelToDb.call(this, source[key], ++depth);
68+
value = mapModelToDb.call(this, model[key]);
7769

7870
}
7971

8072
} else if (map.newKey !== undefined) {
8173

8274
// if a newKey (db key) has been defined, then set the value to the source [key] so that
8375
// result[map.newKey] will get source[key] - otherwise, result[map.newKey] will be undefined.
84-
value = source[key];
76+
value = model[key];
8577

8678
}
8779

88-
result[map.newKey] = value;
89-
}
90-
if (depth > 0 && (result._id && result.id)) { // resolves #106
91-
delete result.id;
80+
dbo[map.newKey] = value;
9281
}
9382

94-
return result;
83+
return dbo;
9584
}
9685

9786
function keyMapper(key, value, dbMeta): { model: any, newKey: string } {
@@ -103,8 +92,13 @@ function keyMapper(key, value, dbMeta): { model: any, newKey: string } {
10392
}
10493

10594
let fieldName: string;
106-
// if there's @Db meta data on the property
107-
if (dbMeta && dbMeta.get) {
95+
const idProperty = Reflect.getMetadata(idSymbols.idByPropertyName, this);
96+
97+
if (idProperty && idProperty === key) { // if this is an @Id field
98+
99+
fieldName = '_id';
100+
101+
} else if (dbMeta && dbMeta.get) { // if there's @Db meta data on the property
108102
const dbOptions = (dbMeta.get(key)) as IDbOptions;
109103

110104
if ((dbOptions || {}).field) {
@@ -118,7 +112,7 @@ function keyMapper(key, value, dbMeta): { model: any, newKey: string } {
118112

119113
// if the model's promiscuous use the property name for the field name if @Db wasn't found...
120114
// otherwise leave the field out of the results
121-
if (!fieldName && ((modelOptions.dbConfig || {}).promiscuous || (this[modelSymbols.modelOptions] || {} as any).promiscuous)) {
115+
if (!fieldName && ((modelOptions.dbConfig || {}).promiscuous || (modelOptions || {} as any).promiscuous)) {
122116
fieldName = key;
123117
}
124118

src/core/@model/model.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,10 +244,12 @@ export function Model(modelOptions?: IModelOptions): (object) => any {
244244
throw new Error(`Model ${target.name} defines 'dbConfig' but does not have an @Id decorated properties`);
245245
}
246246

247-
// map _id to id
248-
newConstructor.prototype._id = undefined;
249-
250247
if (idProperty) {
248+
// if @Id has a default value, move it to _id
249+
if (c[idProperty]) {
250+
c._id = c[idProperty];
251+
}
252+
251253
Reflect.defineProperty(c, idProperty, {
252254
configurable: true,
253255
enumerable: true,

0 commit comments

Comments
 (0)