@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,344 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('tap')
|
|
4
|
+
|
|
5
|
+
const { clear, connInfo, isSQLite, isMysql } = require('./helper')
|
|
6
|
+
const { connect } = require('..')
|
|
7
|
+
const fakeLogger = {
|
|
8
|
+
trace: () => {},
|
|
9
|
+
error: () => {}
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
test('entity fields', async ({ equal, not, same, teardown }) => {
|
|
13
|
+
async function onDatabaseLoad (db, sql) {
|
|
14
|
+
await clear(db, sql)
|
|
15
|
+
teardown(() => db.dispose())
|
|
16
|
+
|
|
17
|
+
if (isSQLite) {
|
|
18
|
+
await db.query(sql`CREATE TABLE pages (
|
|
19
|
+
id INTEGER PRIMARY KEY,
|
|
20
|
+
title VARCHAR(42)
|
|
21
|
+
);`)
|
|
22
|
+
} else {
|
|
23
|
+
await db.query(sql`CREATE TABLE pages (
|
|
24
|
+
id SERIAL PRIMARY KEY,
|
|
25
|
+
title VARCHAR(255) NOT NULL
|
|
26
|
+
);`)
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
const mapper = await connect({
|
|
30
|
+
connectionString: connInfo.connectionString,
|
|
31
|
+
log: fakeLogger,
|
|
32
|
+
onDatabaseLoad,
|
|
33
|
+
ignore: {},
|
|
34
|
+
hooks: {}
|
|
35
|
+
})
|
|
36
|
+
const pageEntity = mapper.entities.page
|
|
37
|
+
not(pageEntity, undefined)
|
|
38
|
+
equal(pageEntity.name, 'Page')
|
|
39
|
+
equal(pageEntity.singularName, 'page')
|
|
40
|
+
equal(pageEntity.pluralName, 'pages')
|
|
41
|
+
equal(pageEntity.primaryKey, 'id')
|
|
42
|
+
equal(pageEntity.table, 'pages')
|
|
43
|
+
equal(pageEntity.camelCasedFields.id.primaryKey, true)
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
test('entity API', async ({ equal, same, teardown, rejects }) => {
|
|
47
|
+
async function onDatabaseLoad (db, sql) {
|
|
48
|
+
await clear(db, sql)
|
|
49
|
+
teardown(() => db.dispose())
|
|
50
|
+
if (isSQLite) {
|
|
51
|
+
await db.query(sql`CREATE TABLE pages (
|
|
52
|
+
id INTEGER PRIMARY KEY,
|
|
53
|
+
the_title VARCHAR(42)
|
|
54
|
+
);`)
|
|
55
|
+
} else {
|
|
56
|
+
await db.query(sql`CREATE TABLE pages (
|
|
57
|
+
id SERIAL PRIMARY KEY,
|
|
58
|
+
the_title VARCHAR(255) NOT NULL
|
|
59
|
+
);`)
|
|
60
|
+
}
|
|
61
|
+
await db.query(sql`INSERT INTO pages (the_title) VALUES ('foo')`)
|
|
62
|
+
await db.query(sql`INSERT INTO pages (the_title) VALUES ('bar')`)
|
|
63
|
+
}
|
|
64
|
+
const mapper = await connect({
|
|
65
|
+
connectionString: connInfo.connectionString,
|
|
66
|
+
log: fakeLogger,
|
|
67
|
+
onDatabaseLoad,
|
|
68
|
+
ignore: {},
|
|
69
|
+
hooks: {}
|
|
70
|
+
})
|
|
71
|
+
const pageEntity = mapper.entities.page
|
|
72
|
+
// fixInput
|
|
73
|
+
const fixedInput = pageEntity.fixInput({ id: 42, theTitle: 'Fixme' })
|
|
74
|
+
same(fixedInput, { id: 42, the_title: 'Fixme' })
|
|
75
|
+
|
|
76
|
+
// fixOutput
|
|
77
|
+
const fixedOutput = pageEntity.fixOutput({
|
|
78
|
+
id: 42,
|
|
79
|
+
the_title: 'Fixme'
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
same(fixedOutput, { id: 42, theTitle: 'Fixme' })
|
|
83
|
+
|
|
84
|
+
// empty fixOutput
|
|
85
|
+
same(pageEntity.fixOutput(undefined), undefined)
|
|
86
|
+
|
|
87
|
+
// find
|
|
88
|
+
const findResult = await pageEntity.find({ fields: ['theTitle'] })
|
|
89
|
+
same(findResult, [{ theTitle: 'foo' }, { theTitle: 'bar' }])
|
|
90
|
+
|
|
91
|
+
// insert - single
|
|
92
|
+
const insertResult = await pageEntity.insert({
|
|
93
|
+
inputs: [{ theTitle: 'foobar' }],
|
|
94
|
+
fields: ['id', 'theTitle']
|
|
95
|
+
})
|
|
96
|
+
same(insertResult, [{ id: '3', theTitle: 'foobar' }])
|
|
97
|
+
|
|
98
|
+
// insert - multiple
|
|
99
|
+
const insertMultipleResult = await pageEntity.insert({
|
|
100
|
+
inputs: [{ theTitle: 'platformatic' }, { theTitle: 'foobar' }],
|
|
101
|
+
fields: ['id', 'theTitle']
|
|
102
|
+
})
|
|
103
|
+
same(insertMultipleResult, [{ id: '4', theTitle: 'platformatic' }, { id: '5', theTitle: 'foobar' }])
|
|
104
|
+
|
|
105
|
+
// save - new record
|
|
106
|
+
same(await pageEntity.save({
|
|
107
|
+
input: { theTitle: 'fourth page' },
|
|
108
|
+
fields: ['id', 'theTitle']
|
|
109
|
+
}), { id: 6, theTitle: 'fourth page' })
|
|
110
|
+
|
|
111
|
+
// save - update record
|
|
112
|
+
same(await pageEntity.save({
|
|
113
|
+
input: { id: 4, theTitle: 'foofoo' },
|
|
114
|
+
fields: ['id', 'theTitle']
|
|
115
|
+
}), { id: '4', theTitle: 'foofoo' })
|
|
116
|
+
|
|
117
|
+
// save - empty object
|
|
118
|
+
rejects(async () => {
|
|
119
|
+
await pageEntity.save({})
|
|
120
|
+
}, Error, 'Input not provided.')
|
|
121
|
+
|
|
122
|
+
rejects(async () => {
|
|
123
|
+
await pageEntity.save({ input: { fakeColumn: 'foobar' } })
|
|
124
|
+
})
|
|
125
|
+
// delete
|
|
126
|
+
same(await pageEntity.delete({
|
|
127
|
+
where: {
|
|
128
|
+
id: {
|
|
129
|
+
eq: 2
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
fields: ['id', 'theTitle']
|
|
133
|
+
}), [{ id: '2', theTitle: 'bar' }])
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
test('empty save', async ({ equal, same, teardown, rejects }) => {
|
|
137
|
+
async function onDatabaseLoad (db, sql) {
|
|
138
|
+
await clear(db, sql)
|
|
139
|
+
teardown(() => db.dispose())
|
|
140
|
+
if (isSQLite) {
|
|
141
|
+
await db.query(sql`CREATE TABLE pages (
|
|
142
|
+
id INTEGER PRIMARY KEY,
|
|
143
|
+
the_title VARCHAR(42)
|
|
144
|
+
);`)
|
|
145
|
+
} else {
|
|
146
|
+
await db.query(sql`CREATE TABLE pages (
|
|
147
|
+
id SERIAL PRIMARY KEY,
|
|
148
|
+
the_title VARCHAR(255)
|
|
149
|
+
);`)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
const mapper = await connect({
|
|
153
|
+
connectionString: connInfo.connectionString,
|
|
154
|
+
log: fakeLogger,
|
|
155
|
+
onDatabaseLoad,
|
|
156
|
+
ignore: {},
|
|
157
|
+
hooks: {}
|
|
158
|
+
})
|
|
159
|
+
|
|
160
|
+
const insertResult = await mapper.entities.page.save({
|
|
161
|
+
input: {},
|
|
162
|
+
fields: ['id', 'theTitle']
|
|
163
|
+
})
|
|
164
|
+
same(insertResult, { id: '1', theTitle: null })
|
|
165
|
+
})
|
|
166
|
+
|
|
167
|
+
test('[SQLite] - UUID', { skip: !isSQLite }, async ({ pass, teardown, same, equal }) => {
|
|
168
|
+
const mapper = await connect({
|
|
169
|
+
connectionString: connInfo.connectionString,
|
|
170
|
+
log: fakeLogger,
|
|
171
|
+
ignore: {},
|
|
172
|
+
hooks: {},
|
|
173
|
+
async onDatabaseLoad (db, sql) {
|
|
174
|
+
pass('onDatabaseLoad called')
|
|
175
|
+
|
|
176
|
+
await clear(db, sql)
|
|
177
|
+
|
|
178
|
+
await db.query(sql`
|
|
179
|
+
CREATE TABLE pages (
|
|
180
|
+
id uuid PRIMARY KEY,
|
|
181
|
+
title VARCHAR(42)
|
|
182
|
+
);`)
|
|
183
|
+
}
|
|
184
|
+
})
|
|
185
|
+
|
|
186
|
+
const pageEntity = mapper.entities.page
|
|
187
|
+
|
|
188
|
+
let id
|
|
189
|
+
{
|
|
190
|
+
const res = await pageEntity.save({ input: { title: 'Hello' } })
|
|
191
|
+
id = res.id
|
|
192
|
+
same(res, {
|
|
193
|
+
id,
|
|
194
|
+
title: 'Hello'
|
|
195
|
+
})
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
{
|
|
199
|
+
const res = await pageEntity.find({ where: { id: { eq: id } } })
|
|
200
|
+
same(res, [{
|
|
201
|
+
id,
|
|
202
|
+
title: 'Hello'
|
|
203
|
+
}])
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
{
|
|
207
|
+
const res = await pageEntity.save({ input: { id, title: 'Hello World' } })
|
|
208
|
+
same(res, {
|
|
209
|
+
id,
|
|
210
|
+
title: 'Hello World'
|
|
211
|
+
})
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
test('[sqlite] throws if PK is not INTEGER', { skip: !isSQLite }, async ({ fail, equal, teardown, rejects }) => {
|
|
216
|
+
async function onDatabaseLoad (db, sql) {
|
|
217
|
+
await clear(db, sql)
|
|
218
|
+
await db.query(sql`CREATE TABLE pages (
|
|
219
|
+
id int PRIMARY KEY,
|
|
220
|
+
title varchar(255) NOT NULL,
|
|
221
|
+
content text NOT NULL
|
|
222
|
+
);`)
|
|
223
|
+
}
|
|
224
|
+
try {
|
|
225
|
+
await connect({
|
|
226
|
+
connectionString: connInfo.connectionString,
|
|
227
|
+
log: fakeLogger,
|
|
228
|
+
onDatabaseLoad,
|
|
229
|
+
ignore: {},
|
|
230
|
+
hooks: {}
|
|
231
|
+
})
|
|
232
|
+
fail()
|
|
233
|
+
} catch (err) {
|
|
234
|
+
equal(err.message, 'Invalid Primary Key type. Expected "integer", found "int"')
|
|
235
|
+
}
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
test('mixing snake and camel case', async ({ pass, teardown, same, equal }) => {
|
|
239
|
+
async function onDatabaseLoad (db, sql) {
|
|
240
|
+
await clear(db, sql)
|
|
241
|
+
teardown(() => db.dispose())
|
|
242
|
+
|
|
243
|
+
if (isMysql) {
|
|
244
|
+
await db.query(sql`
|
|
245
|
+
CREATE TABLE categories (
|
|
246
|
+
id SERIAL PRIMARY KEY,
|
|
247
|
+
name VARCHAR(255)
|
|
248
|
+
);
|
|
249
|
+
CREATE TABLE pages (
|
|
250
|
+
id SERIAL PRIMARY KEY,
|
|
251
|
+
title VARCHAR(255),
|
|
252
|
+
body_content TEXT,
|
|
253
|
+
category_id BIGINT UNSIGNED,
|
|
254
|
+
FOREIGN KEY (category_id) REFERENCES categories(id) ON DELETE CASCADE
|
|
255
|
+
);
|
|
256
|
+
`)
|
|
257
|
+
} else if (isSQLite) {
|
|
258
|
+
await db.query(sql`
|
|
259
|
+
CREATE TABLE "categories" (
|
|
260
|
+
"id" INTEGER PRIMARY KEY,
|
|
261
|
+
"name" TEXT NOT NULL
|
|
262
|
+
);
|
|
263
|
+
`)
|
|
264
|
+
await db.query(sql`
|
|
265
|
+
CREATE TABLE "pages" (
|
|
266
|
+
"id" INTEGER PRIMARY KEY,
|
|
267
|
+
"title" TEXT NOT NULL,
|
|
268
|
+
"body_content" TEXT
|
|
269
|
+
);
|
|
270
|
+
`)
|
|
271
|
+
await db.query(sql`
|
|
272
|
+
ALTER TABLE "pages" ADD COLUMN "category_id" REFERENCES "categories"("id");
|
|
273
|
+
`)
|
|
274
|
+
} else {
|
|
275
|
+
await db.query(sql`
|
|
276
|
+
CREATE TABLE categories (
|
|
277
|
+
id SERIAL PRIMARY KEY,
|
|
278
|
+
name varchar(255) NOT NULL
|
|
279
|
+
);
|
|
280
|
+
|
|
281
|
+
CREATE TABLE pages (
|
|
282
|
+
id SERIAL PRIMARY KEY,
|
|
283
|
+
title varchar(255) NOT NULL,
|
|
284
|
+
body_content text,
|
|
285
|
+
category_id int NOT NULL REFERENCES categories(id)
|
|
286
|
+
);
|
|
287
|
+
`)
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const mapper = await connect({
|
|
292
|
+
connectionString: connInfo.connectionString,
|
|
293
|
+
log: fakeLogger,
|
|
294
|
+
onDatabaseLoad,
|
|
295
|
+
ignore: {},
|
|
296
|
+
hooks: {}
|
|
297
|
+
})
|
|
298
|
+
|
|
299
|
+
const pageEntity = mapper.entities.page
|
|
300
|
+
const categoryEntity = mapper.entities.category
|
|
301
|
+
|
|
302
|
+
const [newCategory] = await categoryEntity.insert({
|
|
303
|
+
fields: ['id', 'name'],
|
|
304
|
+
inputs: [{ name: 'fiction' }]
|
|
305
|
+
})
|
|
306
|
+
|
|
307
|
+
{
|
|
308
|
+
const res = await pageEntity.insert({
|
|
309
|
+
fields: ['id', 'title'],
|
|
310
|
+
inputs: [
|
|
311
|
+
{
|
|
312
|
+
title: 'A fiction', bodyContent: 'This is our first fiction', categoryId: newCategory.id
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
title: 'A fiction', body_content: 'This is our first fiction', category_id: newCategory.id
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
]
|
|
319
|
+
})
|
|
320
|
+
same(res, [{
|
|
321
|
+
id: '1',
|
|
322
|
+
title: 'A fiction',
|
|
323
|
+
categoryId: newCategory.id
|
|
324
|
+
}, {
|
|
325
|
+
id: '2',
|
|
326
|
+
title: 'A fiction',
|
|
327
|
+
categoryId: newCategory.id
|
|
328
|
+
}])
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
{
|
|
332
|
+
const res = await pageEntity.save({
|
|
333
|
+
fields: ['id', 'title'],
|
|
334
|
+
input: {
|
|
335
|
+
title: 'A fiction', body_content: 'This is our first fiction', category_id: newCategory.id
|
|
336
|
+
}
|
|
337
|
+
})
|
|
338
|
+
same(res, {
|
|
339
|
+
id: '3',
|
|
340
|
+
title: 'A fiction',
|
|
341
|
+
categoryId: newCategory.id
|
|
342
|
+
})
|
|
343
|
+
}
|
|
344
|
+
})
|
package/test/helper.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
// Needed to work with dates & postgresql
|
|
4
|
+
// See https://node-postgres.com/features/types/
|
|
5
|
+
process.env.TZ = 'UTC'
|
|
6
|
+
|
|
7
|
+
const connInfo = {}
|
|
8
|
+
|
|
9
|
+
if (!process.env.DB || process.env.DB === 'postgresql') {
|
|
10
|
+
connInfo.connectionString = 'postgres://postgres:postgres@127.0.0.1/postgres'
|
|
11
|
+
module.exports.isPg = true
|
|
12
|
+
} else if (process.env.DB === 'mariadb') {
|
|
13
|
+
connInfo.connectionString = 'mysql://root@127.0.0.1:3307/graph'
|
|
14
|
+
connInfo.poolSize = 10
|
|
15
|
+
module.exports.isMysql = true
|
|
16
|
+
} else if (process.env.DB === 'mysql') {
|
|
17
|
+
connInfo.connectionString = 'mysql://root@127.0.0.1/graph'
|
|
18
|
+
connInfo.poolSize = 10
|
|
19
|
+
module.exports.isMysql = true
|
|
20
|
+
} else if (process.env.DB === 'mysql8') {
|
|
21
|
+
connInfo.connectionString = 'mysql://root@127.0.0.1:3308/graph'
|
|
22
|
+
connInfo.poolSize = 10
|
|
23
|
+
module.exports.isMysql = true
|
|
24
|
+
} else if (process.env.DB === 'sqlite') {
|
|
25
|
+
connInfo.connectionString = 'sqlite://:memory:'
|
|
26
|
+
module.exports.isSQLite = true
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
module.exports.connInfo = connInfo
|
|
30
|
+
|
|
31
|
+
module.exports.clear = async function (db, sql) {
|
|
32
|
+
try {
|
|
33
|
+
await db.query(sql`DROP TABLE pages`)
|
|
34
|
+
} catch (err) {
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
await db.query(sql`DROP TABLE categories`)
|
|
39
|
+
} catch {
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
await db.query(sql`DROP TABLE posts`)
|
|
44
|
+
} catch {
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
await db.query(sql`DROP TABLE simple_types`)
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
try {
|
|
53
|
+
await db.query(sql`DROP TABLE owners`)
|
|
54
|
+
} catch {
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
await db.query(sql`DROP TABLE users`)
|
|
59
|
+
} catch {
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
await db.query(sql`DROP TABLE versions`)
|
|
64
|
+
} catch {
|
|
65
|
+
}
|
|
66
|
+
}
|