@platformatic/sql-mapper 2.0.0-alpha.2 → 2.0.0-alpha.3
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/eslint.config.js +3 -0
- package/lib/cache.js +3 -3
- package/lib/connection-info.js +37 -0
- package/lib/entity.js +24 -12
- package/lib/errors.js +1 -1
- package/lib/queries/index.js +4 -4
- package/lib/queries/mariadb.js +1 -1
- package/lib/queries/mysql-shared.js +1 -1
- package/lib/queries/mysql.js +1 -1
- package/lib/queries/shared.js +1 -1
- package/lib/queries/sqlite.js +3 -3
- package/lib/telemetry.js +92 -0
- package/lib/utils.js +1 -1
- package/mapper.d.ts +6 -8
- package/mapper.js +26 -14
- package/package.json +11 -8
package/eslint.config.js
ADDED
package/lib/cache.js
CHANGED
|
@@ -21,10 +21,10 @@ function setupCache (res, opts) {
|
|
|
21
21
|
serialize (query) {
|
|
22
22
|
const serialized = {
|
|
23
23
|
...query,
|
|
24
|
-
ctx: undefined
|
|
24
|
+
ctx: undefined,
|
|
25
25
|
}
|
|
26
26
|
return serialized
|
|
27
|
-
}
|
|
27
|
+
},
|
|
28
28
|
}, async function (query) {
|
|
29
29
|
const res = await originalFn.call(entity, query)
|
|
30
30
|
return res
|
|
@@ -36,7 +36,7 @@ function setupCache (res, opts) {
|
|
|
36
36
|
return originalFn(query)
|
|
37
37
|
}
|
|
38
38
|
return cache[fnName](query)
|
|
39
|
-
}
|
|
39
|
+
},
|
|
40
40
|
})
|
|
41
41
|
}
|
|
42
42
|
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// The most general way to get the connection info is through the driver.
|
|
2
|
+
// In this way, we don't need to do any assumptions about the connection string
|
|
3
|
+
// (with the exception of SQLite)
|
|
4
|
+
const getConnectionInfo = async (db, connectionString) => {
|
|
5
|
+
let database, host, port, user
|
|
6
|
+
if (db.isPg) {
|
|
7
|
+
const driver = await db._pool.getConnection()
|
|
8
|
+
const connectionParameters = driver.connection.client.connectionParameters
|
|
9
|
+
host = connectionParameters.host
|
|
10
|
+
port = connectionParameters.port
|
|
11
|
+
database = connectionParameters.database
|
|
12
|
+
user = connectionParameters.user
|
|
13
|
+
driver.release()
|
|
14
|
+
} else if (db.isMySql || db.isMariaDB || db.isMySql8) {
|
|
15
|
+
const driver = await db._pool.getConnection()
|
|
16
|
+
const connectionParameters = driver.connection.client.config
|
|
17
|
+
database = connectionParameters.database
|
|
18
|
+
host = connectionParameters.host
|
|
19
|
+
port = connectionParameters.port
|
|
20
|
+
user = connectionParameters.user
|
|
21
|
+
driver.release()
|
|
22
|
+
} else if (db.isSQLite) {
|
|
23
|
+
database = connectionString?.split('sqlite://')[1]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return {
|
|
27
|
+
database,
|
|
28
|
+
host,
|
|
29
|
+
port,
|
|
30
|
+
user,
|
|
31
|
+
isPg: db.isPg,
|
|
32
|
+
isMySql: db.isMySql,
|
|
33
|
+
isSQLite: db.isSQLite,
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = { getConnectionInfo }
|
package/lib/entity.js
CHANGED
|
@@ -6,11 +6,12 @@ const {
|
|
|
6
6
|
toUpperFirst,
|
|
7
7
|
toLowerFirst,
|
|
8
8
|
tableName,
|
|
9
|
-
sanitizeLimit
|
|
9
|
+
sanitizeLimit,
|
|
10
10
|
} = require('./utils')
|
|
11
11
|
const { singularize } = require('inflected')
|
|
12
12
|
const { findNearestString } = require('@platformatic/utils')
|
|
13
13
|
const errors = require('./errors')
|
|
14
|
+
const { wrapDB } = require('./telemetry')
|
|
14
15
|
|
|
15
16
|
function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relations, queries, autoTimestamp, schema, useSchemaInName, limitConfig, columns, constraintsList) {
|
|
16
17
|
/* istanbul ignore next */ // Ignoring because this won't be fully covered by DB not supporting schemas (SQLite)
|
|
@@ -19,6 +20,17 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relation
|
|
|
19
20
|
const pluralName = camelcase(useSchemaInName ? camelcase(`${schema} ${table}`) : table)
|
|
20
21
|
const singularName = camelcase(entityName)
|
|
21
22
|
|
|
23
|
+
// If the db is in the opts, uses it, otherwise uses the defaultDb
|
|
24
|
+
// if telemetry is enabled, wraps the db with telemetry
|
|
25
|
+
const getDB = (opts) => {
|
|
26
|
+
let db = opts?.tx || defaultDb
|
|
27
|
+
if (opts?.ctx?.app?.openTelemetry && opts?.ctx?.reply?.request) {
|
|
28
|
+
const req = opts.ctx.reply.request
|
|
29
|
+
db = wrapDB(opts.ctx.app, db, req)
|
|
30
|
+
}
|
|
31
|
+
return db
|
|
32
|
+
}
|
|
33
|
+
|
|
22
34
|
// Fields remapping
|
|
23
35
|
const fieldMapToRetrieve = {}
|
|
24
36
|
const inputToFieldMap = {}
|
|
@@ -34,7 +46,7 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relation
|
|
|
34
46
|
const primaryKeysTypes = Array.from(primaryKeys).map((key) => {
|
|
35
47
|
return {
|
|
36
48
|
key,
|
|
37
|
-
sqlType: fields[key].sqlType
|
|
49
|
+
sqlType: fields[key].sqlType,
|
|
38
50
|
}
|
|
39
51
|
})
|
|
40
52
|
|
|
@@ -72,7 +84,7 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relation
|
|
|
72
84
|
}
|
|
73
85
|
|
|
74
86
|
async function save (args) {
|
|
75
|
-
const db = args
|
|
87
|
+
const db = getDB(args)
|
|
76
88
|
if (args.input === undefined) {
|
|
77
89
|
throw new errors.InputNotProvidedError()
|
|
78
90
|
}
|
|
@@ -114,7 +126,7 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relation
|
|
|
114
126
|
}
|
|
115
127
|
|
|
116
128
|
async function insert (args) {
|
|
117
|
-
const db = args
|
|
129
|
+
const db = getDB(args)
|
|
118
130
|
const fieldsToRetrieve = computeFields(args.fields).map((f) => sql.ident(f))
|
|
119
131
|
const inputs = args.inputs
|
|
120
132
|
// This else is skipped on MySQL because of https://github.com/ForbesLindesay/atdatabases/issues/221
|
|
@@ -155,7 +167,7 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relation
|
|
|
155
167
|
if (args.where === undefined || Object.keys(args.where).length === 0) {
|
|
156
168
|
throw new errors.MissingWhereClauseError()
|
|
157
169
|
}
|
|
158
|
-
const db = args
|
|
170
|
+
const db = getDB(args)
|
|
159
171
|
const fieldsToRetrieve = computeFields(args.fields).map((f) => sql.ident(f))
|
|
160
172
|
const input = fixInput(args.input)
|
|
161
173
|
if (autoTimestamp && fields[autoTimestamp.updatedAt]) {
|
|
@@ -206,7 +218,7 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relation
|
|
|
206
218
|
all: 'ALL',
|
|
207
219
|
contains: '@>',
|
|
208
220
|
contained: '<@',
|
|
209
|
-
overlaps: '&&'
|
|
221
|
+
overlaps: '&&',
|
|
210
222
|
}
|
|
211
223
|
|
|
212
224
|
function computeCriteria (opts) {
|
|
@@ -291,7 +303,7 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relation
|
|
|
291
303
|
}
|
|
292
304
|
|
|
293
305
|
async function find (opts = {}) {
|
|
294
|
-
const db = opts
|
|
306
|
+
const db = getDB(opts)
|
|
295
307
|
const fieldsToRetrieve = computeFields(opts.fields).map((f) => sql.ident(f))
|
|
296
308
|
const criteria = computeCriteria(opts)
|
|
297
309
|
|
|
@@ -326,7 +338,7 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relation
|
|
|
326
338
|
}
|
|
327
339
|
|
|
328
340
|
async function count (opts = {}) {
|
|
329
|
-
const db = opts
|
|
341
|
+
const db = getDB(opts)
|
|
330
342
|
let totalCountQuery = null
|
|
331
343
|
totalCountQuery = sql`
|
|
332
344
|
SELECT COUNT(*) AS total
|
|
@@ -341,7 +353,7 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relation
|
|
|
341
353
|
}
|
|
342
354
|
|
|
343
355
|
async function _delete (opts) {
|
|
344
|
-
const db = opts
|
|
356
|
+
const db = getDB(opts)
|
|
345
357
|
const fieldsToRetrieve = computeFields(opts.fields).map((f) => sql.ident(f))
|
|
346
358
|
const criteria = computeCriteria(opts)
|
|
347
359
|
const res = await queries.deleteAll(db, sql, table, schema, criteria, fieldsToRetrieve)
|
|
@@ -364,7 +376,7 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relation
|
|
|
364
376
|
insert,
|
|
365
377
|
save,
|
|
366
378
|
delete: _delete,
|
|
367
|
-
updateMany
|
|
379
|
+
updateMany,
|
|
368
380
|
}
|
|
369
381
|
}
|
|
370
382
|
|
|
@@ -383,7 +395,7 @@ function buildEntity (db, sql, log, table, queries, autoTimestamp, schema, useSc
|
|
|
383
395
|
acc[column.column_name] = {
|
|
384
396
|
sqlType: column.udt_name,
|
|
385
397
|
isNullable: column.is_nullable === 'YES',
|
|
386
|
-
isArray: column.isArray
|
|
398
|
+
isArray: column.isArray,
|
|
387
399
|
}
|
|
388
400
|
|
|
389
401
|
// To get enum values in mysql and mariadb
|
|
@@ -437,7 +449,7 @@ function buildEntity (db, sql, log, table, queries, autoTimestamp, schema, useSc
|
|
|
437
449
|
if (!field) {
|
|
438
450
|
// This should never happen
|
|
439
451
|
log.warn({
|
|
440
|
-
constraint
|
|
452
|
+
constraint,
|
|
441
453
|
}, `No field for ${constraint.column_name}`)
|
|
442
454
|
continue
|
|
443
455
|
}
|
package/lib/errors.js
CHANGED
|
@@ -20,5 +20,5 @@ module.exports = {
|
|
|
20
20
|
ParamLimitMustBeNotNegativeError: createError(`${ERROR_PREFIX}_PARAM_LIMIT_MUST_BE_NOT_NEGATIVE`, 'Param limit=%s not allowed. It must be a not negative value.'),
|
|
21
21
|
MissingValueForPrimaryKeyError: createError(`${ERROR_PREFIX}_MISSING_VALUE_FOR_PRIMARY_KEY`, 'Missing value for primary key %s'),
|
|
22
22
|
MissingWhereClauseError: createError(`${ERROR_PREFIX}_MISSING_WHERE_CLAUSE`, 'Missing where clause', 400),
|
|
23
|
-
SQLiteOnlySupportsAutoIncrementOnOneColumnError: createError(`${ERROR_PREFIX}_SQLITE_ONLY_SUPPORTS_AUTO_INCREMENT_ON_ONE_COLUMN`, 'SQLite only supports autoIncrement on one column')
|
|
23
|
+
SQLiteOnlySupportsAutoIncrementOnOneColumnError: createError(`${ERROR_PREFIX}_SQLITE_ONLY_SUPPORTS_AUTO_INCREMENT_ON_ONE_COLUMN`, 'SQLite only supports autoIncrement on one column'),
|
|
24
24
|
}
|
package/lib/queries/index.js
CHANGED
|
@@ -5,19 +5,19 @@
|
|
|
5
5
|
const obj = {}
|
|
6
6
|
|
|
7
7
|
Object.defineProperty(obj, 'pg', {
|
|
8
|
-
get: () => require('./pg')
|
|
8
|
+
get: () => require('./pg'),
|
|
9
9
|
})
|
|
10
10
|
|
|
11
11
|
Object.defineProperty(obj, 'mysql', {
|
|
12
|
-
get: () => require('./mysql')
|
|
12
|
+
get: () => require('./mysql'),
|
|
13
13
|
})
|
|
14
14
|
|
|
15
15
|
Object.defineProperty(obj, 'mariadb', {
|
|
16
|
-
get: () => require('./mariadb')
|
|
16
|
+
get: () => require('./mariadb'),
|
|
17
17
|
})
|
|
18
18
|
|
|
19
19
|
Object.defineProperty(obj, 'sqlite', {
|
|
20
|
-
get: () => require('./sqlite')
|
|
20
|
+
get: () => require('./sqlite'),
|
|
21
21
|
})
|
|
22
22
|
|
|
23
23
|
module.exports = obj
|
package/lib/queries/mariadb.js
CHANGED
package/lib/queries/mysql.js
CHANGED
package/lib/queries/shared.js
CHANGED
package/lib/queries/sqlite.js
CHANGED
|
@@ -54,7 +54,7 @@ async function listConstraints (db, sql, table) {
|
|
|
54
54
|
for (const pk of pks) {
|
|
55
55
|
constraints.push({
|
|
56
56
|
column_name: pk.name,
|
|
57
|
-
constraint_type: 'PRIMARY KEY'
|
|
57
|
+
constraint_type: 'PRIMARY KEY',
|
|
58
58
|
})
|
|
59
59
|
}
|
|
60
60
|
|
|
@@ -69,7 +69,7 @@ async function listConstraints (db, sql, table) {
|
|
|
69
69
|
if (index.unique === 1) {
|
|
70
70
|
constraints.push({
|
|
71
71
|
column_name: index.name,
|
|
72
|
-
constraint_type: 'UNIQUE'
|
|
72
|
+
constraint_type: 'UNIQUE',
|
|
73
73
|
})
|
|
74
74
|
}
|
|
75
75
|
}
|
|
@@ -85,7 +85,7 @@ async function listConstraints (db, sql, table) {
|
|
|
85
85
|
column_name: foreignKey.from,
|
|
86
86
|
constraint_type: 'FOREIGN KEY',
|
|
87
87
|
foreign_table_name: foreignKey.table,
|
|
88
|
-
foreign_column_name: foreignKey.to
|
|
88
|
+
foreign_column_name: foreignKey.to,
|
|
89
89
|
})
|
|
90
90
|
}
|
|
91
91
|
return constraints
|
package/lib/telemetry.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
function wrapQuery (app, db, request) {
|
|
4
|
+
const { startSpan, endSpan, SpanKind } = app.openTelemetry
|
|
5
|
+
async function wrappedQuery () {
|
|
6
|
+
const query = arguments[0]
|
|
7
|
+
const connectionInfo = db.connectionInfo
|
|
8
|
+
|
|
9
|
+
let namePrefix, dbSystem
|
|
10
|
+
if (connectionInfo.isPg) {
|
|
11
|
+
namePrefix = 'pg.query:'
|
|
12
|
+
dbSystem = 'postgresql'
|
|
13
|
+
} else if (connectionInfo.isMySql) {
|
|
14
|
+
namePrefix = 'mysql.query:'
|
|
15
|
+
dbSystem = 'mysql'
|
|
16
|
+
} else if (connectionInfo.isSQLite) {
|
|
17
|
+
namePrefix = 'sqlite.query:'
|
|
18
|
+
dbSystem = 'sqlite'
|
|
19
|
+
} else {
|
|
20
|
+
namePrefix = 'db.query:'
|
|
21
|
+
dbSystem = 'unknown'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const format = {
|
|
25
|
+
escapeIdentifier: (str) => (str),
|
|
26
|
+
formatValue: (value, index) => ({ placeholder: `$${index + 1}`, value }),
|
|
27
|
+
}
|
|
28
|
+
const { text: queryText } = query.format(format)
|
|
29
|
+
// We get the name form the first 20 characters of the query
|
|
30
|
+
// The spane name is not really important, all the info (included the full query) are in the attributes)
|
|
31
|
+
const name = queryText.substring(0, 20)
|
|
32
|
+
const spanName = `${namePrefix}${name.replace(/\n|\r/g, ' ')}`
|
|
33
|
+
|
|
34
|
+
const ctx = request.span?.context
|
|
35
|
+
|
|
36
|
+
const { database, host, port, user } = connectionInfo
|
|
37
|
+
const telemetryAttributes = {
|
|
38
|
+
'db.statement': queryText,
|
|
39
|
+
'db.system': dbSystem,
|
|
40
|
+
'db.name': database,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!db.isSQLite) {
|
|
44
|
+
// we dont' set the connection string as property because it can contain the password
|
|
45
|
+
telemetryAttributes['db.user'] = user
|
|
46
|
+
telemetryAttributes['net.peer.name'] = host
|
|
47
|
+
telemetryAttributes['net.peer.port'] = port
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let span
|
|
51
|
+
try {
|
|
52
|
+
span = startSpan(spanName, ctx, telemetryAttributes, SpanKind.CLIENT)
|
|
53
|
+
const result = await db.query.apply(db, arguments)
|
|
54
|
+
endSpan(span)
|
|
55
|
+
return result
|
|
56
|
+
} catch (err) /* istanbul ignore next */ {
|
|
57
|
+
endSpan(span, err)
|
|
58
|
+
throw err
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return wrappedQuery
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function wrapDB (app, db, request) {
|
|
65
|
+
const newDb = Object.create(db)
|
|
66
|
+
const connectionInfo = db.connectionInfo
|
|
67
|
+
newDb.query = wrapQuery(app, db, request)
|
|
68
|
+
newDb.tx = function wrappedTx (func) {
|
|
69
|
+
return db.tx((db) => {
|
|
70
|
+
db.connectionInfo = connectionInfo
|
|
71
|
+
const _newDb = Object.create(db)
|
|
72
|
+
_newDb.query = wrapQuery(app, db, request)
|
|
73
|
+
return func(_newDb)
|
|
74
|
+
})
|
|
75
|
+
}
|
|
76
|
+
return newDb
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const setupTelemetry = app => {
|
|
80
|
+
// Decorate the request with the wrapped DB.
|
|
81
|
+
// We need that for the queries written directly using `db`
|
|
82
|
+
if (app.platformatic.db) {
|
|
83
|
+
app.decorateRequest('getDB', function getDB () {
|
|
84
|
+
return wrapDB(app, app.platformatic.db, this)
|
|
85
|
+
})
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
module.exports = {
|
|
90
|
+
setupTelemetry,
|
|
91
|
+
wrapDB,
|
|
92
|
+
}
|
package/lib/utils.js
CHANGED
package/mapper.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { FastifyPluginAsync, FastifyInstance, FastifyReply
|
|
1
|
+
import { FastifyPluginAsync, FastifyInstance, FastifyReply } from 'fastify'
|
|
2
2
|
import { SQL, SQLQuery } from '@databases/sql'
|
|
3
3
|
import { FastifyError } from '@fastify/error'
|
|
4
|
-
import { createCache } from 'async-cache-dedupe'
|
|
4
|
+
import type { createCache } from 'async-cache-dedupe'
|
|
5
5
|
|
|
6
6
|
type cacheOptions = (Parameters<typeof createCache>[0]) | boolean
|
|
7
7
|
|
|
@@ -315,7 +315,7 @@ export interface Entity<EntityFields = any> {
|
|
|
315
315
|
updateMany: UpdateMany<EntityFields>
|
|
316
316
|
}
|
|
317
317
|
|
|
318
|
-
type EntityHook<T extends (...args: any) => any> = (original: T, ...options: Parameters<T>) => ReturnType<T
|
|
318
|
+
type EntityHook<T extends (...args: any) => any> = (original: T, ...options: Parameters<T>) => ReturnType<T>
|
|
319
319
|
|
|
320
320
|
export interface EntityHooks<EntityFields = any> {
|
|
321
321
|
find?: EntityHook<Find<EntityFields>>,
|
|
@@ -367,7 +367,7 @@ export interface CreateConnectionPoolOptions extends BasePoolOptions {
|
|
|
367
367
|
log: ILogger
|
|
368
368
|
}
|
|
369
369
|
|
|
370
|
-
export function createConnectionPool(options: CreateConnectionPoolOptions): Promise<{ db: Database, sql: SQL }>
|
|
370
|
+
export function createConnectionPool (options: CreateConnectionPoolOptions): Promise<{ db: Database, sql: SQL }>
|
|
371
371
|
|
|
372
372
|
export interface SQLMapperPluginOptions extends BasePoolOptions {
|
|
373
373
|
/**
|
|
@@ -446,7 +446,7 @@ declare module 'fastify' {
|
|
|
446
446
|
/**
|
|
447
447
|
* Connects to the database and maps the tables to entities.
|
|
448
448
|
*/
|
|
449
|
-
export function connect<T extends Entities>(options: SQLMapperPluginOptions): Promise<SQLMapperPluginInterface<T>>
|
|
449
|
+
export function connect<T extends Entities> (options: SQLMapperPluginOptions): Promise<SQLMapperPluginInterface<T>>
|
|
450
450
|
/**
|
|
451
451
|
* Fastify plugin that connects to the database and maps the tables to entities.
|
|
452
452
|
*/
|
|
@@ -457,7 +457,7 @@ export default plugin
|
|
|
457
457
|
* An object that contains utility functions.
|
|
458
458
|
*/
|
|
459
459
|
export module utils {
|
|
460
|
-
export function toSingular(str: string): string
|
|
460
|
+
export function toSingular (str: string): string
|
|
461
461
|
}
|
|
462
462
|
|
|
463
463
|
/**
|
|
@@ -480,5 +480,3 @@ export module errors {
|
|
|
480
480
|
export const MissingValueForPrimaryKeyError: (key: string) => FastifyError
|
|
481
481
|
export const SQLiteOnlySupportsAutoIncrementOnOneColumnError: () => FastifyError
|
|
482
482
|
}
|
|
483
|
-
|
|
484
|
-
|
package/mapper.js
CHANGED
|
@@ -8,6 +8,8 @@ const queriesFactory = require('./lib/queries')
|
|
|
8
8
|
const { areSchemasSupported } = require('./lib/utils')
|
|
9
9
|
const errors = require('./lib/errors')
|
|
10
10
|
const setupCache = require('./lib/cache')
|
|
11
|
+
const { setupTelemetry } = require('./lib/telemetry')
|
|
12
|
+
const { getConnectionInfo } = require('./lib/connection-info')
|
|
11
13
|
|
|
12
14
|
// Ignore the function as it is only used only for MySQL and PostgreSQL
|
|
13
15
|
/* istanbul ignore next */
|
|
@@ -23,34 +25,34 @@ async function buildConnection (log, createConnectionPool, connectionString, poo
|
|
|
23
25
|
log.trace({
|
|
24
26
|
query: {
|
|
25
27
|
text,
|
|
26
|
-
values
|
|
27
|
-
}
|
|
28
|
+
values,
|
|
29
|
+
},
|
|
28
30
|
}, 'start query')
|
|
29
31
|
},
|
|
30
32
|
onQueryResults: (_query, { text }, results) => {
|
|
31
33
|
log.trace({
|
|
32
34
|
query: {
|
|
33
35
|
text,
|
|
34
|
-
results: results.length
|
|
35
|
-
}
|
|
36
|
+
results: results.length,
|
|
37
|
+
},
|
|
36
38
|
}, 'end query')
|
|
37
39
|
},
|
|
38
40
|
onQueryError: (_query, { text }, err) => {
|
|
39
41
|
log.error({
|
|
40
42
|
query: {
|
|
41
43
|
text,
|
|
42
|
-
error: err.message
|
|
43
|
-
}
|
|
44
|
+
error: err.message,
|
|
45
|
+
},
|
|
44
46
|
}, 'query error')
|
|
45
47
|
},
|
|
46
|
-
schema
|
|
48
|
+
schema,
|
|
47
49
|
})
|
|
48
50
|
return db
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
const defaultAutoTimestampFields = {
|
|
52
54
|
createdAt: 'created_at',
|
|
53
|
-
updatedAt: 'updated_at'
|
|
55
|
+
updatedAt: 'updated_at',
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
async function createConnectionPool ({ log, connectionString, poolSize, idleTimeoutMilliseconds, queueTimeoutMilliseconds, acquireLockTimeoutMilliseconds }) {
|
|
@@ -87,17 +89,22 @@ async function createConnectionPool ({ log, connectionString, poolSize, idleTime
|
|
|
87
89
|
onQuery ({ text, values }) {
|
|
88
90
|
log.trace({
|
|
89
91
|
query: {
|
|
90
|
-
text
|
|
91
|
-
}
|
|
92
|
+
text,
|
|
93
|
+
},
|
|
92
94
|
}, 'query')
|
|
93
|
-
}
|
|
95
|
+
},
|
|
94
96
|
})
|
|
95
97
|
sql = sqlite.sql
|
|
96
98
|
db.isSQLite = true
|
|
99
|
+
db.sql = sql
|
|
97
100
|
} else {
|
|
98
101
|
throw new errors.SpecifyProtocolError()
|
|
99
102
|
}
|
|
100
103
|
|
|
104
|
+
// These info are necessary for telemetry attributes
|
|
105
|
+
const connectionInfo = await getConnectionInfo(db, connectionString)
|
|
106
|
+
db.connectionInfo = connectionInfo
|
|
107
|
+
|
|
101
108
|
return { db, sql }
|
|
102
109
|
}
|
|
103
110
|
|
|
@@ -224,7 +231,7 @@ async function connect ({ connectionString, log, onDatabaseLoad, poolSize, inclu
|
|
|
224
231
|
entities,
|
|
225
232
|
cleanUpAllEntities: buildCleanUp(db, sql, log, entities, queries),
|
|
226
233
|
addEntityHooks,
|
|
227
|
-
dbschema
|
|
234
|
+
dbschema,
|
|
228
235
|
}
|
|
229
236
|
|
|
230
237
|
if (cache) {
|
|
@@ -253,7 +260,7 @@ async function connect ({ connectionString, log, onDatabaseLoad, poolSize, inclu
|
|
|
253
260
|
async function sqlMapper (app, opts) {
|
|
254
261
|
const mapper = await connect({
|
|
255
262
|
log: app.log,
|
|
256
|
-
...opts
|
|
263
|
+
...opts,
|
|
257
264
|
})
|
|
258
265
|
|
|
259
266
|
app.onClose(() => mapper.db.dispose())
|
|
@@ -269,10 +276,15 @@ async function sqlMapper (app, opts) {
|
|
|
269
276
|
app.addHook('onRequest', function (req, reply, done) {
|
|
270
277
|
req.platformaticContext = {
|
|
271
278
|
app: this, // uses the encapsulated fastify instance of the route
|
|
272
|
-
reply
|
|
279
|
+
reply,
|
|
273
280
|
}
|
|
274
281
|
done()
|
|
275
282
|
})
|
|
283
|
+
|
|
284
|
+
// TODO: should we enable db telemetry explicitely with a config?
|
|
285
|
+
if (app.openTelemetry) {
|
|
286
|
+
setupTelemetry(app)
|
|
287
|
+
}
|
|
276
288
|
}
|
|
277
289
|
|
|
278
290
|
async function dropTable (db, sql, table) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/sql-mapper",
|
|
3
|
-
"version": "2.0.0-alpha.
|
|
3
|
+
"version": "2.0.0-alpha.3",
|
|
4
4
|
"description": "A data mapper utility for SQL databases",
|
|
5
5
|
"main": "mapper.js",
|
|
6
6
|
"types": "mapper.d.ts",
|
|
@@ -16,11 +16,13 @@
|
|
|
16
16
|
"homepage": "https://github.com/platformatic/platformatic#readme",
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@matteo.collina/tspl": "^0.1.1",
|
|
19
|
-
"
|
|
19
|
+
"@opentelemetry/api": "^1.8.0",
|
|
20
|
+
"borp": "^0.17.0",
|
|
21
|
+
"eslint": "9",
|
|
20
22
|
"fastify": "^4.26.2",
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
23
|
+
"neostandard": "^0.11.1",
|
|
24
|
+
"tsd": "^0.31.0",
|
|
25
|
+
"typescript": "^5.5.4"
|
|
24
26
|
},
|
|
25
27
|
"dependencies": {
|
|
26
28
|
"@databases/mysql": "^6.0.0",
|
|
@@ -33,15 +35,16 @@
|
|
|
33
35
|
"camelcase": "^6.3.0",
|
|
34
36
|
"fastify-plugin": "^4.5.1",
|
|
35
37
|
"inflected": "^2.1.0",
|
|
36
|
-
"@platformatic/utils": "2.0.0-alpha.
|
|
38
|
+
"@platformatic/utils": "2.0.0-alpha.3",
|
|
39
|
+
"@platformatic/telemetry": "2.0.0-alpha.3"
|
|
37
40
|
},
|
|
38
41
|
"tsd": {
|
|
39
42
|
"directory": "test/types"
|
|
40
43
|
},
|
|
41
44
|
"scripts": {
|
|
42
|
-
"lint": "
|
|
45
|
+
"lint": "eslint",
|
|
43
46
|
"test": "npm run lint && npm run test:typescript && npm run test:postgresql && npm run test:mariadb && npm run test:mysql && npm run test:mysql8 && npm run test:sqlite",
|
|
44
|
-
"test:runner": "borp --pattern 'test/**/*.test.{js, mjs}' --concurrency=1 --timeout=
|
|
47
|
+
"test:runner": "borp --pattern 'test/**/*.test.{js, mjs}' --concurrency=1 --timeout=180000",
|
|
45
48
|
"test:postgresql": "DB=postgresql npm run test:runner",
|
|
46
49
|
"test:mariadb": "DB=mariadb npm run test:runner",
|
|
47
50
|
"test:mysql": "DB=mysql npm run test:runner",
|