@cheetah.js/orm 0.1.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/README.md +228 -0
- package/build.ts +14 -0
- package/cheetah.config.ts +14 -0
- package/dist/SqlBuilder.d.ts +57 -0
- package/dist/SqlBuilder.js +436 -0
- package/dist/SqlBuilder.js.map +1 -0
- package/dist/bun/index.d.ts +13 -0
- package/dist/bun/index.js +224319 -0
- package/dist/bun/index.js.map +306 -0
- package/dist/cheetah.d.ts +1 -0
- package/dist/cheetah.js +24 -0
- package/dist/cheetah.js.map +1 -0
- package/dist/constants.d.ts +4 -0
- package/dist/constants.js +5 -0
- package/dist/constants.js.map +1 -0
- package/dist/decorators/entity.decorator.d.ts +3 -0
- package/dist/decorators/entity.decorator.js +10 -0
- package/dist/decorators/entity.decorator.js.map +1 -0
- package/dist/decorators/index.decorator.d.ts +3 -0
- package/dist/decorators/index.decorator.js +17 -0
- package/dist/decorators/index.decorator.js.map +1 -0
- package/dist/decorators/one-many.decorator.d.ts +3 -0
- package/dist/decorators/one-many.decorator.js +17 -0
- package/dist/decorators/one-many.decorator.js.map +1 -0
- package/dist/decorators/primary-key.decorator.d.ts +2 -0
- package/dist/decorators/primary-key.decorator.js +6 -0
- package/dist/decorators/primary-key.decorator.js.map +1 -0
- package/dist/decorators/property.decorator.d.ts +15 -0
- package/dist/decorators/property.decorator.js +25 -0
- package/dist/decorators/property.decorator.js.map +1 -0
- package/dist/domain/base-entity.d.ts +42 -0
- package/dist/domain/base-entity.js +106 -0
- package/dist/domain/base-entity.js.map +1 -0
- package/dist/domain/collection.d.ts +6 -0
- package/dist/domain/collection.js +11 -0
- package/dist/domain/collection.js.map +1 -0
- package/dist/domain/entities.d.ts +40 -0
- package/dist/domain/entities.js +137 -0
- package/dist/domain/entities.js.map +1 -0
- package/dist/domain/reference.d.ts +4 -0
- package/dist/domain/reference.js +7 -0
- package/dist/domain/reference.js.map +1 -0
- package/dist/driver/driver.interface.d.ts +270 -0
- package/dist/driver/driver.interface.js +2 -0
- package/dist/driver/driver.interface.js.map +1 -0
- package/dist/driver/pg-driver.d.ts +43 -0
- package/dist/driver/pg-driver.js +255 -0
- package/dist/driver/pg-driver.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/migration/diff-calculator.d.ts +17 -0
- package/dist/migration/diff-calculator.js +230 -0
- package/dist/migration/diff-calculator.js.map +1 -0
- package/dist/migration/migrator.d.ts +18 -0
- package/dist/migration/migrator.js +233 -0
- package/dist/migration/migrator.js.map +1 -0
- package/dist/orm.d.ts +14 -0
- package/dist/orm.js +23 -0
- package/dist/orm.js.map +1 -0
- package/dist/orm.service.d.ts +8 -0
- package/dist/orm.service.js +115 -0
- package/dist/orm.service.js.map +1 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +16 -0
- package/dist/utils.js.map +1 -0
- package/package.json +50 -0
- package/src/SqlBuilder.ts +542 -0
- package/src/cheetah.ts +28 -0
- package/src/constants.ts +4 -0
- package/src/decorators/entity.decorator.ts +10 -0
- package/src/decorators/index.decorator.ts +18 -0
- package/src/decorators/one-many.decorator.ts +19 -0
- package/src/decorators/primary-key.decorator.ts +6 -0
- package/src/decorators/property.decorator.ts +41 -0
- package/src/domain/base-entity.ts +149 -0
- package/src/domain/collection.ts +10 -0
- package/src/domain/entities.ts +159 -0
- package/src/domain/reference.ts +5 -0
- package/src/driver/driver.interface.ts +331 -0
- package/src/driver/pg-driver.ts +308 -0
- package/src/index.ts +17 -0
- package/src/migration/diff-calculator.ts +258 -0
- package/src/migration/migrator.ts +278 -0
- package/src/orm.service.ts +115 -0
- package/src/orm.ts +30 -0
- package/src/utils.ts +18 -0
- package/test/domain/base-entity.spec.ts +463 -0
- package/test/migration/.sql +5 -0
- package/test/migration/cheetah.config.ts +13 -0
- package/test/migration/migator.spec.ts +251 -0
- package/test/migration/test.sql +5 -0
- package/test/node-database.ts +32 -0
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import 'reflect-metadata'
|
|
2
|
+
import { afterEach, beforeEach, describe, expect, it, jest } from 'bun:test';
|
|
3
|
+
import { Migrator } from '../../src/migration/migrator';
|
|
4
|
+
import config from './cheetah.config';
|
|
5
|
+
import { BaseEntity, Entity, ManyToOne, PrimaryKey, Property } from '../../src';
|
|
6
|
+
import { execute, mockLogger, purgeDatabase, startDatabase } from '../node-database';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import * as fs from 'fs';
|
|
9
|
+
import { Metadata } from '@cheetah.js/core';
|
|
10
|
+
import { ENTITIES } from '../../src/constants';
|
|
11
|
+
import { Index } from '../../src/decorators/index.decorator';
|
|
12
|
+
|
|
13
|
+
describe('Migration', () => {
|
|
14
|
+
|
|
15
|
+
beforeEach(async () => {
|
|
16
|
+
await startDatabase();
|
|
17
|
+
Metadata.delete(ENTITIES, Reflect);
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
afterEach(async () => {
|
|
21
|
+
await purgeDatabase();
|
|
22
|
+
(mockLogger as jest.Mock).mockClear();
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
it('When search de config file', async () => {
|
|
26
|
+
const migrator = new Migrator();
|
|
27
|
+
await migrator.initConfigFile();
|
|
28
|
+
|
|
29
|
+
expect(migrator.config).toEqual(config)
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('When not search de config file', async () => {
|
|
33
|
+
const migrator = new Migrator();
|
|
34
|
+
expect(async () => {
|
|
35
|
+
await migrator.initConfigFile('src')
|
|
36
|
+
}).toThrow("Config file not found");
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it('should snapshot database', async () => {
|
|
40
|
+
class User extends BaseEntity {
|
|
41
|
+
@PrimaryKey()
|
|
42
|
+
id: number;
|
|
43
|
+
|
|
44
|
+
@Property({ unique: true })
|
|
45
|
+
email: string;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
Entity()(User);
|
|
49
|
+
const migrator = new Migrator();
|
|
50
|
+
await migrator.initConfigFile();
|
|
51
|
+
await migrator.createMigration( 'test');
|
|
52
|
+
|
|
53
|
+
// Caminho para o arquivo gerado
|
|
54
|
+
const migrationFilePath = path.join(__dirname, '/test.sql');
|
|
55
|
+
|
|
56
|
+
// Leitura do conteúdo do arquivo
|
|
57
|
+
const migrationContent = fs.readFileSync(migrationFilePath, {encoding: 'utf-8'});
|
|
58
|
+
|
|
59
|
+
// Verificação do conteúdo conforme necessário
|
|
60
|
+
expect(migrationContent).toContain('CREATE TABLE \"public\".\"user\" (\"id\" numeric(11) NOT NULL PRIMARY KEY UNIQUE,\"email\" character varying(255) NOT NULL UNIQUE);');
|
|
61
|
+
await execute(migrationContent);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('should modify database column', async () => {
|
|
65
|
+
await execute("CREATE TABLE \"public\".\"user\" (\"id\" numeric(11) PRIMARY KEY UNIQUE,\"email\" character varying(255) UNIQUE);")
|
|
66
|
+
class User extends BaseEntity {
|
|
67
|
+
@PrimaryKey()
|
|
68
|
+
id: number;
|
|
69
|
+
|
|
70
|
+
@Property()
|
|
71
|
+
email: string;
|
|
72
|
+
|
|
73
|
+
@Property({length: 10})
|
|
74
|
+
password: string;
|
|
75
|
+
|
|
76
|
+
@Property({nullable: true})
|
|
77
|
+
token?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
Entity()(User);
|
|
81
|
+
|
|
82
|
+
const migrator = new Migrator();
|
|
83
|
+
await migrator.initConfigFile();
|
|
84
|
+
await migrator.createMigration( 'test');
|
|
85
|
+
const migrationFilePath = path.join(__dirname, '/test.sql');
|
|
86
|
+
const migrationContent = fs.readFileSync(migrationFilePath, {encoding: 'utf-8'});
|
|
87
|
+
|
|
88
|
+
expect(migrationContent).toContain("ALTER TABLE \"public\".\"user\" DROP CONSTRAINT \"user_email_key\";");
|
|
89
|
+
expect(migrationContent).toContain('ALTER TABLE \"public\".\"user\" ADD COLUMN \"password\" character varying(10) NOT NULL')
|
|
90
|
+
expect(migrationContent).toContain('ALTER TABLE \"public\".\"user\" ADD COLUMN \"token\" character varying(255);')
|
|
91
|
+
await execute(migrationContent);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it('should modify a column unique', async () => {
|
|
95
|
+
await execute("CREATE TABLE \"public\".\"user\" (\"id\" numeric(11) NOT NULL PRIMARY KEY UNIQUE,\"email\" character varying(255) NOT NULL unique, \"password\" character varying(10) NOT NULL);")
|
|
96
|
+
class User extends BaseEntity {
|
|
97
|
+
@PrimaryKey()
|
|
98
|
+
id: number;
|
|
99
|
+
|
|
100
|
+
@Property()
|
|
101
|
+
email: string;
|
|
102
|
+
|
|
103
|
+
@Property({length: 10, unique: true})
|
|
104
|
+
password: string;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
Entity()(User);
|
|
108
|
+
|
|
109
|
+
const migrator = new Migrator();
|
|
110
|
+
await migrator.initConfigFile();
|
|
111
|
+
await migrator.createMigration( 'test');
|
|
112
|
+
const migrationFilePath = path.join(__dirname, '/test.sql');
|
|
113
|
+
const migrationContent = fs.readFileSync(migrationFilePath, {encoding: 'utf-8'});
|
|
114
|
+
|
|
115
|
+
expect(migrationContent).toContain("ALTER TABLE \"public\".\"user\" DROP CONSTRAINT \"user_email_key\";");
|
|
116
|
+
expect(migrationContent).toContain("ALTER TABLE \"public\".\"user\" ADD UNIQUE (\"password\");")
|
|
117
|
+
await execute(migrationContent);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('should add a relation property', async () => {
|
|
121
|
+
await execute("CREATE TABLE \"public\".\"user\" (\"id\" numeric(11) NOT NULL PRIMARY KEY UNIQUE,\"email\" character varying(255) NOT NULL unique);")
|
|
122
|
+
await execute("CREATE TABLE \"public\".\"address\" (\"id\" numeric(11) NOT NULL PRIMARY KEY UNIQUE);")
|
|
123
|
+
class User extends BaseEntity {
|
|
124
|
+
@PrimaryKey()
|
|
125
|
+
id: number;
|
|
126
|
+
|
|
127
|
+
@Property()
|
|
128
|
+
email: string;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
class Address extends BaseEntity {
|
|
132
|
+
@PrimaryKey()
|
|
133
|
+
id: number;
|
|
134
|
+
|
|
135
|
+
@ManyToOne(() => User)
|
|
136
|
+
user: User;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
Entity()(User);
|
|
140
|
+
Entity()(Address);
|
|
141
|
+
|
|
142
|
+
const migrator = new Migrator();
|
|
143
|
+
await migrator.initConfigFile();
|
|
144
|
+
await migrator.createMigration( 'test');
|
|
145
|
+
const migrationFilePath = path.join(__dirname, '/test.sql');
|
|
146
|
+
const migrationContent = fs.readFileSync(migrationFilePath, {encoding: 'utf-8'});
|
|
147
|
+
|
|
148
|
+
expect(migrationContent).toContain("ALTER TABLE \"public\".\"user\" DROP CONSTRAINT \"user_email_key\";");
|
|
149
|
+
expect(migrationContent).toContain("ALTER TABLE \"public\".\"address\" ADD COLUMN \"user\" numeric(11) NOT NULL;");
|
|
150
|
+
expect(migrationContent).toContain("ALTER TABLE \"public\".\"address\" ADD CONSTRAINT \"address_user_fk\" FOREIGN KEY (\"user\") REFERENCES \"user\" (\"id\");")
|
|
151
|
+
await execute(migrationContent);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
it('should add a relation property', async () => {
|
|
155
|
+
await execute("CREATE TABLE \"public\".\"user\" (\"id\" numeric(11) NOT NULL PRIMARY KEY UNIQUE,\"email\" character varying(255) NOT NULL);")
|
|
156
|
+
await execute("CREATE TABLE \"public\".\"address\" (\"id\" numeric(11) NOT NULL PRIMARY KEY UNIQUE, \"user\" numeric(11) NOT NULL);")
|
|
157
|
+
await execute("ALTER TABLE \"public\".\"address\" ADD CONSTRAINT \"address_user_fk\" FOREIGN KEY (\"user\") REFERENCES \"public\".\"user\" (\"id\");")
|
|
158
|
+
|
|
159
|
+
class User extends BaseEntity {
|
|
160
|
+
@PrimaryKey()
|
|
161
|
+
id: number;
|
|
162
|
+
|
|
163
|
+
@Property()
|
|
164
|
+
email: string;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
class Address extends BaseEntity {
|
|
168
|
+
@PrimaryKey()
|
|
169
|
+
id: number;
|
|
170
|
+
|
|
171
|
+
@Property({length: 11, nullable: true})
|
|
172
|
+
user: number;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
Entity()(User);
|
|
176
|
+
Entity()(Address);
|
|
177
|
+
|
|
178
|
+
const migrator = new Migrator();
|
|
179
|
+
await migrator.initConfigFile();
|
|
180
|
+
await migrator.createMigration( 'test');
|
|
181
|
+
const migrationFilePath = path.join(__dirname, '/test.sql');
|
|
182
|
+
const migrationContent = fs.readFileSync(migrationFilePath, {encoding: 'utf-8'});
|
|
183
|
+
|
|
184
|
+
expect(migrationContent).toContain("ALTER TABLE \"public\".\"address\" DROP CONSTRAINT \"address_user_fk\";")
|
|
185
|
+
expect(migrationContent).toContain("ALTER TABLE \"public\".\"address\" ALTER COLUMN \"user\" DROP NOT NULL;")
|
|
186
|
+
await execute(migrationContent);
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
it('should add a index property with multiple indexes', async () => {
|
|
190
|
+
await execute("CREATE TABLE \"public\".\"user\" (\"id\" numeric(11) NOT NULL PRIMARY KEY UNIQUE,\"email\" character varying(255) NOT NULL);")
|
|
191
|
+
|
|
192
|
+
class User extends BaseEntity {
|
|
193
|
+
@PrimaryKey()
|
|
194
|
+
@Index({properties: ['id', 'email']})
|
|
195
|
+
id: number;
|
|
196
|
+
|
|
197
|
+
@Property()
|
|
198
|
+
@Index()
|
|
199
|
+
email: string;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
Entity()(User);
|
|
203
|
+
|
|
204
|
+
const migrator = new Migrator();
|
|
205
|
+
await migrator.initConfigFile();
|
|
206
|
+
await migrator.createMigration( 'test');
|
|
207
|
+
const migrationFilePath = path.join(__dirname, '/test.sql');
|
|
208
|
+
const migrationContent = fs.readFileSync(migrationFilePath, {encoding: 'utf-8'});
|
|
209
|
+
|
|
210
|
+
expect(migrationContent).toContain("CREATE INDEX \"id_email_index\" ON \"public\".\"user\" (\"id\", \"email\");")
|
|
211
|
+
expect(migrationContent).toContain("CREATE INDEX \"email_index\" ON \"public\".\"user\" (\"email\");")
|
|
212
|
+
await execute(migrationContent);
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('should add a create with relations', async () => {
|
|
216
|
+
class User extends BaseEntity {
|
|
217
|
+
@PrimaryKey()
|
|
218
|
+
@Index({properties: ['id', 'email']})
|
|
219
|
+
id: number;
|
|
220
|
+
|
|
221
|
+
@Property()
|
|
222
|
+
@Index()
|
|
223
|
+
email: string;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
class Address extends BaseEntity {
|
|
227
|
+
@PrimaryKey()
|
|
228
|
+
id: number;
|
|
229
|
+
|
|
230
|
+
@ManyToOne(() => User)
|
|
231
|
+
user: User;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
Entity()(User);
|
|
235
|
+
Entity()(Address);
|
|
236
|
+
|
|
237
|
+
const migrator = new Migrator();
|
|
238
|
+
await migrator.initConfigFile();
|
|
239
|
+
await migrator.createMigration( 'test');
|
|
240
|
+
const migrationFilePath = path.join(__dirname, '/test.sql');
|
|
241
|
+
const migrationContent = fs.readFileSync(migrationFilePath, {encoding: 'utf-8'});
|
|
242
|
+
|
|
243
|
+
expect(migrationContent).toContain("CREATE TABLE \"public\".\"user\" (\"id\" numeric(11) NOT NULL PRIMARY KEY UNIQUE,\"email\" character varying(255) NOT NULL);")
|
|
244
|
+
expect(migrationContent).toContain("CREATE INDEX \"id_email_index\" ON \"public\".\"user\" (\"id\", \"email\");")
|
|
245
|
+
expect(migrationContent).toContain("CREATE INDEX \"email_index\" ON \"public\".\"user\" (\"email\");\n")
|
|
246
|
+
expect(migrationContent).toContain("CREATE TABLE \"public\".\"address\" (\"id\" numeric(11) NOT NULL PRIMARY KEY UNIQUE,\"user\" numeric(11) NOT NULL);")
|
|
247
|
+
expect(migrationContent).toContain("ALTER TABLE \"public\".\"address\" ADD CONSTRAINT \"address_user_fk\" FOREIGN KEY (\"user\") REFERENCES \"user\" (\"id\");")
|
|
248
|
+
expect(migrationContent.split('\n').length).toEqual(5)
|
|
249
|
+
await execute(migrationContent);
|
|
250
|
+
});
|
|
251
|
+
})
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
CREATE TABLE "public"."user" ("id" numeric(11) NOT NULL PRIMARY KEY UNIQUE,"email" character varying(255) NOT NULL);
|
|
2
|
+
CREATE INDEX "id_email_index" ON "public"."user" ("id", "email");
|
|
3
|
+
CREATE INDEX "email_index" ON "public"."user" ("email");
|
|
4
|
+
CREATE TABLE "public"."address" ("id" numeric(11) NOT NULL PRIMARY KEY UNIQUE,"user" numeric(11) NOT NULL);
|
|
5
|
+
ALTER TABLE "public"."address" ADD CONSTRAINT "address_user_fk" FOREIGN KEY ("user") REFERENCES "user" ("id");
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
import { Orm, OrmService, PgDriver } from '@cheetah.js/orm';
|
|
3
|
+
import { EntityStorage } from 'packages/orm/src/domain/entities';
|
|
4
|
+
import { LoggerService } from '@cheetah.js/core/services/logger.service';
|
|
5
|
+
import { spyOn } from 'bun:test';
|
|
6
|
+
|
|
7
|
+
const loggerInstance = new LoggerService({applicationConfig: {logger: { level: 'info'}}} as any)
|
|
8
|
+
export let app: Orm<PgDriver>
|
|
9
|
+
export const mockLogger = spyOn(loggerInstance, 'debug')
|
|
10
|
+
|
|
11
|
+
export async function startDatabase(entityFile: string | undefined = undefined, logger: LoggerService = loggerInstance) {
|
|
12
|
+
const service = new OrmService(new EntityStorage(), entityFile)
|
|
13
|
+
app = new Orm({
|
|
14
|
+
host: 'localhost',
|
|
15
|
+
port: 5432,
|
|
16
|
+
database: 'postgres',
|
|
17
|
+
username: 'postgres',
|
|
18
|
+
password: 'postgres',
|
|
19
|
+
driver: PgDriver,
|
|
20
|
+
}, logger)
|
|
21
|
+
service.onInit()
|
|
22
|
+
|
|
23
|
+
await app.connect()
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function purgeDatabase(schema: string = 'public') {
|
|
27
|
+
await app.driverInstance.executeSql(`DROP SCHEMA IF EXISTS ${schema} CASCADE; CREATE SCHEMA ${schema};`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function execute(sql: string) {
|
|
31
|
+
return await app.driverInstance.executeSql(sql);
|
|
32
|
+
}
|