@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.
Files changed (36) hide show
  1. package/.nyc_output/0208d41a-48bc-4675-a861-0475eb461a17.json +1 -0
  2. package/.nyc_output/588169a6-88e9-4949-af5a-631822d7dc42.json +1 -0
  3. package/.nyc_output/5bbdf331-cd01-4869-9d54-3d708610224a.json +1 -0
  4. package/.nyc_output/6d7c60ad-a404-4a1d-af86-8a334cff5f02.json +1 -0
  5. package/.nyc_output/8dae7e8c-5022-4a0c-a5b3-bf547f97961b.json +1 -0
  6. package/.nyc_output/f63bf7c5-4f58-4b46-a822-6f5ccf5a54a8.json +1 -0
  7. package/.nyc_output/processinfo/0208d41a-48bc-4675-a861-0475eb461a17.json +1 -0
  8. package/.nyc_output/processinfo/588169a6-88e9-4949-af5a-631822d7dc42.json +1 -0
  9. package/.nyc_output/processinfo/5bbdf331-cd01-4869-9d54-3d708610224a.json +1 -0
  10. package/.nyc_output/processinfo/6d7c60ad-a404-4a1d-af86-8a334cff5f02.json +1 -0
  11. package/.nyc_output/processinfo/8dae7e8c-5022-4a0c-a5b3-bf547f97961b.json +1 -0
  12. package/.nyc_output/processinfo/f63bf7c5-4f58-4b46-a822-6f5ccf5a54a8.json +1 -0
  13. package/.nyc_output/processinfo/index.json +1 -0
  14. package/.taprc +1 -0
  15. package/LICENSE +201 -0
  16. package/NOTICE +13 -0
  17. package/README.md +13 -0
  18. package/lib/entity.js +287 -0
  19. package/lib/queries/index.js +23 -0
  20. package/lib/queries/mariadb.js +11 -0
  21. package/lib/queries/mysql-shared.js +62 -0
  22. package/lib/queries/mysql.js +104 -0
  23. package/lib/queries/pg.js +79 -0
  24. package/lib/queries/shared.js +100 -0
  25. package/lib/queries/sqlite.js +169 -0
  26. package/lib/utils.js +14 -0
  27. package/mapper.d.ts +308 -0
  28. package/mapper.js +155 -0
  29. package/package.json +44 -0
  30. package/test/entity.test.js +344 -0
  31. package/test/helper.js +66 -0
  32. package/test/hooks.test.js +325 -0
  33. package/test/inserted_at_updated_at.test.js +132 -0
  34. package/test/mapper.test.js +288 -0
  35. package/test/types/mapper.test-d.ts +64 -0
  36. 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
+ }