@platformatic/sql-mapper 0.40.0 → 0.41.1
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/lib/entity.js +3 -1
- package/mapper.d.ts +22 -3
- package/mapper.js +32 -17
- package/package.json +3 -3
- package/test/create-connection.test.js +39 -0
- package/test/types/mapper.test-d.ts +17 -7
- package/test/updateMany.test.js +1 -0
- package/test/where.test.js +4 -1
package/lib/entity.js
CHANGED
|
@@ -223,6 +223,9 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relation
|
|
|
223
223
|
}
|
|
224
224
|
const value = where[key]
|
|
225
225
|
const field = inputToFieldMap[key]
|
|
226
|
+
if (!field) {
|
|
227
|
+
throw new Error(`Unknown field ${key}`)
|
|
228
|
+
}
|
|
226
229
|
for (const key of Object.keys(value)) {
|
|
227
230
|
const operator = whereMap[key]
|
|
228
231
|
/* istanbul ignore next */
|
|
@@ -231,7 +234,6 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relation
|
|
|
231
234
|
throw new Error(`Unsupported where clause ${JSON.stringify(where[key])}`)
|
|
232
235
|
}
|
|
233
236
|
const fieldWrap = fields[field]
|
|
234
|
-
|
|
235
237
|
/* istanbul ignore next */
|
|
236
238
|
if (fieldWrap.isArray) {
|
|
237
239
|
if (operator === 'ANY') {
|
package/mapper.d.ts
CHANGED
|
@@ -277,19 +277,38 @@ export interface EntityHooks<EntityFields = any> {
|
|
|
277
277
|
count?: EntityHook<Count>,
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
-
|
|
280
|
+
interface BasePoolOptions {
|
|
281
281
|
/**
|
|
282
282
|
* Database connection string.
|
|
283
283
|
*/
|
|
284
284
|
connectionString: string,
|
|
285
|
+
|
|
285
286
|
/**
|
|
286
|
-
*
|
|
287
|
+
* The maximum number of connections to create at once. Default is 10.
|
|
288
|
+
* @default 10
|
|
287
289
|
*/
|
|
288
|
-
|
|
290
|
+
poolSize?: number
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
export interface CreateConnectionPoolOptions extends BasePoolOptions {
|
|
294
|
+
/**
|
|
295
|
+
* A logger object (like [Pino](https://getpino.io))
|
|
296
|
+
*/
|
|
297
|
+
log: ILogger
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
export function createConnectionPool(options: CreateConnectionPoolOptions) : Promise<{ db: Database, sql: SQL }>
|
|
301
|
+
|
|
302
|
+
export interface SQLMapperPluginOptions extends BasePoolOptions {
|
|
289
303
|
/**
|
|
290
304
|
* A logger object (like [Pino](https://getpino.io))
|
|
291
305
|
*/
|
|
292
306
|
log?: ILogger,
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Set to true to enable auto timestamping for updated_at and inserted_at fields.
|
|
310
|
+
*/
|
|
311
|
+
autoTimestamp?: boolean,
|
|
293
312
|
/**
|
|
294
313
|
* Database table to ignore when mapping to entities.
|
|
295
314
|
*/
|
package/mapper.js
CHANGED
|
@@ -47,25 +47,17 @@ const defaultAutoTimestampFields = {
|
|
|
47
47
|
updatedAt: 'updated_at'
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
async function
|
|
51
|
-
if (typeof autoTimestamp === 'boolean' && autoTimestamp === true) {
|
|
52
|
-
autoTimestamp = defaultAutoTimestampFields
|
|
53
|
-
}
|
|
54
|
-
// TODO validate config using the schema
|
|
55
|
-
if (!connectionString) {
|
|
56
|
-
throw new Error('connectionString is required')
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
let queries
|
|
60
|
-
let sql
|
|
50
|
+
async function createConnectionPool ({ log, connectionString, poolSize }) {
|
|
61
51
|
let db
|
|
52
|
+
let sql
|
|
53
|
+
|
|
54
|
+
poolSize = poolSize || 10
|
|
62
55
|
|
|
63
56
|
/* istanbul ignore next */
|
|
64
57
|
if (connectionString.indexOf('postgres') === 0) {
|
|
65
58
|
const createConnectionPoolPg = require('@databases/pg')
|
|
66
59
|
db = await buildConnection(log, createConnectionPoolPg, connectionString, poolSize)
|
|
67
60
|
sql = createConnectionPoolPg.sql
|
|
68
|
-
queries = queriesFactory.pg
|
|
69
61
|
db.isPg = true
|
|
70
62
|
} else if (connectionString.indexOf('mysql') === 0) {
|
|
71
63
|
const createConnectionPoolMysql = require('@databases/mysql')
|
|
@@ -74,11 +66,8 @@ async function connect ({ connectionString, log, onDatabaseLoad, poolSize = 10,
|
|
|
74
66
|
const version = (await db.query(sql`SELECT VERSION()`))[0]['VERSION()']
|
|
75
67
|
db.version = version
|
|
76
68
|
db.isMariaDB = version.indexOf('maria') !== -1
|
|
77
|
-
if (db.isMariaDB) {
|
|
78
|
-
queries = queriesFactory.mariadb
|
|
79
|
-
} else {
|
|
69
|
+
if (!db.isMariaDB) {
|
|
80
70
|
db.isMySql = true
|
|
81
|
-
queries = queriesFactory.mysql
|
|
82
71
|
}
|
|
83
72
|
} else if (connectionString.indexOf('sqlite') === 0) {
|
|
84
73
|
const sqlite = require('@matteo.collina/sqlite-pool')
|
|
@@ -98,12 +87,37 @@ async function connect ({ connectionString, log, onDatabaseLoad, poolSize = 10,
|
|
|
98
87
|
}
|
|
99
88
|
})
|
|
100
89
|
sql = sqlite.sql
|
|
101
|
-
queries = queriesFactory.sqlite
|
|
102
90
|
db.isSQLite = true
|
|
103
91
|
} else {
|
|
104
92
|
throw new Error('You must specify either postgres, mysql or sqlite as protocols')
|
|
105
93
|
}
|
|
106
94
|
|
|
95
|
+
return { db, sql }
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function connect ({ connectionString, log, onDatabaseLoad, poolSize, ignore = {}, autoTimestamp = true, hooks = {}, schema, limit = {}, dbschema }) {
|
|
99
|
+
if (typeof autoTimestamp === 'boolean' && autoTimestamp === true) {
|
|
100
|
+
autoTimestamp = defaultAutoTimestampFields
|
|
101
|
+
}
|
|
102
|
+
// TODO validate config using the schema
|
|
103
|
+
if (!connectionString) {
|
|
104
|
+
throw new Error('connectionString is required')
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let queries
|
|
108
|
+
const { db, sql } = await createConnectionPool({ log, connectionString, poolSize })
|
|
109
|
+
|
|
110
|
+
/* istanbul ignore next */
|
|
111
|
+
if (db.isPg) {
|
|
112
|
+
queries = queriesFactory.pg
|
|
113
|
+
} else if (db.isMySql) {
|
|
114
|
+
queries = queriesFactory.mysql
|
|
115
|
+
} else if (db.isMariaDB) {
|
|
116
|
+
queries = queriesFactory.mariadb
|
|
117
|
+
} else if (db.isSQLite) {
|
|
118
|
+
queries = queriesFactory.sqlite
|
|
119
|
+
}
|
|
120
|
+
|
|
107
121
|
// Specify an empty array must be the same of specifying no schema
|
|
108
122
|
/* istanbul ignore next */ // Ignoring because this won't be fully covered by DB not supporting schemas (SQLite)
|
|
109
123
|
const schemaList = areSchemasSupported(db) && schema?.length > 0 ? schema : null
|
|
@@ -222,5 +236,6 @@ async function sqlMapper (app, opts) {
|
|
|
222
236
|
|
|
223
237
|
module.exports = fp(sqlMapper)
|
|
224
238
|
module.exports.connect = connect
|
|
239
|
+
module.exports.createConnectionPool = createConnectionPool
|
|
225
240
|
module.exports.plugin = module.exports
|
|
226
241
|
module.exports.utils = require('./lib/utils')
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/sql-mapper",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.1",
|
|
4
4
|
"description": "A data mapper utility for SQL databases",
|
|
5
5
|
"main": "mapper.js",
|
|
6
6
|
"types": "mapper.d.ts",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"snazzy": "^9.0.0",
|
|
20
20
|
"standard": "^17.1.0",
|
|
21
21
|
"tap": "^16.3.6",
|
|
22
|
-
"tsd": "^0.
|
|
22
|
+
"tsd": "^0.29.0"
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@databases/mysql": "^6.0.0",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"camelcase": "^6.3.0",
|
|
31
31
|
"fastify-plugin": "^4.5.0",
|
|
32
32
|
"inflected": "^2.1.0",
|
|
33
|
-
"@platformatic/types": "0.
|
|
33
|
+
"@platformatic/types": "0.41.1"
|
|
34
34
|
},
|
|
35
35
|
"tsd": {
|
|
36
36
|
"directory": "test/types"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
const { test } = require('tap')
|
|
4
|
+
const { clear, connInfo, isSQLite } = require('./helper')
|
|
5
|
+
const { createConnectionPool } = require('..')
|
|
6
|
+
const fakeLogger = {
|
|
7
|
+
trace: () => {},
|
|
8
|
+
error: () => {}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
test('createConnectionPool', async ({ equal, same, teardown, rejects }) => {
|
|
12
|
+
const { db, sql } = await createConnectionPool({
|
|
13
|
+
connectionString: connInfo.connectionString,
|
|
14
|
+
log: fakeLogger
|
|
15
|
+
})
|
|
16
|
+
await clear(db, sql)
|
|
17
|
+
teardown(() => db.dispose())
|
|
18
|
+
if (isSQLite) {
|
|
19
|
+
await db.query(sql`CREATE TABLE pages (
|
|
20
|
+
id INTEGER PRIMARY KEY,
|
|
21
|
+
the_title VARCHAR(42),
|
|
22
|
+
is_published BOOLEAN NOT NULL
|
|
23
|
+
);`)
|
|
24
|
+
} else {
|
|
25
|
+
await db.query(sql`CREATE TABLE pages (
|
|
26
|
+
id SERIAL PRIMARY KEY,
|
|
27
|
+
the_title VARCHAR(255) NOT NULL,
|
|
28
|
+
is_published BOOLEAN NOT NULL
|
|
29
|
+
);`)
|
|
30
|
+
}
|
|
31
|
+
await db.query(sql`INSERT INTO pages (the_title, is_published) VALUES ('foo', true)`)
|
|
32
|
+
|
|
33
|
+
const res = await db.query(sql`SELECT * FROM pages`)
|
|
34
|
+
same(res, [{
|
|
35
|
+
id: 1,
|
|
36
|
+
the_title: 'foo',
|
|
37
|
+
is_published: true
|
|
38
|
+
}])
|
|
39
|
+
})
|
|
@@ -10,9 +10,16 @@ import {
|
|
|
10
10
|
Database,
|
|
11
11
|
SQLMapperPluginInterface,
|
|
12
12
|
EntityHooks,
|
|
13
|
+
createConnectionPool
|
|
13
14
|
} from '../../mapper'
|
|
14
15
|
|
|
15
|
-
const
|
|
16
|
+
const log = {
|
|
17
|
+
trace() {},
|
|
18
|
+
error() {},
|
|
19
|
+
warn() {}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const pluginOptions: SQLMapperPluginInterface = await connect({ connectionString: '', log })
|
|
16
23
|
expectType<Database>(pluginOptions.db)
|
|
17
24
|
expectType<SQL>(pluginOptions.sql)
|
|
18
25
|
expectType<{ [entityName: string]: Entity }>(pluginOptions.entities)
|
|
@@ -49,17 +56,18 @@ const entityHooks: EntityHooks = {
|
|
|
49
56
|
async count(originalCount: typeof entity.count, ...options: Parameters<typeof entity.count>): ReturnType<typeof entity.count> { return 0 },
|
|
50
57
|
}
|
|
51
58
|
expectType<EntityHooks>(entityHooks)
|
|
52
|
-
expectType<SQLMapperPluginInterface>(await connect({ connectionString: '' }))
|
|
53
|
-
expectType<SQLMapperPluginInterface>(await connect({ connectionString: '', autoTimestamp: true }))
|
|
54
|
-
expectType<SQLMapperPluginInterface>(await connect({ connectionString: '', hooks: {} }))
|
|
59
|
+
expectType<SQLMapperPluginInterface>(await connect({ connectionString: '', log }))
|
|
60
|
+
expectType<SQLMapperPluginInterface>(await connect({ connectionString: '', autoTimestamp: true, log }))
|
|
61
|
+
expectType<SQLMapperPluginInterface>(await connect({ connectionString: '', hooks: {}, log }))
|
|
55
62
|
expectType<SQLMapperPluginInterface>(await connect({
|
|
56
63
|
connectionString: '', hooks: {
|
|
57
64
|
Page: entityHooks
|
|
58
|
-
}
|
|
65
|
+
},
|
|
66
|
+
log
|
|
59
67
|
}))
|
|
60
|
-
expectType<SQLMapperPluginInterface>(await connect({ connectionString: '', ignore: {} }))
|
|
68
|
+
expectType<SQLMapperPluginInterface>(await connect({ connectionString: '', ignore: {}, log }))
|
|
61
69
|
expectType<SQLMapperPluginInterface>(await connect({
|
|
62
|
-
connectionString: '', onDatabaseLoad(db: Database, sql: SQL) {
|
|
70
|
+
connectionString: '', log, onDatabaseLoad(db: Database, sql: SQL) {
|
|
63
71
|
expectType<(query: SQLQuery) => Promise<any[]>>(db.query)
|
|
64
72
|
expectType<() => Promise<void>>(db.dispose)
|
|
65
73
|
expectType<boolean | undefined>(pluginOptions.db.isMySql)
|
|
@@ -95,3 +103,5 @@ instance.register((instance) => {
|
|
|
95
103
|
})
|
|
96
104
|
|
|
97
105
|
expectType<(str: string) => string>(utils.toSingular)
|
|
106
|
+
|
|
107
|
+
expectType<Promise<{ db: Database, sql: SQL }>>(createConnectionPool({ connectionString: '', log }))
|
package/test/updateMany.test.js
CHANGED
package/test/where.test.js
CHANGED
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
const { test } = require('tap')
|
|
4
4
|
const { connect } = require('..')
|
|
5
5
|
const { clear, connInfo, isMysql, isSQLite } = require('./helper')
|
|
6
|
+
|
|
6
7
|
const fakeLogger = {
|
|
7
8
|
trace: () => {},
|
|
8
9
|
error: () => {}
|
|
9
10
|
}
|
|
10
11
|
|
|
11
|
-
test('list', async ({ pass, teardown, same,
|
|
12
|
+
test('list', async ({ pass, teardown, same, rejects }) => {
|
|
12
13
|
const mapper = await connect({
|
|
13
14
|
...connInfo,
|
|
14
15
|
log: fakeLogger,
|
|
@@ -60,6 +61,8 @@ test('list', async ({ pass, teardown, same, equal }) => {
|
|
|
60
61
|
inputs: posts
|
|
61
62
|
})
|
|
62
63
|
|
|
64
|
+
rejects(entity.find.bind(entity, { where: { invalidField: { eq: 'Dog' } } }), 'Unknown field invalidField')
|
|
65
|
+
|
|
63
66
|
same(await entity.find({ where: { title: { eq: 'Dog' } }, fields: ['id', 'title', 'longText'] }), [{
|
|
64
67
|
id: '1',
|
|
65
68
|
title: 'Dog',
|