Skip to content

Commit ce6d3cd

Browse files
kaufmonsams
andauthored
API Generator: Add support for extending entities (#3846)
Co-authored-by: Niko Sams <[email protected]>
1 parent e5b3b3d commit ce6d3cd

File tree

3 files changed

+83
-2
lines changed

3 files changed

+83
-2
lines changed

.changeset/small-islands-create.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@comet/api-generator": minor
3+
---
4+
5+
Add basic support for inheritance used by entities
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { BaseEntity, defineConfig, Entity, MikroORM, PrimaryKey, Property } from "@mikro-orm/postgresql";
2+
import { Field } from "@nestjs/graphql";
3+
import { LazyMetadataStorage } from "@nestjs/graphql/dist/schema-builder/storages/lazy-metadata.storage";
4+
import { v4 as uuid } from "uuid";
5+
6+
import { formatGeneratedFiles, parseSource } from "../../utils/test-helper";
7+
import { generateCrud } from "../generate-crud";
8+
9+
@Entity({ abstract: true })
10+
export abstract class TimestampEntity extends BaseEntity {
11+
@Property({
12+
columnType: "timestamp with time zone",
13+
})
14+
@Field()
15+
createdAt: Date = new Date();
16+
17+
@Property({ onUpdate: () => new Date(), columnType: "timestamp with time zone" })
18+
@Field()
19+
updatedAt: Date = new Date();
20+
}
21+
22+
@Entity()
23+
export class TestEntityWithTimestamps extends TimestampEntity {
24+
@PrimaryKey({ type: "uuid" })
25+
id: string = uuid();
26+
27+
@Property()
28+
foo: string;
29+
}
30+
31+
describe("GenerateCrudInputExtendEntity", () => {
32+
it("should include timestamp fields in the generated CRUD files", async () => {
33+
LazyMetadataStorage.load();
34+
const orm = await MikroORM.init(
35+
defineConfig({
36+
dbName: "test-db",
37+
connect: false,
38+
entities: [TestEntityWithTimestamps, TimestampEntity],
39+
}),
40+
);
41+
42+
const out = await generateCrud({ targetDirectory: __dirname }, orm.em.getMetadata().get("TestEntityWithTimestamps"));
43+
const formattedOut = await formatGeneratedFiles(out);
44+
45+
const file = formattedOut.find((file) => file.name === "dto/test-entity-with-timestamps.filter.ts");
46+
if (!file) throw new Error("File not found");
47+
48+
const source = parseSource(file.content);
49+
50+
const classes = source.getClasses();
51+
expect(classes.length).toBe(1);
52+
53+
const cls = classes[0];
54+
expect(cls.getName()).toBe("TestEntityWithTimestampsFilter");
55+
56+
const structure = cls.getStructure();
57+
const properties = structure.properties || [];
58+
59+
const createdAtField = properties.find((prop) => prop.name === "createdAt");
60+
const updatedAtField = properties.find((prop) => prop.name === "updatedAt");
61+
62+
expect(createdAtField).toBeDefined();
63+
expect(createdAtField?.type).toBe("DateTimeFilter");
64+
65+
expect(updatedAtField).toBeDefined();
66+
expect(updatedAtField?.type).toBe("DateTimeFilter");
67+
68+
orm.close();
69+
});
70+
});

packages/api/api-generator/src/commands/generate/utils/ts-morph-helper.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,14 @@ function morphTsClass(metadata: EntityMetadata<any>) {
2121
return tsClass;
2222
}
2323
export function morphTsProperty(name: string, metadata: EntityMetadata<any>) {
24-
const tsClass = morphTsClass(metadata);
25-
return tsClass.getPropertyOrThrow(name);
24+
let currentClass: ClassDeclaration | undefined = morphTsClass(metadata);
25+
while (currentClass) {
26+
const prop = currentClass.getProperty(name);
27+
if (prop) return prop;
28+
29+
currentClass = currentClass.getBaseClass();
30+
}
31+
throw new Error(`Property ${name} not found in ${metadata.className}`);
2632
}
2733

2834
function findImportPath(importName: string, targetDirectory: string, metadata: EntityMetadata<any>) {

0 commit comments

Comments
 (0)