@platformatic/sql-mapper 3.0.0-alpha.5 → 3.0.0-alpha.8

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 CHANGED
@@ -1,3 +1,3 @@
1
- 'use strict'
1
+ import neostandard from 'neostandard'
2
2
 
3
- module.exports = require('neostandard')({ ts: true })
3
+ export default neostandard({ ts: true })
@@ -319,6 +319,10 @@ export interface Entity<EntityFields = any> {
319
319
  * The primary key of the database entity.
320
320
  */
321
321
  primaryKey: string,
322
+ /**
323
+ * Primary keys of the database entity.
324
+ */
325
+ primaryKeys: Set<string>,
322
326
  /**
323
327
  * The table of the database entity.
324
328
  */
@@ -1,19 +1,31 @@
1
- 'use strict'
2
-
3
- const fp = require('fastify-plugin')
4
- const { findNearestString } = require('@platformatic/foundation')
5
- const buildEntity = require('./lib/entity')
6
- const buildCleanUp = require('./lib/clean-up')
7
- const queriesFactory = require('./lib/queries')
8
- const { areSchemasSupported } = require('./lib/utils')
9
- const errors = require('./lib/errors')
10
- const setupCache = require('./lib/cache')
11
- const { setupTelemetry } = require('./lib/telemetry')
12
- const { getConnectionInfo } = require('./lib/connection-info')
1
+ import { findNearestString } from '@platformatic/foundation'
2
+ import fp from 'fastify-plugin'
3
+ import { setupCache } from './lib/cache.js'
4
+ import { buildCleanUp } from './lib/clean-up.js'
5
+ import { getConnectionInfo } from './lib/connection-info.js'
6
+ import { buildEntity } from './lib/entity.js'
7
+ import {
8
+ CannotFindEntityError,
9
+ ConnectionStringRequiredError,
10
+ SpecifyProtocolError,
11
+ TableMustBeAStringError
12
+ } from './lib/errors.js'
13
+ import * as queriesFactory from './lib/queries/index.js'
14
+ import { setupTelemetry } from './lib/telemetry.js'
15
+ import { areSchemasSupported } from './lib/utils.js'
13
16
 
14
17
  // Ignore the function as it is only used only for MySQL and PostgreSQL
15
18
  /* istanbul ignore next */
16
- async function buildConnection (log, createConnectionPool, connectionString, poolSize, schema, idleTimeoutMilliseconds, queueTimeoutMilliseconds, acquireLockTimeoutMilliseconds) {
19
+ async function buildConnection (
20
+ log,
21
+ createConnectionPool,
22
+ connectionString,
23
+ poolSize,
24
+ schema,
25
+ idleTimeoutMilliseconds,
26
+ queueTimeoutMilliseconds,
27
+ acquireLockTimeoutMilliseconds
28
+ ) {
17
29
  const db = await createConnectionPool({
18
30
  connectionString,
19
31
  bigIntMode: 'string',
@@ -22,40 +34,56 @@ async function buildConnection (log, createConnectionPool, connectionString, poo
22
34
  queueTimeoutMilliseconds,
23
35
  acquireLockTimeoutMilliseconds,
24
36
  onQueryStart: (_query, { text, values }) => {
25
- log.trace({
26
- query: {
27
- text,
28
- values,
37
+ log.trace(
38
+ {
39
+ query: {
40
+ text,
41
+ values
42
+ }
29
43
  },
30
- }, 'start query')
44
+ 'start query'
45
+ )
31
46
  },
32
47
  onQueryResults: (_query, { text }, results) => {
33
- log.trace({
34
- query: {
35
- text,
36
- results: results.length,
48
+ log.trace(
49
+ {
50
+ query: {
51
+ text,
52
+ results: results.length
53
+ }
37
54
  },
38
- }, 'end query')
55
+ 'end query'
56
+ )
39
57
  },
40
58
  onQueryError: (_query, { text }, err) => {
41
- log.error({
42
- query: {
43
- text,
44
- error: err.message,
59
+ log.error(
60
+ {
61
+ query: {
62
+ text,
63
+ error: err.message
64
+ }
45
65
  },
46
- }, 'query error')
66
+ 'query error'
67
+ )
47
68
  },
48
- schema,
69
+ schema
49
70
  })
50
71
  return db
51
72
  }
52
73
 
53
74
  const defaultAutoTimestampFields = {
54
75
  createdAt: 'created_at',
55
- updatedAt: 'updated_at',
76
+ updatedAt: 'updated_at'
56
77
  }
57
78
 
58
- async function createConnectionPool ({ log, connectionString, poolSize, idleTimeoutMilliseconds, queueTimeoutMilliseconds, acquireLockTimeoutMilliseconds }) {
79
+ export async function createConnectionPool ({
80
+ log,
81
+ connectionString,
82
+ poolSize,
83
+ idleTimeoutMilliseconds,
84
+ queueTimeoutMilliseconds,
85
+ acquireLockTimeoutMilliseconds
86
+ }) {
59
87
  let db
60
88
  let sql
61
89
 
@@ -63,13 +91,31 @@ async function createConnectionPool ({ log, connectionString, poolSize, idleTime
63
91
 
64
92
  /* istanbul ignore next */
65
93
  if (connectionString.indexOf('postgres') === 0) {
66
- const createConnectionPoolPg = require('@databases/pg')
67
- db = await buildConnection(log, createConnectionPoolPg, connectionString, poolSize, null, idleTimeoutMilliseconds, queueTimeoutMilliseconds, acquireLockTimeoutMilliseconds)
94
+ const { default: createConnectionPoolPg } = await import('@databases/pg')
95
+ db = await buildConnection(
96
+ log,
97
+ createConnectionPoolPg,
98
+ connectionString,
99
+ poolSize,
100
+ null,
101
+ idleTimeoutMilliseconds,
102
+ queueTimeoutMilliseconds,
103
+ acquireLockTimeoutMilliseconds
104
+ )
68
105
  sql = createConnectionPoolPg.sql
69
106
  db.isPg = true
70
107
  } else if (connectionString.indexOf('mysql') === 0) {
71
- const createConnectionPoolMysql = require('@databases/mysql')
72
- db = await buildConnection(log, createConnectionPoolMysql, connectionString, poolSize, null, idleTimeoutMilliseconds, queueTimeoutMilliseconds, acquireLockTimeoutMilliseconds)
108
+ const { default: createConnectionPoolMysql } = await import('@databases/mysql')
109
+ db = await buildConnection(
110
+ log,
111
+ createConnectionPoolMysql,
112
+ connectionString,
113
+ poolSize,
114
+ null,
115
+ idleTimeoutMilliseconds,
116
+ queueTimeoutMilliseconds,
117
+ acquireLockTimeoutMilliseconds
118
+ )
73
119
  sql = createConnectionPoolMysql.sql
74
120
  const version = (await db.query(sql`SELECT VERSION()`))[0]['VERSION()']
75
121
  db.version = version
@@ -78,27 +124,34 @@ async function createConnectionPool ({ log, connectionString, poolSize, idleTime
78
124
  db.isMySql = true
79
125
  }
80
126
  } else if (connectionString.indexOf('sqlite') === 0) {
81
- const sqlite = require('@matteo.collina/sqlite-pool')
127
+ const { default: sqlite } = await import('@matteo.collina/sqlite-pool')
82
128
  const path = connectionString.replace('sqlite://', '')
83
- db = sqlite.default(connectionString === 'sqlite://:memory:' ? undefined : path, {}, {
84
- // TODO make this configurable
85
- maxSize: 1,
86
- // TODO make this configurable
87
- // 10s max time to wait for a connection
88
- releaseTimeoutMilliseconds: 10000,
89
- onQuery ({ text, values }) {
90
- log.trace({
91
- query: {
92
- text,
93
- },
94
- }, 'query')
95
- },
96
- })
129
+ db = sqlite.default(
130
+ connectionString === 'sqlite://:memory:' ? undefined : path,
131
+ {},
132
+ {
133
+ // TODO make this configurable
134
+ maxSize: 1,
135
+ // TODO make this configurable
136
+ // 10s max time to wait for a connection
137
+ releaseTimeoutMilliseconds: 10000,
138
+ onQuery ({ text, values }) {
139
+ log.trace(
140
+ {
141
+ query: {
142
+ text
143
+ }
144
+ },
145
+ 'query'
146
+ )
147
+ }
148
+ }
149
+ )
97
150
  sql = sqlite.sql
98
151
  db.isSQLite = true
99
152
  db.sql = sql
100
153
  } else {
101
- throw new errors.SpecifyProtocolError()
154
+ throw new SpecifyProtocolError()
102
155
  }
103
156
 
104
157
  // These info are necessary for telemetry attributes
@@ -108,17 +161,40 @@ async function createConnectionPool ({ log, connectionString, poolSize, idleTime
108
161
  return { db, sql }
109
162
  }
110
163
 
111
- async function connect ({ connectionString, log, onDatabaseLoad, poolSize, include = {}, ignore = {}, autoTimestamp = true, hooks = {}, schema, limit = {}, dbschema, cache, idleTimeoutMilliseconds, queueTimeoutMilliseconds, acquireLockTimeoutMilliseconds }) {
164
+ export async function connect ({
165
+ connectionString,
166
+ log,
167
+ onDatabaseLoad,
168
+ poolSize,
169
+ include = {},
170
+ ignore = {},
171
+ autoTimestamp = true,
172
+ hooks = {},
173
+ schema,
174
+ limit = {},
175
+ dbschema,
176
+ cache,
177
+ idleTimeoutMilliseconds,
178
+ queueTimeoutMilliseconds,
179
+ acquireLockTimeoutMilliseconds
180
+ }) {
112
181
  if (typeof autoTimestamp === 'boolean' && autoTimestamp === true) {
113
182
  autoTimestamp = defaultAutoTimestampFields
114
183
  }
115
184
  // TODO validate config using the schema
116
185
  if (!connectionString) {
117
- throw new errors.ConnectionStringRequiredError()
186
+ throw new ConnectionStringRequiredError()
118
187
  }
119
188
 
120
189
  let queries
121
- const { db, sql } = await createConnectionPool({ log, connectionString, poolSize, queueTimeoutMilliseconds, acquireLockTimeoutMilliseconds, idleTimeoutMilliseconds })
190
+ const { db, sql } = await createConnectionPool({
191
+ log,
192
+ connectionString,
193
+ poolSize,
194
+ queueTimeoutMilliseconds,
195
+ acquireLockTimeoutMilliseconds,
196
+ idleTimeoutMilliseconds
197
+ })
122
198
 
123
199
  /* istanbul ignore next */
124
200
  if (db.isPg) {
@@ -200,7 +276,7 @@ async function connect ({ connectionString, log, onDatabaseLoad, poolSize, inclu
200
276
  // it should never happen.
201
277
  /* istanbul ignore next */
202
278
  if (typeof table !== 'string') {
203
- throw new errors.TableMustBeAStringError(table)
279
+ throw new TableMustBeAStringError(table)
204
280
  }
205
281
  // If include is being used and a table is not explicitly included add it to the ignore object
206
282
  if (Object.keys(include).length && !include[table]) {
@@ -209,7 +285,21 @@ async function connect ({ connectionString, log, onDatabaseLoad, poolSize, inclu
209
285
  if (ignore[table] === true) {
210
286
  continue
211
287
  }
212
- const entity = buildEntity(db, sql, log, table, queries, autoTimestamp, schema, useSchema, ignore[table] || {}, limit, schemaList, columns, constraints)
288
+ const entity = buildEntity(
289
+ db,
290
+ sql,
291
+ log,
292
+ table,
293
+ queries,
294
+ autoTimestamp,
295
+ schema,
296
+ useSchema,
297
+ ignore[table] || {},
298
+ limit,
299
+ schemaList,
300
+ columns,
301
+ constraints
302
+ )
213
303
  // Check for primary key of all entities
214
304
  if (entity.primaryKeys.size === 0) {
215
305
  log.warn({ table }, 'Cannot find any primary keys for table')
@@ -231,7 +321,7 @@ async function connect ({ connectionString, log, onDatabaseLoad, poolSize, inclu
231
321
  entities,
232
322
  cleanUpAllEntities: buildCleanUp(db, sql, log, entities, queries),
233
323
  addEntityHooks,
234
- dbschema,
324
+ dbschema
235
325
  }
236
326
 
237
327
  if (cache) {
@@ -247,7 +337,7 @@ async function connect ({ connectionString, log, onDatabaseLoad, poolSize, inclu
247
337
  function addEntityHooks (entityName, hooks) {
248
338
  const entity = entities[entityName]
249
339
  if (!entity) {
250
- throw new errors.CannotFindEntityError(entityName)
340
+ throw new CannotFindEntityError(entityName)
251
341
  }
252
342
  for (const key of Object.keys(hooks)) {
253
343
  if (hooks[key] && entity[key]) {
@@ -260,7 +350,7 @@ async function connect ({ connectionString, log, onDatabaseLoad, poolSize, inclu
260
350
  async function sqlMapper (app, opts) {
261
351
  const mapper = await connect({
262
352
  log: app.log,
263
- ...opts,
353
+ ...opts
264
354
  })
265
355
 
266
356
  app.onClose(() => mapper.db.dispose())
@@ -276,7 +366,7 @@ async function sqlMapper (app, opts) {
276
366
  app.addHook('onRequest', function (req, reply, done) {
277
367
  req.platformaticContext = {
278
368
  app: this, // uses the encapsulated fastify instance of the route
279
- reply,
369
+ reply
280
370
  }
281
371
  done()
282
372
  })
@@ -300,7 +390,7 @@ async function dropTable (db, sql, table) {
300
390
  }
301
391
  }
302
392
 
303
- async function dropAllTables (db, sql, schemas) {
393
+ export async function dropAllTables (db, sql, schemas) {
304
394
  let queries
305
395
  /* istanbul ignore next */
306
396
  if (db.isPg) {
@@ -313,13 +403,15 @@ async function dropAllTables (db, sql, schemas) {
313
403
  queries = queriesFactory.sqlite
314
404
  }
315
405
 
316
- const tables = new Set((await queries.listTables(db, sql, schemas)).map((t) => {
317
- /* istanbul ignore next */
318
- if (t.schema) {
319
- return `${t.schema}.${t.table}`
320
- }
321
- return t.table
322
- }))
406
+ const tables = new Set(
407
+ (await queries.listTables(db, sql, schemas)).map(t => {
408
+ /* istanbul ignore next */
409
+ if (t.schema) {
410
+ return `${t.schema}.${t.table}`
411
+ }
412
+ return t.table
413
+ })
414
+ )
323
415
  let count = 0
324
416
 
325
417
  while (tables.size > 0) {
@@ -341,10 +433,7 @@ async function dropAllTables (db, sql, schemas) {
341
433
  }
342
434
  }
343
435
 
344
- module.exports = fp(sqlMapper)
345
- module.exports.connect = connect
346
- module.exports.createConnectionPool = createConnectionPool
347
- module.exports.plugin = module.exports
348
- module.exports.utils = require('./lib/utils')
349
- module.exports.errors = errors
350
- module.exports.dropAllTables = dropAllTables
436
+ export const plugin = fp(sqlMapper)
437
+ export default plugin
438
+ export * as errors from './lib/errors.js'
439
+ export * as utils from './lib/utils.js'
package/lib/cache.js CHANGED
@@ -1,7 +1,6 @@
1
- 'use strict'
2
- const { createCache } = require('async-cache-dedupe')
1
+ import { createCache } from 'async-cache-dedupe'
3
2
 
4
- function setupCache (res, opts) {
3
+ export function setupCache (res, opts) {
5
4
  // TODO validate opts
6
5
  if (opts === true) {
7
6
  opts = { ttl: 0 }
@@ -17,18 +16,22 @@ function setupCache (res, opts) {
17
16
  const fnName = `${entity.name}Find`
18
17
  const originalFn = entity.find
19
18
 
20
- cache.define(fnName, {
21
- serialize (query) {
22
- const serialized = {
23
- ...query,
24
- ctx: undefined,
19
+ cache.define(
20
+ fnName,
21
+ {
22
+ serialize (query) {
23
+ const serialized = {
24
+ ...query,
25
+ ctx: undefined
26
+ }
27
+ return serialized
25
28
  }
26
- return serialized
27
29
  },
28
- }, async function (query) {
29
- const res = await originalFn.call(entity, query)
30
- return res
31
- })
30
+ async function (query) {
31
+ const res = await originalFn.call(entity, query)
32
+ return res
33
+ }
34
+ )
32
35
 
33
36
  addEntityHooks(entity.singularName, {
34
37
  find (originalFn, query) {
@@ -36,11 +39,9 @@ function setupCache (res, opts) {
36
39
  return originalFn(query)
37
40
  }
38
41
  return cache[fnName](query)
39
- },
42
+ }
40
43
  })
41
44
  }
42
45
 
43
46
  return cache
44
47
  }
45
-
46
- module.exports = setupCache
package/lib/clean-up.js CHANGED
@@ -1,9 +1,7 @@
1
- 'use strict'
1
+ import { Sorter } from '@hapi/topo'
2
+ import { tableName } from './utils.js'
2
3
 
3
- const { tableName } = require('./utils')
4
- const { Sorter } = require('@hapi/topo')
5
-
6
- function buildCleanUp (db, sql, logger, entities) {
4
+ export function buildCleanUp (db, sql, logger, entities) {
7
5
  return async function cleanUp () {
8
6
  logger.trace('cleaning up')
9
7
  await db.tx(async tx => {
@@ -32,5 +30,3 @@ function buildCleanUp (db, sql, logger, entities) {
32
30
  })
33
31
  }
34
32
  }
35
-
36
- module.exports = buildCleanUp
@@ -1,7 +1,7 @@
1
1
  // The most general way to get the connection info is through the driver.
2
2
  // In this way, we don't need to do any assumptions about the connection string
3
3
  // (with the exception of SQLite)
4
- const getConnectionInfo = async (db, connectionString) => {
4
+ export async function getConnectionInfo (db, connectionString) {
5
5
  let database, host, port, user
6
6
  let dbSystem = 'unknown'
7
7
  if (db.isPg) {
@@ -35,8 +35,6 @@ const getConnectionInfo = async (db, connectionString) => {
35
35
  isPg: !!db.isPg,
36
36
  isMySql: !!db.isMySql,
37
37
  isSQLite: !!db.isSQLite,
38
- dbSystem,
38
+ dbSystem
39
39
  }
40
40
  }
41
-
42
- module.exports = { getConnectionInfo }
package/lib/cursor.js CHANGED
@@ -1,17 +1,20 @@
1
- 'use strict'
2
-
3
- const errors = require('./errors')
1
+ import {
2
+ MissingOrderByClauseError,
3
+ MissingOrderByFieldForCursorError,
4
+ MissingUniqueFieldInCursorError,
5
+ UnknownFieldError
6
+ } from './errors.js'
4
7
 
5
8
  function sanitizeCursor (cursor, orderBy, inputToFieldMap, fields, primaryKeys) {
6
- if (!orderBy || orderBy.length === 0) throw new errors.MissingOrderByClauseError()
9
+ if (!orderBy || orderBy.length === 0) throw new MissingOrderByClauseError()
7
10
  let hasUniqueField = false
8
11
  const validCursorFields = new Map()
9
12
 
10
13
  for (const [key, value] of Object.entries(cursor)) {
11
14
  const dbField = inputToFieldMap[key]
12
- if (!dbField) throw new errors.UnknownFieldError(key)
13
- const order = orderBy.find((order) => order.field === key)
14
- if (!order) throw new errors.MissingOrderByFieldForCursorError(key)
15
+ if (!dbField) throw new UnknownFieldError(key)
16
+ const order = orderBy.find(order => order.field === key)
17
+ if (!order) throw new MissingOrderByFieldForCursorError(key)
15
18
  if (primaryKeys.has(dbField)) hasUniqueField = true
16
19
  validCursorFields.set(key, {
17
20
  dbField,
@@ -20,7 +23,7 @@ function sanitizeCursor (cursor, orderBy, inputToFieldMap, fields, primaryKeys)
20
23
  fieldWrap: fields[dbField]
21
24
  })
22
25
  }
23
- if (!hasUniqueField) throw new errors.MissingUniqueFieldInCursorError()
26
+ if (!hasUniqueField) throw new MissingUniqueFieldInCursorError()
24
27
 
25
28
  // Process fields in orderBy order
26
29
  const cursorFields = []
@@ -72,7 +75,16 @@ function buildQuery (cursorFields, sql, computeCriteriaValue, isBackwardPaginati
72
75
  return sql`(${sql.join(conditions, sql` OR `)})`
73
76
  }
74
77
 
75
- function buildCursorCondition (sql, cursor, orderBy, inputToFieldMap, fields, computeCriteriaValue, primaryKeys, isBackwardPagination) {
78
+ export function buildCursorCondition (
79
+ sql,
80
+ cursor,
81
+ orderBy,
82
+ inputToFieldMap,
83
+ fields,
84
+ computeCriteriaValue,
85
+ primaryKeys,
86
+ isBackwardPagination
87
+ ) {
76
88
  if (!cursor || Object.keys(cursor).length === 0) return null
77
89
  const cursorFields = sanitizeCursor(cursor, orderBy, inputToFieldMap, fields, primaryKeys)
78
90
  const sameSortDirection = cursorFields.every(({ direction }) => direction === cursorFields[0].direction)
@@ -80,7 +92,3 @@ function buildCursorCondition (sql, cursor, orderBy, inputToFieldMap, fields, co
80
92
  ? buildTupleQuery(cursorFields, sql, computeCriteriaValue, isBackwardPagination)
81
93
  : buildQuery(cursorFields, sql, computeCriteriaValue, isBackwardPagination)
82
94
  }
83
-
84
- module.exports = {
85
- buildCursorCondition,
86
- }