Skip to content

Commit

Permalink
fix(mongoose): fix ref decorator when the collection type is used
Browse files Browse the repository at this point in the history
Closes: #2439
  • Loading branch information
Romakita committed Sep 20, 2023
1 parent fa119a6 commit 0799087
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 2 deletions.
13 changes: 11 additions & 2 deletions packages/orm/mongoose/src/decorators/ref.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {isArrowFn, isObject, isObjectID, isString, StoreMerge, Type, useDecorators} from "@tsed/core";
import {OnDeserialize, OnSerialize, serialize, deserialize} from "@tsed/json-mapper";
import {isArrowFn, isCollection, isObject, isObjectID, isString, StoreMerge, Type, useDecorators} from "@tsed/core";
import {deserialize, OnDeserialize, OnSerialize, serialize} from "@tsed/json-mapper";
import {ForwardGroups, JsonEntityFn, lazyRef, matchGroups, OneOf, Property, string} from "@tsed/schema";
import {Schema as MongooseSchema} from "mongoose";
import {MONGOOSE_SCHEMA} from "../constants/constants";
Expand Down Expand Up @@ -83,12 +83,21 @@ export function Ref(
return value.toString();
}

if (isCollection(value) && isRef(value[0])) {
return value.map(String);
}

return deserialize(value, {type: getType(), useAlias: false});
}),
OnSerialize((value: any, ctx) => {
if (isRef(value)) {
return value.toString();
}

if (isCollection(value) && isRef(value[0])) {
return value.map(String);
}

const type = getType();

return serialize(value, {...ctx, type});
Expand Down
12 changes: 12 additions & 0 deletions packages/orm/mongoose/test/helpers/models/User.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@ export class TestProfile extends BaseModel {
user: Ref<TestUser>;
}

@Model({name: "profile_2", schemaOptions: {timestamps: {createdAt: "created", updatedAt: "updated"}}})
export class TestProfile2 extends BaseModel {
@Property()
image: string;

@Property()
age: number;

@Ref(TestUser)
users: Ref<TestUser>[];
}

@Model({schemaOptions: {timestamps: true}})
export class SelfUser {
@ObjectID()
Expand Down
177 changes: 177 additions & 0 deletions packages/orm/mongoose/test/ref-array.integration.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import faker from "@faker-js/faker";
import {BodyParams, Controller, Get, Inject, PlatformTest, Post, QueryParams} from "@tsed/common";
import {deserialize, serialize} from "@tsed/json-mapper";
import {MongooseModel} from "@tsed/mongoose";
import {PlatformExpress} from "@tsed/platform-express";
import {TestMongooseContext} from "@tsed/testing-mongoose";
import SuperTest from "supertest";
import {TestProfile2, TestUser} from "./helpers/models/User";
import {Server} from "./helpers/Server";

@Controller("/profiles")
class ProfilesCtrl {
@Inject(TestUser)
private UserModel: MongooseModel<TestUser>;

@Inject(TestProfile2)
private ProfileModel2: MongooseModel<TestProfile2>;

@Post("/user")
createUser(@BodyParams() body: TestUser) {
return new this.UserModel(body).save();
}

@Post("/")
createProfile(@BodyParams() body: TestProfile2) {
return new this.ProfileModel2(body).save();
}

@Get("/")
getTest(@QueryParams("full") full: boolean) {
return full ? this.ProfileModel2.find().populate("users") : this.ProfileModel2.find();
}
}

const baseUser = {
email: faker.internet.email(),
password: faker.internet.password(12)
};

const baseProfile = {
image: faker.image.avatar(),
age: faker.datatype.number(2)
};

describe("Mongoose", () => {
describe("Ref", () => {
let request: SuperTest.SuperTest<SuperTest.Test>;
let currentUser: any;
let currentProfile: any;
beforeEach(
TestMongooseContext.bootstrap(Server, {
platform: PlatformExpress,
mount: {
"/rest": [ProfilesCtrl]
}
})
);
beforeEach(async () => {
const repository = PlatformTest.get<ProfilesCtrl>(ProfilesCtrl)!;

currentUser = await repository.createUser(deserialize<TestUser>(baseUser, {type: TestUser}));

currentProfile = await repository.createProfile(
deserialize<TestProfile2>(
{
...baseProfile,
users: [currentUser._id]
},
{
type: TestProfile2
}
)
);
});
beforeEach(() => {
request = SuperTest(PlatformTest.callback());
});

afterEach(TestMongooseContext.reset);

it("should deserialize class with ref", () => {
const result = deserialize(
{
image: "url",
age: 12,
users: [
{
email: "[email protected]",
password: "password"
}
]
},
{type: TestProfile2}
);

expect(result).toEqual({
age: 12,
image: "url",
users: [
{
alwaysIgnored: "hello ignore",
email: "[email protected]",
password: "password"
}
]
});
expect(result.users[0]).toBeInstanceOf(TestUser);
});

it("should transform mongoose instance to class", () => {
const result = currentUser.toClass();

expect(result).toBeInstanceOf(TestUser);
expect(typeof result._id).toBe("string");
expect(result.alwaysIgnored).toBe("hello ignore");
expect(Date.parse(result.created)).not.toBeNaN();
expect(result.email).toBe(currentUser.email);
expect(result.password).toBe(currentUser.password);
expect(result.post).toBe("hello post");
expect(result.pre).toBe("hello pre");
expect(Date.parse(result.updated)).not.toBeNaN();
});

it("should transform mongoose instance to object", () => {
const result = serialize(currentUser, {type: TestUser, endpoint: true});

expect(result).toBeInstanceOf(Object);
expect(typeof result.id).toBe("string");
expect(result.alwaysIgnored).toBeUndefined();
expect(Date.parse(result.created)).not.toBeNaN();
expect(result.email).toBe(currentUser.email);
expect(result.password).toBe(currentUser.password);
expect(result.post).toBe("hello post");
expect(result.pre).toBe("hello pre");
expect(typeof result.updated).toBe("string");
});

it("GET /profiles full=true", async () => {
const {body: list} = await request.get(`/rest/profiles?full=true`);

expect(list).toEqual([
{
id: list[0].id,
age: baseProfile.age,
image: baseProfile.image,
created: list[0].created,
updated: list[0].updated,
users: [
{
id: list[0].users[0].id,
email: baseUser.email,
password: baseUser.password,
pre: "hello pre",
created: list[0].users[0].created,
updated: list[0].users[0].updated
}
]
}
]);
});

it("GET /profiles full=false", async () => {
const {body: list} = await request.get(`/rest/profiles?full=false`);

expect(list).toEqual([
{
id: String(list[0].id),
created: String(list[0].created),
updated: String(list[0].updated),
age: baseProfile.age,
image: baseProfile.image,
users: [String(list[0].users[0])]
}
]);
});
});
});

0 comments on commit 0799087

Please sign in to comment.