@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,325 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('tap')
4
+ const { connect } = require('..')
5
+ const { clear, connInfo, isSQLite } = require('./helper')
6
+ const fakeLogger = {
7
+ trace: () => {},
8
+ error: () => {}
9
+ }
10
+
11
+ test('basic hooks', async ({ pass, teardown, same, equal, plan, fail }) => {
12
+ plan(14)
13
+ const mapper = await connect({
14
+ ...connInfo,
15
+ log: fakeLogger,
16
+ async onDatabaseLoad (db, sql) {
17
+ teardown(() => db.dispose())
18
+ pass('onDatabaseLoad called')
19
+
20
+ await clear(db, sql)
21
+
22
+ if (isSQLite) {
23
+ await db.query(sql`CREATE TABLE pages (
24
+ id INTEGER PRIMARY KEY,
25
+ title VARCHAR(42)
26
+ );`)
27
+ } else {
28
+ await db.query(sql`CREATE TABLE pages (
29
+ id SERIAL PRIMARY KEY,
30
+ title VARCHAR(42)
31
+ );`)
32
+ }
33
+ },
34
+ hooks: {
35
+ Page: {
36
+ noKey () {
37
+ fail('noKey should never be called')
38
+ },
39
+ async save (original, { input, ctx, fields }) {
40
+ pass('save called')
41
+
42
+ if (!input.id) {
43
+ same(input, {
44
+ title: 'Hello'
45
+ })
46
+
47
+ return original({
48
+ input: {
49
+ title: 'Hello from hook'
50
+ },
51
+ fields
52
+ })
53
+ } else {
54
+ same(input, {
55
+ id: 1,
56
+ title: 'Hello World'
57
+ })
58
+
59
+ return original({
60
+ input: {
61
+ id: 1,
62
+ title: 'Hello from hook 2'
63
+ },
64
+ fields
65
+ })
66
+ }
67
+ },
68
+ async find (original, args) {
69
+ pass('find called')
70
+
71
+ same(args.where, {
72
+ id: {
73
+ eq: '1'
74
+ }
75
+ })
76
+ args.where = {
77
+ id: {
78
+ eq: '2'
79
+ }
80
+ }
81
+ same(args.fields, ['id', 'title'])
82
+ return original(args)
83
+ },
84
+ async insert (original, args) {
85
+ pass('insert called')
86
+
87
+ same(args.inputs, [{
88
+ title: 'hello'
89
+ }, {
90
+ title: 'world'
91
+ }])
92
+ same(args.fields, ['id', 'title'])
93
+ return original(args)
94
+ }
95
+ }
96
+ }
97
+ })
98
+
99
+ const entity = mapper.entities.page
100
+
101
+ same(await entity.save({ input: { title: 'Hello' } }), {
102
+ id: 1,
103
+ title: 'Hello from hook'
104
+ })
105
+
106
+ same(await entity.find({ where: { id: { eq: 1 } }, fields: ['id', 'title'] }), [])
107
+
108
+ same(await entity.save({ input: { id: 1, title: 'Hello World' } }), {
109
+ id: 1,
110
+ title: 'Hello from hook 2'
111
+ })
112
+
113
+ await entity.insert({ inputs: [{ title: 'hello' }, { title: 'world' }], fields: ['id', 'title'] })
114
+ })
115
+
116
+ test('addEntityHooks', async ({ pass, teardown, same, equal, plan, fail, throws }) => {
117
+ plan(15)
118
+ const mapper = await connect({
119
+ ...connInfo,
120
+ log: fakeLogger,
121
+ async onDatabaseLoad (db, sql) {
122
+ teardown(() => db.dispose())
123
+ pass('onDatabaseLoad called')
124
+
125
+ await clear(db, sql)
126
+
127
+ if (isSQLite) {
128
+ await db.query(sql`CREATE TABLE pages (
129
+ id INTEGER PRIMARY KEY,
130
+ title VARCHAR(42)
131
+ );`)
132
+ } else {
133
+ await db.query(sql`CREATE TABLE pages (
134
+ id SERIAL PRIMARY KEY,
135
+ title VARCHAR(42)
136
+ );`)
137
+ }
138
+ }
139
+ })
140
+
141
+ throws(() => mapper.addEntityHooks('user', {}), 'Cannot find entity user')
142
+
143
+ mapper.addEntityHooks('page', {
144
+ noKey () {
145
+ fail('noKey should never be called')
146
+ },
147
+ async save (original, { input, ctx, fields }) {
148
+ pass('save called')
149
+
150
+ if (!input.id) {
151
+ same(input, {
152
+ title: 'Hello'
153
+ })
154
+
155
+ return original({
156
+ input: {
157
+ title: 'Hello from hook'
158
+ },
159
+ fields
160
+ })
161
+ } else {
162
+ same(input, {
163
+ id: 1,
164
+ title: 'Hello World'
165
+ })
166
+
167
+ return original({
168
+ input: {
169
+ id: 1,
170
+ title: 'Hello from hook 2'
171
+ },
172
+ fields
173
+ })
174
+ }
175
+ },
176
+ async find (original, args) {
177
+ pass('find called')
178
+
179
+ same(args.where, {
180
+ id: {
181
+ eq: '1'
182
+ }
183
+ })
184
+ args.where = {
185
+ id: {
186
+ eq: '2'
187
+ }
188
+ }
189
+ same(args.fields, ['id', 'title'])
190
+ return original(args)
191
+ },
192
+ async insert (original, args) {
193
+ pass('insert called')
194
+
195
+ same(args.inputs, [{
196
+ title: 'hello'
197
+ }, {
198
+ title: 'world'
199
+ }])
200
+ same(args.fields, ['id', 'title'])
201
+ return original(args)
202
+ }
203
+ })
204
+
205
+ const entity = mapper.entities.page
206
+
207
+ same(await entity.save({ input: { title: 'Hello' } }), {
208
+ id: 1,
209
+ title: 'Hello from hook'
210
+ })
211
+
212
+ same(await entity.find({ where: { id: { eq: 1 } }, fields: ['id', 'title'] }), [])
213
+
214
+ same(await entity.save({ input: { id: 1, title: 'Hello World' } }), {
215
+ id: 1,
216
+ title: 'Hello from hook 2'
217
+ })
218
+
219
+ await entity.insert({ inputs: [{ title: 'hello' }, { title: 'world' }], fields: ['id', 'title'] })
220
+ })
221
+
222
+ test('basic hooks with smaller cap name', async ({ pass, teardown, same, equal, plan, fail }) => {
223
+ plan(14)
224
+ const mapper = await connect({
225
+ ...connInfo,
226
+ log: fakeLogger,
227
+ async onDatabaseLoad (db, sql) {
228
+ teardown(() => db.dispose())
229
+ pass('onDatabaseLoad called')
230
+
231
+ await clear(db, sql)
232
+
233
+ if (isSQLite) {
234
+ await db.query(sql`CREATE TABLE pages (
235
+ id INTEGER PRIMARY KEY,
236
+ title VARCHAR(42)
237
+ );`)
238
+ } else {
239
+ await db.query(sql`CREATE TABLE pages (
240
+ id SERIAL PRIMARY KEY,
241
+ title VARCHAR(42)
242
+ );`)
243
+ }
244
+ },
245
+ hooks: {
246
+ page: {
247
+ noKey () {
248
+ fail('noKey should never be called')
249
+ },
250
+ async save (original, { input, ctx, fields }) {
251
+ pass('save called')
252
+
253
+ if (!input.id) {
254
+ same(input, {
255
+ title: 'Hello'
256
+ })
257
+
258
+ return original({
259
+ input: {
260
+ title: 'Hello from hook'
261
+ },
262
+ fields
263
+ })
264
+ } else {
265
+ same(input, {
266
+ id: 1,
267
+ title: 'Hello World'
268
+ })
269
+
270
+ return original({
271
+ input: {
272
+ id: 1,
273
+ title: 'Hello from hook 2'
274
+ },
275
+ fields
276
+ })
277
+ }
278
+ },
279
+ async find (original, args) {
280
+ pass('find called')
281
+
282
+ same(args.where, {
283
+ id: {
284
+ eq: '1'
285
+ }
286
+ })
287
+ args.where = {
288
+ id: {
289
+ eq: '2'
290
+ }
291
+ }
292
+ same(args.fields, ['id', 'title'])
293
+ return original(args)
294
+ },
295
+ async insert (original, args) {
296
+ pass('insert called')
297
+
298
+ same(args.inputs, [{
299
+ title: 'hello'
300
+ }, {
301
+ title: 'world'
302
+ }])
303
+ same(args.fields, ['id', 'title'])
304
+ return original(args)
305
+ }
306
+ }
307
+ }
308
+ })
309
+
310
+ const entity = mapper.entities.page
311
+
312
+ same(await entity.save({ input: { title: 'Hello' } }), {
313
+ id: 1,
314
+ title: 'Hello from hook'
315
+ })
316
+
317
+ same(await entity.find({ where: { id: { eq: 1 } }, fields: ['id', 'title'] }), [])
318
+
319
+ same(await entity.save({ input: { id: 1, title: 'Hello World' } }), {
320
+ id: 1,
321
+ title: 'Hello from hook 2'
322
+ })
323
+
324
+ await entity.insert({ inputs: [{ title: 'hello' }, { title: 'world' }], fields: ['id', 'title'] })
325
+ })
@@ -0,0 +1,132 @@
1
+ 'use strict'
2
+
3
+ const { test } = require('tap')
4
+ const { clear, connInfo, isSQLite, isMysql } = require('./helper')
5
+ const { setTimeout } = require('timers/promises')
6
+ const { connect } = require('..')
7
+ const fakeLogger = {
8
+ trace: () => {},
9
+ error: () => {}
10
+ }
11
+
12
+ async function createBasicPages (db, sql) {
13
+ if (isSQLite) {
14
+ await db.query(sql`CREATE TABLE pages (
15
+ id INTEGER PRIMARY KEY,
16
+ title VARCHAR(42),
17
+ inserted_at TIMESTAMP,
18
+ updated_at TIMESTAMP
19
+ );`)
20
+ } else if (isMysql) {
21
+ await db.query(sql`CREATE TABLE pages (
22
+ id SERIAL PRIMARY KEY,
23
+ title VARCHAR(42),
24
+ inserted_at TIMESTAMP NULL DEFAULT NULL,
25
+ updated_at TIMESTAMP NULL DEFAULT NULL
26
+ );`)
27
+ } else {
28
+ await db.query(sql`CREATE TABLE pages (
29
+ id SERIAL PRIMARY KEY,
30
+ title VARCHAR(42),
31
+ inserted_at TIMESTAMP,
32
+ updated_at TIMESTAMP
33
+ );`)
34
+ }
35
+ }
36
+
37
+ test('inserted_at updated_at happy path', async ({ pass, teardown, same, equal, not, comment, notSame }) => {
38
+ const mapper = await connect({
39
+ ...connInfo,
40
+ log: fakeLogger,
41
+ async onDatabaseLoad (db, sql) {
42
+ teardown(() => db.dispose())
43
+ pass('onDatabaseLoad called')
44
+
45
+ await clear(db, sql)
46
+ await createBasicPages(db, sql)
47
+ }
48
+ })
49
+
50
+ const entity = mapper.entities.page
51
+
52
+ equal(entity.fields.inserted_at.autoTimestamp, true)
53
+ equal(entity.fields.updated_at.autoTimestamp, true)
54
+
55
+ const original = await entity.save({
56
+ input: { title: 'Hello' }
57
+ })
58
+ not(original.insertedAt, null, 'insertedAt')
59
+ not(original.updatedAt, null, 'updatedAt')
60
+ comment(`insertedAt: ${original.insertedAt}`)
61
+ comment(`updatedAt: ${original.updatedAt}`)
62
+
63
+ {
64
+ const [data] = await entity.find({ where: { id: { eq: original.id } } })
65
+ same(data.insertedAt, original.insertedAt, 'insertedAt')
66
+ same(data.updatedAt, original.updatedAt, 'updatedAt')
67
+ comment(`insertedAt: ${data.insertedAt}`)
68
+ comment(`updatedAt: ${data.updatedAt}`)
69
+ }
70
+
71
+ await setTimeout(1000) // await 1s
72
+
73
+ let updated
74
+ {
75
+ const data = await entity.save({
76
+ input: { id: original.id, title: 'Hello World' }
77
+ })
78
+ same(data.insertedAt, original.insertedAt, 'insertedAt')
79
+ notSame(data.updatedAt, original.updatedAt, 'updatedAt')
80
+ updated = data
81
+ comment(`insertedAt: ${data.insertedAt}`)
82
+ comment(`updatedAt: ${data.updatedAt}`)
83
+ }
84
+
85
+ {
86
+ const [data] = await entity.find({ where: { id: { eq: original.id } } })
87
+ same(data.insertedAt, updated.insertedAt, 'insertedAt')
88
+ same(data.updatedAt, updated.updatedAt, 'updatedAt')
89
+ comment(`insertedAt: ${data.insertedAt}`)
90
+ comment(`updatedAt: ${data.updatedAt}`)
91
+ }
92
+ })
93
+
94
+ test('bulk insert adds inserted_at updated_at', async ({ pass, teardown, same, equal, not, comment }) => {
95
+ const mapper = await connect({
96
+ ...connInfo,
97
+ log: fakeLogger,
98
+ async onDatabaseLoad (db, sql) {
99
+ teardown(() => db.dispose())
100
+ pass('onDatabaseLoad called')
101
+
102
+ await clear(db, sql)
103
+ await createBasicPages(db, sql)
104
+ }
105
+ })
106
+
107
+ const entity = mapper.entities.page
108
+
109
+ {
110
+ const pages = await entity.insert({
111
+ inputs: [
112
+ { title: 'Page 1' },
113
+ { title: 'Page 2' },
114
+ { title: 'Page 3' }
115
+ ]
116
+ })
117
+ for (const page of pages) {
118
+ not(page.insertedAt, null, 'insertedAt')
119
+ not(page.updatedAt, null, 'updatedAt')
120
+ same(page.insertedAt, page.updatedAt, 'insertedAt === updatedAt')
121
+ }
122
+ }
123
+
124
+ {
125
+ const pages = await entity.find()
126
+ for (const page of pages) {
127
+ not(page.insertedAt, null, 'insertedAt')
128
+ not(page.updatedAt, null, 'updatedAt')
129
+ same(page.insertedAt, page.updatedAt, 'insertedAt === updatedAt')
130
+ }
131
+ }
132
+ })