@platformatic/sql-mapper 0.0.23
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/.nyc_output/0208d41a-48bc-4675-a861-0475eb461a17.json +1 -0
- package/.nyc_output/588169a6-88e9-4949-af5a-631822d7dc42.json +1 -0
- package/.nyc_output/5bbdf331-cd01-4869-9d54-3d708610224a.json +1 -0
- package/.nyc_output/6d7c60ad-a404-4a1d-af86-8a334cff5f02.json +1 -0
- package/.nyc_output/8dae7e8c-5022-4a0c-a5b3-bf547f97961b.json +1 -0
- package/.nyc_output/f63bf7c5-4f58-4b46-a822-6f5ccf5a54a8.json +1 -0
- package/.nyc_output/processinfo/0208d41a-48bc-4675-a861-0475eb461a17.json +1 -0
- package/.nyc_output/processinfo/588169a6-88e9-4949-af5a-631822d7dc42.json +1 -0
- package/.nyc_output/processinfo/5bbdf331-cd01-4869-9d54-3d708610224a.json +1 -0
- package/.nyc_output/processinfo/6d7c60ad-a404-4a1d-af86-8a334cff5f02.json +1 -0
- package/.nyc_output/processinfo/8dae7e8c-5022-4a0c-a5b3-bf547f97961b.json +1 -0
- package/.nyc_output/processinfo/f63bf7c5-4f58-4b46-a822-6f5ccf5a54a8.json +1 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/.taprc +1 -0
- package/LICENSE +201 -0
- package/NOTICE +13 -0
- package/README.md +13 -0
- package/lib/entity.js +287 -0
- package/lib/queries/index.js +23 -0
- package/lib/queries/mariadb.js +11 -0
- package/lib/queries/mysql-shared.js +62 -0
- package/lib/queries/mysql.js +104 -0
- package/lib/queries/pg.js +79 -0
- package/lib/queries/shared.js +100 -0
- package/lib/queries/sqlite.js +169 -0
- package/lib/utils.js +14 -0
- package/mapper.d.ts +308 -0
- package/mapper.js +155 -0
- package/package.json +44 -0
- package/test/entity.test.js +344 -0
- package/test/helper.js +66 -0
- package/test/hooks.test.js +325 -0
- package/test/inserted_at_updated_at.test.js +132 -0
- package/test/mapper.test.js +288 -0
- package/test/types/mapper.test-d.ts +64 -0
- package/test/where.test.js +316 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
|
|
2
|
+
const { test } = require('tap')
|
|
3
|
+
const { connect, plugin } = require('..')
|
|
4
|
+
const { clear, connInfo, isPg, isMysql, isSQLite } = require('./helper')
|
|
5
|
+
const fastify = require('fastify')
|
|
6
|
+
|
|
7
|
+
const fakeLogger = {
|
|
8
|
+
trace: () => {},
|
|
9
|
+
error: () => {}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
test('should throw if no connection string is provided', async ({ equal }) => {
|
|
13
|
+
try {
|
|
14
|
+
await connect({
|
|
15
|
+
connectionString: false
|
|
16
|
+
})
|
|
17
|
+
} catch (err) {
|
|
18
|
+
equal(err.message, 'connectionString is required')
|
|
19
|
+
}
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
test('[PG] return entities', { skip: !isPg }, async ({ pass, teardown, equal }) => {
|
|
23
|
+
async function onDatabaseLoad (db, sql) {
|
|
24
|
+
await clear(db, sql)
|
|
25
|
+
teardown(() => db.dispose())
|
|
26
|
+
|
|
27
|
+
await db.query(sql`CREATE TABLE pages (
|
|
28
|
+
id SERIAL PRIMARY KEY,
|
|
29
|
+
title VARCHAR(255) NOT NULL
|
|
30
|
+
);`)
|
|
31
|
+
}
|
|
32
|
+
const mapper = await connect({
|
|
33
|
+
connectionString: connInfo.connectionString,
|
|
34
|
+
log: fakeLogger,
|
|
35
|
+
onDatabaseLoad,
|
|
36
|
+
ignore: {},
|
|
37
|
+
hooks: {}
|
|
38
|
+
})
|
|
39
|
+
const pageEntity = mapper.entities.page
|
|
40
|
+
equal(pageEntity.name, 'Page')
|
|
41
|
+
equal(pageEntity.singularName, 'page')
|
|
42
|
+
equal(pageEntity.pluralName, 'pages')
|
|
43
|
+
pass()
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('[mysql] return entities', { skip: !isMysql }, async ({ pass, teardown, equal }) => {
|
|
47
|
+
async function onDatabaseLoad (db, sql) {
|
|
48
|
+
await clear(db, sql)
|
|
49
|
+
teardown(() => db.dispose())
|
|
50
|
+
|
|
51
|
+
await db.query(sql`CREATE TABLE pages (
|
|
52
|
+
id SERIAL PRIMARY KEY,
|
|
53
|
+
title VARCHAR(255) NOT NULL
|
|
54
|
+
);`)
|
|
55
|
+
}
|
|
56
|
+
const mapper = await connect({
|
|
57
|
+
connectionString: connInfo.connectionString,
|
|
58
|
+
log: fakeLogger,
|
|
59
|
+
onDatabaseLoad,
|
|
60
|
+
ignore: {},
|
|
61
|
+
hooks: {}
|
|
62
|
+
})
|
|
63
|
+
const pageEntity = mapper.entities.page
|
|
64
|
+
equal(pageEntity.name, 'Page')
|
|
65
|
+
equal(pageEntity.singularName, 'page')
|
|
66
|
+
equal(pageEntity.pluralName, 'pages')
|
|
67
|
+
pass()
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
test('[sqlite] return entities', { skip: !isSQLite }, async ({ pass, teardown, equal }) => {
|
|
71
|
+
async function onDatabaseLoad (db, sql) {
|
|
72
|
+
teardown(async () => await clear(db, sql))
|
|
73
|
+
teardown(() => db.dispose())
|
|
74
|
+
|
|
75
|
+
await db.query(sql`CREATE TABLE IF NOT EXISTS pages (
|
|
76
|
+
id SERIAL PRIMARY KEY,
|
|
77
|
+
title VARCHAR(255) NOT NULL
|
|
78
|
+
);`)
|
|
79
|
+
}
|
|
80
|
+
const mapper = await connect({
|
|
81
|
+
connectionString: connInfo.connectionString,
|
|
82
|
+
log: fakeLogger,
|
|
83
|
+
onDatabaseLoad,
|
|
84
|
+
ignore: {},
|
|
85
|
+
hooks: {}
|
|
86
|
+
})
|
|
87
|
+
const pageEntity = mapper.entities.page
|
|
88
|
+
equal(pageEntity.name, 'Page')
|
|
89
|
+
equal(pageEntity.singularName, 'page')
|
|
90
|
+
equal(pageEntity.pluralName, 'pages')
|
|
91
|
+
pass()
|
|
92
|
+
})
|
|
93
|
+
|
|
94
|
+
test('ignore tables', async ({ teardown, has }) => {
|
|
95
|
+
async function onDatabaseLoad (db, sql) {
|
|
96
|
+
teardown(async () => await clear(db, sql))
|
|
97
|
+
teardown(() => db.dispose())
|
|
98
|
+
|
|
99
|
+
await db.query(sql`CREATE TABLE IF NOT EXISTS pages (
|
|
100
|
+
id SERIAL PRIMARY KEY,
|
|
101
|
+
title VARCHAR(255) NOT NULL
|
|
102
|
+
);`)
|
|
103
|
+
|
|
104
|
+
await db.query(sql`CREATE TABLE IF NOT EXISTS users (
|
|
105
|
+
id SERIAL PRIMARY KEY,
|
|
106
|
+
username VARCHAR(255) NOT NULL
|
|
107
|
+
);`)
|
|
108
|
+
}
|
|
109
|
+
const mapper = await connect({
|
|
110
|
+
connectionString: connInfo.connectionString,
|
|
111
|
+
log: fakeLogger,
|
|
112
|
+
onDatabaseLoad,
|
|
113
|
+
ignore: { users: true },
|
|
114
|
+
hooks: {}
|
|
115
|
+
})
|
|
116
|
+
has(mapper.entities.users, undefined)
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
test('[PG] return entities with Fastify', { skip: !isPg }, async ({ pass, teardown, equal }) => {
|
|
120
|
+
async function onDatabaseLoad (db, sql) {
|
|
121
|
+
teardown(async () => await clear(db, sql))
|
|
122
|
+
|
|
123
|
+
await db.query(sql`CREATE TABLE IF NOT EXISTS pages (
|
|
124
|
+
id SERIAL PRIMARY KEY,
|
|
125
|
+
title VARCHAR(255) NOT NULL
|
|
126
|
+
);`)
|
|
127
|
+
}
|
|
128
|
+
const app = fastify()
|
|
129
|
+
teardown(() => app.close())
|
|
130
|
+
app.register(plugin, {
|
|
131
|
+
connectionString: connInfo.connectionString,
|
|
132
|
+
log: fakeLogger,
|
|
133
|
+
onDatabaseLoad
|
|
134
|
+
})
|
|
135
|
+
await app.ready()
|
|
136
|
+
const pageEntity = app.platformatic.entities.page
|
|
137
|
+
equal(pageEntity.name, 'Page')
|
|
138
|
+
equal(pageEntity.singularName, 'page')
|
|
139
|
+
equal(pageEntity.pluralName, 'pages')
|
|
140
|
+
pass()
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
test('[mysql] return entities', { skip: !isMysql }, async ({ pass, teardown, equal }) => {
|
|
144
|
+
async function onDatabaseLoad (db, sql) {
|
|
145
|
+
teardown(async () => await clear(db, sql))
|
|
146
|
+
|
|
147
|
+
await db.query(sql`CREATE TABLE IF NOT EXISTS pages (
|
|
148
|
+
id SERIAL PRIMARY KEY,
|
|
149
|
+
title VARCHAR(255) NOT NULL
|
|
150
|
+
);`)
|
|
151
|
+
}
|
|
152
|
+
const app = fastify()
|
|
153
|
+
teardown(() => app.close())
|
|
154
|
+
app.register(plugin, {
|
|
155
|
+
connectionString: connInfo.connectionString,
|
|
156
|
+
onDatabaseLoad
|
|
157
|
+
})
|
|
158
|
+
await app.ready()
|
|
159
|
+
const pageEntity = app.platformatic.entities.page
|
|
160
|
+
equal(pageEntity.name, 'Page')
|
|
161
|
+
equal(pageEntity.singularName, 'page')
|
|
162
|
+
equal(pageEntity.pluralName, 'pages')
|
|
163
|
+
pass()
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
test('[sqlite] return entities', { skip: !isSQLite }, async ({ pass, teardown, equal }) => {
|
|
167
|
+
async function onDatabaseLoad (db, sql) {
|
|
168
|
+
teardown(async () => await clear(db, sql))
|
|
169
|
+
|
|
170
|
+
await db.query(sql`CREATE TABLE IF NOT EXISTS pages (
|
|
171
|
+
id SERIAL PRIMARY KEY,
|
|
172
|
+
title VARCHAR(255) NOT NULL
|
|
173
|
+
);`)
|
|
174
|
+
}
|
|
175
|
+
const app = fastify()
|
|
176
|
+
teardown(() => app.close())
|
|
177
|
+
app.register(plugin, {
|
|
178
|
+
connectionString: connInfo.connectionString,
|
|
179
|
+
onDatabaseLoad
|
|
180
|
+
})
|
|
181
|
+
await app.ready()
|
|
182
|
+
const pageEntity = app.platformatic.entities.page
|
|
183
|
+
equal(pageEntity.name, 'Page')
|
|
184
|
+
equal(pageEntity.singularName, 'page')
|
|
185
|
+
equal(pageEntity.pluralName, 'pages')
|
|
186
|
+
pass()
|
|
187
|
+
})
|
|
188
|
+
|
|
189
|
+
test('missing connectionString', async ({ rejects }) => {
|
|
190
|
+
const app = fastify()
|
|
191
|
+
app.register(plugin)
|
|
192
|
+
|
|
193
|
+
await rejects(app.ready(), /connectionString/)
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
test('[pg] throws if no primary key', { skip: !isPg }, async ({ pass, teardown, equal }) => {
|
|
197
|
+
async function onDatabaseLoad (db, sql) {
|
|
198
|
+
await clear(db, sql)
|
|
199
|
+
teardown(() => db.dispose())
|
|
200
|
+
|
|
201
|
+
await db.query(sql`CREATE TABLE IF NOT EXISTS pages (
|
|
202
|
+
id SERIAL,
|
|
203
|
+
title VARCHAR(255) NOT NULL
|
|
204
|
+
);`)
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
await connect({
|
|
208
|
+
connectionString: connInfo.connectionString,
|
|
209
|
+
log: fakeLogger,
|
|
210
|
+
onDatabaseLoad,
|
|
211
|
+
ignore: {},
|
|
212
|
+
hooks: {}
|
|
213
|
+
})
|
|
214
|
+
} catch (err) {
|
|
215
|
+
equal(err.message, 'Cannot find primary key for Page entity')
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
|
|
219
|
+
test('[mysql] throws if no primary key', { skip: !isMysql }, async ({ pass, teardown, equal }) => {
|
|
220
|
+
async function onDatabaseLoad (db, sql) {
|
|
221
|
+
await clear(db, sql)
|
|
222
|
+
teardown(() => db.dispose())
|
|
223
|
+
|
|
224
|
+
await db.query(sql`CREATE TABLE pages (
|
|
225
|
+
id SERIAL,
|
|
226
|
+
title VARCHAR(255) NOT NULL
|
|
227
|
+
);`)
|
|
228
|
+
}
|
|
229
|
+
try {
|
|
230
|
+
await connect({
|
|
231
|
+
connectionString: connInfo.connectionString,
|
|
232
|
+
log: fakeLogger,
|
|
233
|
+
onDatabaseLoad,
|
|
234
|
+
ignore: {},
|
|
235
|
+
hooks: {}
|
|
236
|
+
})
|
|
237
|
+
} catch (err) {
|
|
238
|
+
equal(err.message, 'Cannot find primary key for Page entity')
|
|
239
|
+
}
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
test('[sqlite] throws if primary key is not defined', { skip: !isSQLite }, async ({ pass, teardown, equal }) => {
|
|
243
|
+
async function onDatabaseLoad (db, sql) {
|
|
244
|
+
await clear(db, sql)
|
|
245
|
+
|
|
246
|
+
await db.query(sql`CREATE TABLE pages(
|
|
247
|
+
id INTEGER NOT NULL,
|
|
248
|
+
title TEXT NOT NULL
|
|
249
|
+
);
|
|
250
|
+
`)
|
|
251
|
+
}
|
|
252
|
+
try {
|
|
253
|
+
await connect({
|
|
254
|
+
connectionString: connInfo.connectionString,
|
|
255
|
+
log: fakeLogger,
|
|
256
|
+
onDatabaseLoad,
|
|
257
|
+
ignore: {},
|
|
258
|
+
hooks: {}
|
|
259
|
+
})
|
|
260
|
+
} catch (err) {
|
|
261
|
+
equal(err.message, 'Cannot find primary key for Page entity')
|
|
262
|
+
}
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
test('[sqlite] throws with multiple primary keys', { skip: !isSQLite }, async ({ pass, teardown, equal }) => {
|
|
266
|
+
async function onDatabaseLoad (db, sql) {
|
|
267
|
+
await clear(db, sql)
|
|
268
|
+
|
|
269
|
+
await db.query(sql`CREATE TABLE pages(
|
|
270
|
+
id INTEGER NOT NULL,
|
|
271
|
+
author_id INTEGER NOT NULL,
|
|
272
|
+
title TEXT NOT NULL,
|
|
273
|
+
PRIMARY KEY (id, author_id)
|
|
274
|
+
);
|
|
275
|
+
`)
|
|
276
|
+
}
|
|
277
|
+
try {
|
|
278
|
+
await connect({
|
|
279
|
+
connectionString: connInfo.connectionString,
|
|
280
|
+
log: fakeLogger,
|
|
281
|
+
onDatabaseLoad,
|
|
282
|
+
ignore: {},
|
|
283
|
+
hooks: {}
|
|
284
|
+
})
|
|
285
|
+
} catch (err) {
|
|
286
|
+
equal(err.message, 'Table pages has 2 primary keys')
|
|
287
|
+
}
|
|
288
|
+
})
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { expectType } from 'tsd'
|
|
2
|
+
import { SQL, SQLQuery } from '@databases/sql'
|
|
3
|
+
import { fastify, FastifyInstance } from 'fastify'
|
|
4
|
+
import {
|
|
5
|
+
connect,
|
|
6
|
+
plugin,
|
|
7
|
+
utils,
|
|
8
|
+
Entity,
|
|
9
|
+
DBEntityField,
|
|
10
|
+
Database,
|
|
11
|
+
WhereCondition,
|
|
12
|
+
SQLMapperPluginInterface,
|
|
13
|
+
EntityHooks,
|
|
14
|
+
} from '../../mapper'
|
|
15
|
+
|
|
16
|
+
const pluginOptions: SQLMapperPluginInterface = await connect({ connectionString: '' })
|
|
17
|
+
expectType<Database>(pluginOptions.db)
|
|
18
|
+
expectType<SQL>(pluginOptions.sql)
|
|
19
|
+
expectType<(entityName: string, hooks: EntityHooks) => any>(pluginOptions.addEntityHooks)
|
|
20
|
+
expectType<{ [entityName: string]: Entity }>(pluginOptions.entities)
|
|
21
|
+
|
|
22
|
+
interface EntityFields {
|
|
23
|
+
id: number,
|
|
24
|
+
name: string,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const entity: Entity<EntityFields> = pluginOptions.entities.entityName
|
|
28
|
+
expectType<string>(entity.name)
|
|
29
|
+
expectType<string>(entity.singularName)
|
|
30
|
+
expectType<string>(entity.pluralName)
|
|
31
|
+
expectType<string>(entity.primaryKey)
|
|
32
|
+
expectType<string>(entity.table)
|
|
33
|
+
expectType<any[]>(entity.relations)
|
|
34
|
+
expectType<{ [columnName: string]: DBEntityField }>(entity.fields)
|
|
35
|
+
expectType<{ [columnName: string]: DBEntityField }>(entity.camelCasedFields)
|
|
36
|
+
expectType<(input: { [columnName: string]: any }) => { [columnName: string]: any }>(entity.fixInput)
|
|
37
|
+
expectType<(input: { [columnName: string]: any }) => { [columnName: string]: any }>(entity.fixOutput)
|
|
38
|
+
expectType<Partial<EntityFields>[]>(await entity.find())
|
|
39
|
+
expectType<Partial<EntityFields>[]>(await entity.insert({ inputs: [{ id: 1, name: 'test' }] }))
|
|
40
|
+
expectType<Partial<EntityFields>>(await entity.save({ input: { id: 1, name: 'test' } }))
|
|
41
|
+
expectType<Partial<EntityFields>[]>(await entity.delete())
|
|
42
|
+
|
|
43
|
+
expectType<SQLMapperPluginInterface>(await connect({ connectionString: '' }))
|
|
44
|
+
expectType<SQLMapperPluginInterface>(await connect({ connectionString: '', autoTimestamp: true }))
|
|
45
|
+
expectType<SQLMapperPluginInterface>(await connect({ connectionString: '', hooks: {} }))
|
|
46
|
+
expectType<SQLMapperPluginInterface>(await connect({ connectionString: '', hooks: {
|
|
47
|
+
Page: {
|
|
48
|
+
async find(options: any): Promise<any[]> { return [] },
|
|
49
|
+
async insert(options: { inputs: any[], fields?: string[] }): Promise<any[]> { return [] },
|
|
50
|
+
async save(options: { input: any, fields?: string[] }): Promise<any> { return {} },
|
|
51
|
+
async delete(options?: { where: WhereCondition, fields: string[] }): Promise<any[]> { return [] },
|
|
52
|
+
}
|
|
53
|
+
}}))
|
|
54
|
+
expectType<SQLMapperPluginInterface>(await connect({ connectionString: '', ignore: {} }))
|
|
55
|
+
expectType<SQLMapperPluginInterface>(await connect({ connectionString: '', onDatabaseLoad(db: Database, sql: SQL) {
|
|
56
|
+
expectType<(query: SQLQuery) => Promise<any[]>>(db.query)
|
|
57
|
+
expectType<() => Promise<void>>(db.dispose)
|
|
58
|
+
}}))
|
|
59
|
+
|
|
60
|
+
const instance: FastifyInstance = fastify()
|
|
61
|
+
instance.register(plugin, { connectionString: '', autoTimestamp: true })
|
|
62
|
+
instance.register((instance) => { expectType<SQLMapperPluginInterface>(instance.platformatic) })
|
|
63
|
+
|
|
64
|
+
expectType<(str: string) => string>(utils.toSingular)
|
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('tap')
|
|
4
|
+
const { connect } = require('..')
|
|
5
|
+
const { clear, connInfo, isMysql, isSQLite } = require('./helper')
|
|
6
|
+
const fakeLogger = {
|
|
7
|
+
trace: () => {},
|
|
8
|
+
error: () => {}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
test('list', async ({ pass, teardown, same, equal }) => {
|
|
12
|
+
const mapper = await connect({
|
|
13
|
+
...connInfo,
|
|
14
|
+
log: fakeLogger,
|
|
15
|
+
async onDatabaseLoad (db, sql) {
|
|
16
|
+
teardown(() => db.dispose())
|
|
17
|
+
pass('onDatabaseLoad called')
|
|
18
|
+
|
|
19
|
+
await clear(db, sql)
|
|
20
|
+
|
|
21
|
+
if (isSQLite) {
|
|
22
|
+
await db.query(sql`CREATE TABLE posts (
|
|
23
|
+
id INTEGER PRIMARY KEY,
|
|
24
|
+
title VARCHAR(42),
|
|
25
|
+
long_text TEXT,
|
|
26
|
+
counter INTEGER
|
|
27
|
+
);`)
|
|
28
|
+
} else {
|
|
29
|
+
await db.query(sql`CREATE TABLE posts (
|
|
30
|
+
id SERIAL PRIMARY KEY,
|
|
31
|
+
title VARCHAR(42),
|
|
32
|
+
long_text TEXT,
|
|
33
|
+
counter INTEGER
|
|
34
|
+
);`)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
const entity = mapper.entities.post
|
|
40
|
+
|
|
41
|
+
const posts = [{
|
|
42
|
+
title: 'Dog',
|
|
43
|
+
longText: 'Foo',
|
|
44
|
+
counter: 10
|
|
45
|
+
}, {
|
|
46
|
+
title: 'Cat',
|
|
47
|
+
longText: 'Bar',
|
|
48
|
+
counter: 20
|
|
49
|
+
}, {
|
|
50
|
+
title: 'Mouse',
|
|
51
|
+
longText: 'Baz',
|
|
52
|
+
counter: 30
|
|
53
|
+
}, {
|
|
54
|
+
title: 'Duck',
|
|
55
|
+
longText: 'A duck tale',
|
|
56
|
+
counter: 40
|
|
57
|
+
}]
|
|
58
|
+
|
|
59
|
+
await entity.insert({
|
|
60
|
+
inputs: posts
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
same(await entity.find({ where: { title: { eq: 'Dog' } }, fields: ['id', 'title', 'longText'] }), [{
|
|
64
|
+
id: '1',
|
|
65
|
+
title: 'Dog',
|
|
66
|
+
longText: 'Foo'
|
|
67
|
+
}])
|
|
68
|
+
|
|
69
|
+
same(await entity.find({ limit: 1, offset: 0, fields: ['id', 'title', 'longText'] }), [{
|
|
70
|
+
id: '1',
|
|
71
|
+
title: 'Dog',
|
|
72
|
+
longText: 'Foo'
|
|
73
|
+
}])
|
|
74
|
+
|
|
75
|
+
same(await entity.find({ limit: 1, offset: 0, orderBy: [{ field: 'id', direction: 'desc' }], fields: ['id', 'title'] }), [{
|
|
76
|
+
id: '4',
|
|
77
|
+
title: 'Duck'
|
|
78
|
+
}])
|
|
79
|
+
|
|
80
|
+
same(await entity.find({ where: { title: { neq: 'Dog' } }, fields: ['id', 'title', 'longText'] }), [{
|
|
81
|
+
id: '2',
|
|
82
|
+
title: 'Cat',
|
|
83
|
+
longText: 'Bar'
|
|
84
|
+
}, {
|
|
85
|
+
id: '3',
|
|
86
|
+
title: 'Mouse',
|
|
87
|
+
longText: 'Baz'
|
|
88
|
+
}, {
|
|
89
|
+
id: '4',
|
|
90
|
+
title: 'Duck',
|
|
91
|
+
longText: 'A duck tale'
|
|
92
|
+
}])
|
|
93
|
+
|
|
94
|
+
same(await entity.find({ where: { counter: { gt: 10 } }, fields: ['id', 'title', 'longText'] }), [{
|
|
95
|
+
id: '2',
|
|
96
|
+
title: 'Cat',
|
|
97
|
+
longText: 'Bar'
|
|
98
|
+
}, {
|
|
99
|
+
id: '3',
|
|
100
|
+
title: 'Mouse',
|
|
101
|
+
longText: 'Baz'
|
|
102
|
+
}, {
|
|
103
|
+
id: '4',
|
|
104
|
+
title: 'Duck',
|
|
105
|
+
longText: 'A duck tale'
|
|
106
|
+
}])
|
|
107
|
+
|
|
108
|
+
same(await entity.find({ where: { counter: { lt: 40 } }, fields: ['id', 'title', 'longText'] }), [{
|
|
109
|
+
id: '1',
|
|
110
|
+
title: 'Dog',
|
|
111
|
+
longText: 'Foo'
|
|
112
|
+
}, {
|
|
113
|
+
id: '2',
|
|
114
|
+
title: 'Cat',
|
|
115
|
+
longText: 'Bar'
|
|
116
|
+
}, {
|
|
117
|
+
id: '3',
|
|
118
|
+
title: 'Mouse',
|
|
119
|
+
longText: 'Baz'
|
|
120
|
+
}])
|
|
121
|
+
|
|
122
|
+
same(await entity.find({ where: { counter: { lte: 30 } }, fields: ['id', 'title', 'longText'] }), [{
|
|
123
|
+
id: '1',
|
|
124
|
+
title: 'Dog',
|
|
125
|
+
longText: 'Foo'
|
|
126
|
+
}, {
|
|
127
|
+
id: '2',
|
|
128
|
+
title: 'Cat',
|
|
129
|
+
longText: 'Bar'
|
|
130
|
+
}, {
|
|
131
|
+
id: '3',
|
|
132
|
+
title: 'Mouse',
|
|
133
|
+
longText: 'Baz'
|
|
134
|
+
}])
|
|
135
|
+
|
|
136
|
+
same(await entity.find({ where: { counter: { gte: 20 } }, fields: ['id', 'title', 'longText'] }), [{
|
|
137
|
+
id: '2',
|
|
138
|
+
title: 'Cat',
|
|
139
|
+
longText: 'Bar'
|
|
140
|
+
}, {
|
|
141
|
+
id: '3',
|
|
142
|
+
title: 'Mouse',
|
|
143
|
+
longText: 'Baz'
|
|
144
|
+
}, {
|
|
145
|
+
id: '4',
|
|
146
|
+
title: 'Duck',
|
|
147
|
+
longText: 'A duck tale'
|
|
148
|
+
}])
|
|
149
|
+
|
|
150
|
+
same(await entity.find({ where: { counter: { in: [20, 30] } }, fields: ['id', 'title', 'longText'] }), [{
|
|
151
|
+
id: '2',
|
|
152
|
+
title: 'Cat',
|
|
153
|
+
longText: 'Bar'
|
|
154
|
+
}, {
|
|
155
|
+
id: '3',
|
|
156
|
+
title: 'Mouse',
|
|
157
|
+
longText: 'Baz'
|
|
158
|
+
}])
|
|
159
|
+
|
|
160
|
+
same(await entity.find({ where: { counter: { nin: [10, 40] } }, fields: ['id', 'title', 'longText'] }), [{
|
|
161
|
+
id: '2',
|
|
162
|
+
title: 'Cat',
|
|
163
|
+
longText: 'Bar'
|
|
164
|
+
}, {
|
|
165
|
+
id: '3',
|
|
166
|
+
title: 'Mouse',
|
|
167
|
+
longText: 'Baz'
|
|
168
|
+
}])
|
|
169
|
+
|
|
170
|
+
same(await entity.find({ where: { counter: { gt: 10, lt: 40 } }, fields: ['id', 'title', 'longText'] }), [{
|
|
171
|
+
id: '2',
|
|
172
|
+
title: 'Cat',
|
|
173
|
+
longText: 'Bar'
|
|
174
|
+
}, {
|
|
175
|
+
id: '3',
|
|
176
|
+
title: 'Mouse',
|
|
177
|
+
longText: 'Baz'
|
|
178
|
+
}])
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
test('foreign keys', async ({ pass, teardown, same, equal }) => {
|
|
182
|
+
const mapper = await connect({
|
|
183
|
+
...connInfo,
|
|
184
|
+
log: fakeLogger,
|
|
185
|
+
async onDatabaseLoad (db, sql) {
|
|
186
|
+
teardown(() => db.dispose())
|
|
187
|
+
pass('onDatabaseLoad called')
|
|
188
|
+
|
|
189
|
+
await clear(db, sql)
|
|
190
|
+
|
|
191
|
+
if (isMysql) {
|
|
192
|
+
await db.query(sql`
|
|
193
|
+
CREATE TABLE owners (
|
|
194
|
+
id SERIAL PRIMARY KEY,
|
|
195
|
+
name VARCHAR(255)
|
|
196
|
+
);
|
|
197
|
+
CREATE TABLE posts (
|
|
198
|
+
id SERIAL PRIMARY KEY,
|
|
199
|
+
title VARCHAR(42),
|
|
200
|
+
long_text TEXT,
|
|
201
|
+
counter INTEGER,
|
|
202
|
+
owner_id BIGINT UNSIGNED,
|
|
203
|
+
FOREIGN KEY (owner_id) REFERENCES owners(id) ON DELETE CASCADE
|
|
204
|
+
);
|
|
205
|
+
`)
|
|
206
|
+
} else if (isSQLite) {
|
|
207
|
+
await db.query(sql`
|
|
208
|
+
CREATE TABLE owners (
|
|
209
|
+
id INTEGER PRIMARY KEY,
|
|
210
|
+
name VARCHAR(255)
|
|
211
|
+
);
|
|
212
|
+
`)
|
|
213
|
+
|
|
214
|
+
await db.query(sql`
|
|
215
|
+
CREATE TABLE posts (
|
|
216
|
+
id INTEGER PRIMARY KEY,
|
|
217
|
+
title VARCHAR(42),
|
|
218
|
+
long_text TEXT,
|
|
219
|
+
counter INTEGER,
|
|
220
|
+
owner_id BIGINT UNSIGNED,
|
|
221
|
+
FOREIGN KEY (owner_id) REFERENCES owners(id) ON DELETE CASCADE
|
|
222
|
+
);
|
|
223
|
+
`)
|
|
224
|
+
} else {
|
|
225
|
+
await db.query(sql`
|
|
226
|
+
CREATE TABLE owners (
|
|
227
|
+
id SERIAL PRIMARY KEY,
|
|
228
|
+
name VARCHAR(255)
|
|
229
|
+
);
|
|
230
|
+
CREATE TABLE posts (
|
|
231
|
+
id SERIAL PRIMARY KEY,
|
|
232
|
+
title VARCHAR(42),
|
|
233
|
+
long_text TEXT,
|
|
234
|
+
counter INTEGER,
|
|
235
|
+
owner_id INTEGER REFERENCES owners(id)
|
|
236
|
+
);`)
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
})
|
|
240
|
+
|
|
241
|
+
const owners = [{
|
|
242
|
+
name: 'Matteo'
|
|
243
|
+
}, {
|
|
244
|
+
name: 'Luca'
|
|
245
|
+
}]
|
|
246
|
+
|
|
247
|
+
const posts = [{
|
|
248
|
+
title: 'Dog',
|
|
249
|
+
longText: 'Foo',
|
|
250
|
+
counter: 10
|
|
251
|
+
}, {
|
|
252
|
+
title: 'Cat',
|
|
253
|
+
longText: 'Bar',
|
|
254
|
+
counter: 20
|
|
255
|
+
}, {
|
|
256
|
+
title: 'Mouse',
|
|
257
|
+
longText: 'Baz',
|
|
258
|
+
counter: 30
|
|
259
|
+
}, {
|
|
260
|
+
title: 'Duck',
|
|
261
|
+
longText: 'A duck tale',
|
|
262
|
+
counter: 40
|
|
263
|
+
}]
|
|
264
|
+
|
|
265
|
+
{
|
|
266
|
+
const res = await mapper.entities.owner.insert({
|
|
267
|
+
inputs: owners
|
|
268
|
+
})
|
|
269
|
+
const toAssign = [...posts]
|
|
270
|
+
for (const owner of res) {
|
|
271
|
+
toAssign.shift().ownerId = owner.id
|
|
272
|
+
toAssign.shift().ownerId = owner.id
|
|
273
|
+
}
|
|
274
|
+
await mapper.entities.post.insert({
|
|
275
|
+
inputs: posts
|
|
276
|
+
})
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
{
|
|
280
|
+
const owners = await mapper.entities.owner.find()
|
|
281
|
+
equal(owners.length, 2)
|
|
282
|
+
|
|
283
|
+
for (const owner of owners) {
|
|
284
|
+
owner.posts = await mapper.entities.post.find({ where: { ownerId: { eq: owner.id } }, fields: ['id', 'title', 'longText'] })
|
|
285
|
+
}
|
|
286
|
+
same(owners, [{
|
|
287
|
+
id: '1',
|
|
288
|
+
name: 'Matteo',
|
|
289
|
+
posts: [{
|
|
290
|
+
id: '1',
|
|
291
|
+
title: 'Dog',
|
|
292
|
+
longText: 'Foo',
|
|
293
|
+
ownerId: '1'
|
|
294
|
+
}, {
|
|
295
|
+
id: '2',
|
|
296
|
+
title: 'Cat',
|
|
297
|
+
longText: 'Bar',
|
|
298
|
+
ownerId: '1'
|
|
299
|
+
}]
|
|
300
|
+
}, {
|
|
301
|
+
id: '2',
|
|
302
|
+
name: 'Luca',
|
|
303
|
+
posts: [{
|
|
304
|
+
id: '3',
|
|
305
|
+
title: 'Mouse',
|
|
306
|
+
longText: 'Baz',
|
|
307
|
+
ownerId: '2'
|
|
308
|
+
}, {
|
|
309
|
+
id: '4',
|
|
310
|
+
title: 'Duck',
|
|
311
|
+
longText: 'A duck tale',
|
|
312
|
+
ownerId: '2'
|
|
313
|
+
}]
|
|
314
|
+
}])
|
|
315
|
+
}
|
|
316
|
+
})
|