@platformatic/sql-mapper 0.8.0 → 0.9.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 +18 -14
- package/lib/queries/pg.js +0 -1
- package/lib/utils.js +33 -2
- package/mapper.js +12 -19
- package/package.json +2 -2
- package/test/schema.test.js +120 -61
- package/test/where.test.js +164 -0
package/lib/entity.js
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
'use strict'
|
|
2
2
|
|
|
3
3
|
const camelcase = require('camelcase')
|
|
4
|
-
const { singularize } = require('inflected')
|
|
5
4
|
const {
|
|
6
5
|
toSingular,
|
|
7
|
-
|
|
6
|
+
toUpperFirst,
|
|
7
|
+
tableName,
|
|
8
|
+
sanitizeLimit
|
|
8
9
|
} = require('./utils')
|
|
9
10
|
|
|
10
|
-
function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relations, queries, autoTimestamp, schema) {
|
|
11
|
-
|
|
11
|
+
function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relations, queries, autoTimestamp, schema, useSchemaInName, limitConfig) {
|
|
12
|
+
/* istanbul ignore next */ // Ignoring because this won't be fully covered by DB not supporting schemas (SQLite)
|
|
13
|
+
const entityName = useSchemaInName ? toUpperFirst(`${schema}${toSingular(table)}`) : toSingular(table)
|
|
14
|
+
/* istanbul ignore next */
|
|
15
|
+
const pluralName = camelcase(useSchemaInName ? camelcase(`${schema} ${table}`) : table)
|
|
16
|
+
const singularName = camelcase(entityName)
|
|
12
17
|
|
|
13
18
|
// Fields remapping
|
|
14
19
|
const fieldMapToRetrieve = {}
|
|
@@ -265,13 +270,12 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relation
|
|
|
265
270
|
query = sql`${query} ORDER BY ${sql.join(orderBy, sql`, `)}`
|
|
266
271
|
}
|
|
267
272
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
if (opts.offset !== undefined) {
|
|
273
|
-
query = sql`${query} OFFSET ${opts.offset}`
|
|
273
|
+
query = sql`${query} LIMIT ${sanitizeLimit(opts.limit, limitConfig)}`
|
|
274
|
+
if (opts.offset !== undefined) {
|
|
275
|
+
if (opts.offset < 0) {
|
|
276
|
+
throw new Error(`Param offset=${opts.offset} not allowed. It must be not negative value.`)
|
|
274
277
|
}
|
|
278
|
+
query = sql`${query} OFFSET ${opts.offset}`
|
|
275
279
|
}
|
|
276
280
|
|
|
277
281
|
const res = await db.query(query)
|
|
@@ -303,8 +307,8 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relation
|
|
|
303
307
|
|
|
304
308
|
return {
|
|
305
309
|
name: entityName,
|
|
306
|
-
singularName
|
|
307
|
-
pluralName
|
|
310
|
+
singularName,
|
|
311
|
+
pluralName,
|
|
308
312
|
primaryKeys,
|
|
309
313
|
table,
|
|
310
314
|
schema,
|
|
@@ -321,7 +325,7 @@ function createMapper (defaultDb, sql, log, table, fields, primaryKeys, relation
|
|
|
321
325
|
}
|
|
322
326
|
}
|
|
323
327
|
|
|
324
|
-
async function buildEntity (db, sql, log, table, queries, autoTimestamp, schema, ignore) {
|
|
328
|
+
async function buildEntity (db, sql, log, table, queries, autoTimestamp, schema, useSchemaInName, ignore, limitConfig) {
|
|
325
329
|
// Compute the columns
|
|
326
330
|
const columns = (await queries.listColumns(db, sql, table, schema)).filter((c) => !ignore[c.column_name])
|
|
327
331
|
const fields = columns.reduce((acc, column) => {
|
|
@@ -391,7 +395,7 @@ async function buildEntity (db, sql, log, table, queries, autoTimestamp, schema,
|
|
|
391
395
|
}
|
|
392
396
|
}
|
|
393
397
|
|
|
394
|
-
const entity = createMapper(db, sql, log, table, fields, primaryKeys, currentRelations, queries, autoTimestamp, schema)
|
|
398
|
+
const entity = createMapper(db, sql, log, table, fields, primaryKeys, currentRelations, queries, autoTimestamp, schema, useSchemaInName, limitConfig)
|
|
395
399
|
entity.relations = currentRelations
|
|
396
400
|
|
|
397
401
|
return entity
|
package/lib/queries/pg.js
CHANGED
package/lib/utils.js
CHANGED
|
@@ -3,18 +3,49 @@
|
|
|
3
3
|
const { singularize } = require('inflected')
|
|
4
4
|
const camelcase = require('camelcase')
|
|
5
5
|
|
|
6
|
+
function toUpperFirst (str) {
|
|
7
|
+
return str[0].toUpperCase() + str.slice(1)
|
|
8
|
+
}
|
|
9
|
+
|
|
6
10
|
function toSingular (str) {
|
|
7
11
|
str = camelcase(singularize(str))
|
|
8
|
-
str =
|
|
12
|
+
str = toUpperFirst(str)
|
|
9
13
|
return str
|
|
10
14
|
}
|
|
11
15
|
|
|
16
|
+
/**
|
|
17
|
+
* If limit is not defined or invalid
|
|
18
|
+
* let's set a safe default value preventing to load huge amount of data in memory
|
|
19
|
+
*/
|
|
20
|
+
function sanitizeLimit (unsafeLimit, conf) {
|
|
21
|
+
const defaultLimit = conf?.default ?? 10
|
|
22
|
+
const limit = (unsafeLimit !== undefined) ? unsafeLimit : defaultLimit
|
|
23
|
+
const max = conf?.max ?? 100
|
|
24
|
+
|
|
25
|
+
if (limit > max) {
|
|
26
|
+
throw new Error(`Param limit=${limit} not allowed. Max accepted value ${max}.`)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (limit < 0) {
|
|
30
|
+
throw new Error(`Param limit=${limit} not allowed. It must be not negative value.`)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return limit
|
|
34
|
+
}
|
|
35
|
+
|
|
12
36
|
function tableName (sql, table, schema) {
|
|
13
37
|
/* istanbul ignore next */
|
|
14
38
|
return schema ? sql.ident(schema, table) : sql.ident(table)
|
|
15
39
|
}
|
|
16
40
|
|
|
41
|
+
function areSchemasSupported (sql) {
|
|
42
|
+
return !sql.isSQLite
|
|
43
|
+
}
|
|
44
|
+
|
|
17
45
|
module.exports = {
|
|
18
46
|
toSingular,
|
|
19
|
-
|
|
47
|
+
toUpperFirst,
|
|
48
|
+
sanitizeLimit,
|
|
49
|
+
tableName,
|
|
50
|
+
areSchemasSupported
|
|
20
51
|
}
|
package/mapper.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
'use strict'
|
|
2
|
-
|
|
3
1
|
const buildEntity = require('./lib/entity')
|
|
4
2
|
const queriesFactory = require('./lib/queries')
|
|
5
3
|
const fp = require('fastify-plugin')
|
|
4
|
+
const { areSchemasSupported } = require('./lib/utils')
|
|
6
5
|
|
|
7
6
|
// Ignore the function as it is only used only for MySQL and PostgreSQL
|
|
8
7
|
/* istanbul ignore next */
|
|
@@ -40,7 +39,7 @@ async function buildConnection (log, createConnectionPool, connectionString, poo
|
|
|
40
39
|
return db
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
async function connect ({ connectionString, log, onDatabaseLoad, poolSize = 10, ignore = {}, autoTimestamp = true, hooks = {}, schema }) {
|
|
42
|
+
async function connect ({ connectionString, log, onDatabaseLoad, poolSize = 10, ignore = {}, autoTimestamp = true, hooks = {}, schema, limit = {} }) {
|
|
44
43
|
// TODO validate config using the schema
|
|
45
44
|
if (!connectionString) {
|
|
46
45
|
throw new Error('connectionString is required')
|
|
@@ -50,15 +49,10 @@ async function connect ({ connectionString, log, onDatabaseLoad, poolSize = 10,
|
|
|
50
49
|
let sql
|
|
51
50
|
let db
|
|
52
51
|
|
|
53
|
-
// Specify an empty array must be the same of specifying no schema
|
|
54
|
-
const schemaList = schema?.length > 0 ? schema : null
|
|
55
|
-
|
|
56
52
|
/* istanbul ignore next */
|
|
57
53
|
if (connectionString.indexOf('postgres') === 0) {
|
|
58
54
|
const createConnectionPoolPg = require('@databases/pg')
|
|
59
|
-
|
|
60
|
-
// because now we use fully qualified names in all queries.
|
|
61
|
-
db = await buildConnection(log, createConnectionPoolPg, connectionString, poolSize, schemaList)
|
|
55
|
+
db = await buildConnection(log, createConnectionPoolPg, connectionString, poolSize)
|
|
62
56
|
sql = createConnectionPoolPg.sql
|
|
63
57
|
queries = queriesFactory.pg
|
|
64
58
|
db.isPg = true
|
|
@@ -93,6 +87,11 @@ async function connect ({ connectionString, log, onDatabaseLoad, poolSize = 10,
|
|
|
93
87
|
throw new Error('You must specify either postgres, mysql or sqlite as protocols')
|
|
94
88
|
}
|
|
95
89
|
|
|
90
|
+
// Specify an empty array must be the same of specifying no schema
|
|
91
|
+
/* istanbul ignore next */ // Ignoring because this won't be fully covered by DB not supporting schemas (SQLite)
|
|
92
|
+
const schemaList = areSchemasSupported(db) && schema?.length > 0 ? schema : null
|
|
93
|
+
const useSchema = !!schemaList
|
|
94
|
+
|
|
96
95
|
const entities = {}
|
|
97
96
|
|
|
98
97
|
try {
|
|
@@ -102,14 +101,6 @@ async function connect ({ connectionString, log, onDatabaseLoad, poolSize = 10,
|
|
|
102
101
|
}
|
|
103
102
|
|
|
104
103
|
const tablesWithSchema = await queries.listTables(db, sql, schemaList)
|
|
105
|
-
const tables = tablesWithSchema.map(({ table }) => table)
|
|
106
|
-
const duplicates = tables.filter((table, index) => tables.indexOf(table) !== index)
|
|
107
|
-
|
|
108
|
-
// Ignored because this never happens in sqlite
|
|
109
|
-
/* istanbul ignore next */
|
|
110
|
-
if (duplicates.length > 0) {
|
|
111
|
-
throw new Error(`Conflicting table names: ${duplicates.join(', ')}`)
|
|
112
|
-
}
|
|
113
104
|
|
|
114
105
|
for (const { table, schema } of tablesWithSchema) {
|
|
115
106
|
// The following line is a safety net when developing this module,
|
|
@@ -121,12 +112,14 @@ async function connect ({ connectionString, log, onDatabaseLoad, poolSize = 10,
|
|
|
121
112
|
if (ignore[table] === true) {
|
|
122
113
|
continue
|
|
123
114
|
}
|
|
124
|
-
const entity = await buildEntity(db, sql, log, table, queries, autoTimestamp, schema, ignore[table] || {})
|
|
115
|
+
const entity = await buildEntity(db, sql, log, table, queries, autoTimestamp, schema, useSchema, ignore[table] || {}, limit)
|
|
125
116
|
// Check for primary key of all entities
|
|
126
117
|
if (entity.primaryKeys.size === 0) {
|
|
127
|
-
throw
|
|
118
|
+
throw Error(`Cannot find any primary keys for ${entity.name} entity`)
|
|
128
119
|
}
|
|
120
|
+
|
|
129
121
|
entities[entity.singularName] = entity
|
|
122
|
+
|
|
130
123
|
if (hooks[entity.name]) {
|
|
131
124
|
addEntityHooks(entity.singularName, hooks[entity.name])
|
|
132
125
|
} else if (hooks[entity.singularName]) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@platformatic/sql-mapper",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.1",
|
|
4
4
|
"description": "A data mapper utility for SQL databases",
|
|
5
5
|
"main": "mapper.js",
|
|
6
6
|
"repository": {
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
"snazzy": "^9.0.0",
|
|
19
19
|
"standard": "^17.0.0",
|
|
20
20
|
"tap": "^16.0.0",
|
|
21
|
-
"tsd": "^0.
|
|
21
|
+
"tsd": "^0.25.0"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
24
|
"@databases/mysql": "^5.2.0",
|
package/test/schema.test.js
CHANGED
|
@@ -48,15 +48,15 @@ test('uses tables from different schemas', { skip: isSQLite }, async ({ pass, te
|
|
|
48
48
|
hooks: {},
|
|
49
49
|
schema: ['test1', 'test2']
|
|
50
50
|
})
|
|
51
|
-
const pageEntity = mapper.entities.
|
|
52
|
-
equal(pageEntity.name, '
|
|
53
|
-
equal(pageEntity.singularName, '
|
|
54
|
-
equal(pageEntity.pluralName, '
|
|
51
|
+
const pageEntity = mapper.entities.test1Page
|
|
52
|
+
equal(pageEntity.name, 'Test1Page')
|
|
53
|
+
equal(pageEntity.singularName, 'test1Page')
|
|
54
|
+
equal(pageEntity.pluralName, 'test1Pages')
|
|
55
55
|
equal(pageEntity.schema, 'test1')
|
|
56
|
-
const userEntity = mapper.entities.
|
|
57
|
-
equal(userEntity.name, '
|
|
58
|
-
equal(userEntity.singularName, '
|
|
59
|
-
equal(userEntity.pluralName, '
|
|
56
|
+
const userEntity = mapper.entities.test2User
|
|
57
|
+
equal(userEntity.name, 'Test2User')
|
|
58
|
+
equal(userEntity.singularName, 'test2User')
|
|
59
|
+
equal(userEntity.pluralName, 'test2Users')
|
|
60
60
|
equal(userEntity.schema, 'test2')
|
|
61
61
|
pass()
|
|
62
62
|
})
|
|
@@ -98,61 +98,13 @@ test('find enums correctly using schemas', { skip: isSQLite }, async ({ pass, te
|
|
|
98
98
|
hooks: {},
|
|
99
99
|
schema: ['test1']
|
|
100
100
|
})
|
|
101
|
-
const pageEntity = mapper.entities.
|
|
102
|
-
equal(pageEntity.name, '
|
|
103
|
-
equal(pageEntity.singularName, '
|
|
104
|
-
equal(pageEntity.pluralName, '
|
|
101
|
+
const pageEntity = mapper.entities.test1Page
|
|
102
|
+
equal(pageEntity.name, 'Test1Page')
|
|
103
|
+
equal(pageEntity.singularName, 'test1Page')
|
|
104
|
+
equal(pageEntity.pluralName, 'test1Pages')
|
|
105
105
|
pass()
|
|
106
106
|
})
|
|
107
107
|
|
|
108
|
-
test('should fail if two schemas has the same table', { skip: isSQLite }, async ({ pass, teardown, equal }) => {
|
|
109
|
-
async function onDatabaseLoad (db, sql) {
|
|
110
|
-
await clear(db, sql)
|
|
111
|
-
teardown(() => db.dispose())
|
|
112
|
-
|
|
113
|
-
await db.query(sql`CREATE SCHEMA IF NOT EXISTS test1;`)
|
|
114
|
-
if (isMysql || isMysql8) {
|
|
115
|
-
await db.query(sql`CREATE TABLE IF NOT EXISTS \`test1\`.\`pages\` (
|
|
116
|
-
id SERIAL PRIMARY KEY,
|
|
117
|
-
title VARCHAR(255) NOT NULL
|
|
118
|
-
);`)
|
|
119
|
-
} else {
|
|
120
|
-
await db.query(sql`CREATE TABLE IF NOT EXISTS "test1"."pages" (
|
|
121
|
-
id SERIAL PRIMARY KEY,
|
|
122
|
-
title VARCHAR(255) NOT NULL
|
|
123
|
-
);`)
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
await db.query(sql`CREATE SCHEMA IF NOT EXISTS test2;`)
|
|
127
|
-
|
|
128
|
-
if (isMysql || isMysql8) {
|
|
129
|
-
await db.query(sql`CREATE TABLE IF NOT EXISTS \`test2\`.\`pages\` (
|
|
130
|
-
id SERIAL PRIMARY KEY,
|
|
131
|
-
username VARCHAR(255) NOT NULL
|
|
132
|
-
);`)
|
|
133
|
-
} else {
|
|
134
|
-
await db.query(sql`CREATE TABLE IF NOT EXISTS "test2"."pages" (
|
|
135
|
-
id SERIAL PRIMARY KEY,
|
|
136
|
-
username VARCHAR(255) NOT NULL
|
|
137
|
-
);`)
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
try {
|
|
142
|
-
await connect({
|
|
143
|
-
connectionString: connInfo.connectionString,
|
|
144
|
-
log: fakeLogger,
|
|
145
|
-
onDatabaseLoad,
|
|
146
|
-
ignore: {},
|
|
147
|
-
hooks: {},
|
|
148
|
-
schema: ['test1', 'test2']
|
|
149
|
-
})
|
|
150
|
-
} catch (err) {
|
|
151
|
-
console.log(err.message)
|
|
152
|
-
equal(err.message, 'Conflicting table names: pages')
|
|
153
|
-
}
|
|
154
|
-
})
|
|
155
|
-
|
|
156
108
|
test('if schema is empty array, should not load entities from tables in explicit schema', { skip: isSQLite }, async ({ pass, teardown, equal }) => {
|
|
157
109
|
async function onDatabaseLoad (db, sql) {
|
|
158
110
|
await clear(db, sql)
|
|
@@ -194,7 +146,6 @@ test('if schema is empty array, should not load entities from tables in explicit
|
|
|
194
146
|
schema: []
|
|
195
147
|
})
|
|
196
148
|
|
|
197
|
-
console.log(mapper.entities)
|
|
198
149
|
equal(Object.keys(mapper.entities).length, 0)
|
|
199
150
|
pass()
|
|
200
151
|
})
|
|
@@ -259,3 +210,111 @@ test('[sqlite] if sqllite, ignores schema information', { skip: !isSQLite }, asy
|
|
|
259
210
|
equal(pageEntity.schema, null)
|
|
260
211
|
pass()
|
|
261
212
|
})
|
|
213
|
+
|
|
214
|
+
test('addEntityHooks in entities with schema', { skip: isSQLite }, async ({ pass, teardown, same, equal, plan, fail, throws, end }) => {
|
|
215
|
+
async function onDatabaseLoad (db, sql) {
|
|
216
|
+
await clear(db, sql)
|
|
217
|
+
teardown(() => db.dispose())
|
|
218
|
+
|
|
219
|
+
await db.query(sql`CREATE SCHEMA IF NOT EXISTS test1;`)
|
|
220
|
+
if (isMysql || isMysql8) {
|
|
221
|
+
await db.query(sql`CREATE TABLE IF NOT EXISTS \`test1\`.\`pages\` (
|
|
222
|
+
id SERIAL PRIMARY KEY,
|
|
223
|
+
title VARCHAR(255) NOT NULL
|
|
224
|
+
);`)
|
|
225
|
+
} else {
|
|
226
|
+
await db.query(sql`CREATE TABLE IF NOT EXISTS "test1"."pages" (
|
|
227
|
+
id SERIAL PRIMARY KEY,
|
|
228
|
+
title VARCHAR(255) NOT NULL
|
|
229
|
+
);`)
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const mapper = await connect({
|
|
234
|
+
...connInfo,
|
|
235
|
+
log: fakeLogger,
|
|
236
|
+
onDatabaseLoad,
|
|
237
|
+
schema: ['test1']
|
|
238
|
+
})
|
|
239
|
+
|
|
240
|
+
throws(() => mapper.addEntityHooks('user', {}), 'Cannot find entity user')
|
|
241
|
+
|
|
242
|
+
mapper.addEntityHooks('test1Page', {
|
|
243
|
+
noKey () {
|
|
244
|
+
fail('noKey should never be called')
|
|
245
|
+
},
|
|
246
|
+
async save (original, { input, ctx, fields }) {
|
|
247
|
+
pass('save called')
|
|
248
|
+
|
|
249
|
+
if (!input.id) {
|
|
250
|
+
same(input, {
|
|
251
|
+
title: 'Hello'
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
return original({
|
|
255
|
+
input: {
|
|
256
|
+
title: 'Hello from hook'
|
|
257
|
+
},
|
|
258
|
+
fields
|
|
259
|
+
})
|
|
260
|
+
} else {
|
|
261
|
+
same(input, {
|
|
262
|
+
id: 1,
|
|
263
|
+
title: 'Hello World'
|
|
264
|
+
})
|
|
265
|
+
|
|
266
|
+
return original({
|
|
267
|
+
input: {
|
|
268
|
+
id: 1,
|
|
269
|
+
title: 'Hello from hook 2'
|
|
270
|
+
},
|
|
271
|
+
fields
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
async find (original, args) {
|
|
276
|
+
pass('find called')
|
|
277
|
+
|
|
278
|
+
same(args.where, {
|
|
279
|
+
id: {
|
|
280
|
+
eq: '1'
|
|
281
|
+
}
|
|
282
|
+
})
|
|
283
|
+
args.where = {
|
|
284
|
+
id: {
|
|
285
|
+
eq: '2'
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
same(args.fields, ['id', 'title'])
|
|
289
|
+
return original(args)
|
|
290
|
+
},
|
|
291
|
+
async insert (original, args) {
|
|
292
|
+
pass('insert called')
|
|
293
|
+
|
|
294
|
+
same(args.inputs, [{
|
|
295
|
+
title: 'hello'
|
|
296
|
+
}, {
|
|
297
|
+
title: 'world'
|
|
298
|
+
}])
|
|
299
|
+
same(args.fields, ['id', 'title'])
|
|
300
|
+
return original(args)
|
|
301
|
+
}
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
const entity = mapper.entities.test1Page
|
|
305
|
+
|
|
306
|
+
same(await entity.save({ input: { title: 'Hello' } }), {
|
|
307
|
+
id: 1,
|
|
308
|
+
title: 'Hello from hook'
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
same(await entity.find({ where: { id: { eq: 1 } }, fields: ['id', 'title'] }), [])
|
|
312
|
+
|
|
313
|
+
same(await entity.save({ input: { id: 1, title: 'Hello World' } }), {
|
|
314
|
+
id: 1,
|
|
315
|
+
title: 'Hello from hook 2'
|
|
316
|
+
})
|
|
317
|
+
|
|
318
|
+
await entity.insert({ inputs: [{ title: 'hello' }, { title: 'world' }], fields: ['id', 'title'] })
|
|
319
|
+
end()
|
|
320
|
+
})
|
package/test/where.test.js
CHANGED
|
@@ -388,6 +388,170 @@ test('foreign keys', async ({ pass, teardown, same, equal }) => {
|
|
|
388
388
|
}
|
|
389
389
|
})
|
|
390
390
|
|
|
391
|
+
test('limit should be 10 by default 100 at max', async ({ pass, teardown, same, fail, match }) => {
|
|
392
|
+
const mapper = await connect({
|
|
393
|
+
...connInfo,
|
|
394
|
+
log: fakeLogger,
|
|
395
|
+
async onDatabaseLoad (db, sql) {
|
|
396
|
+
teardown(() => db.dispose())
|
|
397
|
+
pass('onDatabaseLoad called')
|
|
398
|
+
|
|
399
|
+
await clear(db, sql)
|
|
400
|
+
|
|
401
|
+
if (isSQLite) {
|
|
402
|
+
await db.query(sql`CREATE TABLE posts (
|
|
403
|
+
id INTEGER PRIMARY KEY,
|
|
404
|
+
title VARCHAR(42),
|
|
405
|
+
long_text TEXT,
|
|
406
|
+
counter INTEGER
|
|
407
|
+
);`)
|
|
408
|
+
} else {
|
|
409
|
+
await db.query(sql`CREATE TABLE posts (
|
|
410
|
+
id SERIAL PRIMARY KEY,
|
|
411
|
+
title VARCHAR(42),
|
|
412
|
+
long_text TEXT,
|
|
413
|
+
counter INTEGER
|
|
414
|
+
);`)
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
})
|
|
418
|
+
|
|
419
|
+
const entity = mapper.entities.post
|
|
420
|
+
|
|
421
|
+
const posts = []
|
|
422
|
+
|
|
423
|
+
for (let i = 0; i <= 105; i++) {
|
|
424
|
+
posts.push({
|
|
425
|
+
title: 'Dog',
|
|
426
|
+
longText: 'Foo',
|
|
427
|
+
counter: i
|
|
428
|
+
})
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
await entity.insert({
|
|
432
|
+
inputs: posts
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
const defaultLimit = 10
|
|
436
|
+
const max = 100
|
|
437
|
+
|
|
438
|
+
same(await (await entity.find()).length, defaultLimit)
|
|
439
|
+
|
|
440
|
+
same(await (await entity.find({ limit: 1 })).length, 1)
|
|
441
|
+
|
|
442
|
+
same(await (await entity.find({ offset: 3 })).length, defaultLimit)
|
|
443
|
+
|
|
444
|
+
same(await (await entity.find({ limit: 1, offset: 0 })).length, 1)
|
|
445
|
+
|
|
446
|
+
same(await (await entity.find({ limit: 0 })).length, 0)
|
|
447
|
+
|
|
448
|
+
try {
|
|
449
|
+
await entity.find({ limit: -1 })
|
|
450
|
+
fail('Expected error for limit not allowed value')
|
|
451
|
+
} catch (e) {
|
|
452
|
+
match(e, new Error('Param limit=-1 not allowed. It must be not negative value.'))
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
same(await (await entity.find({ limit: 1, offset: 0 })).length, 1)
|
|
456
|
+
|
|
457
|
+
try {
|
|
458
|
+
await entity.find({ limit: 1, offset: -1 })
|
|
459
|
+
fail('Expected error for offset not allowed value')
|
|
460
|
+
} catch (e) {
|
|
461
|
+
match(e, new Error('Param offset=-1 not allowed. It must be not negative value.'))
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
try {
|
|
465
|
+
await entity.find({ limit: 200 })
|
|
466
|
+
fail('Expected error for limit exceeding max allowed value')
|
|
467
|
+
} catch (e) {
|
|
468
|
+
match(e, new Error(`Param limit=200 not allowed. Max accepted value ${max}.`))
|
|
469
|
+
}
|
|
470
|
+
})
|
|
471
|
+
|
|
472
|
+
test('limit must accept custom configuration', async ({ pass, teardown, same, fail, match }) => {
|
|
473
|
+
const customLimitConf = {
|
|
474
|
+
default: 1,
|
|
475
|
+
max: 5
|
|
476
|
+
}
|
|
477
|
+
const mapper = await connect({
|
|
478
|
+
...connInfo,
|
|
479
|
+
log: fakeLogger,
|
|
480
|
+
async onDatabaseLoad (db, sql) {
|
|
481
|
+
teardown(() => db.dispose())
|
|
482
|
+
pass('onDatabaseLoad called')
|
|
483
|
+
|
|
484
|
+
await clear(db, sql)
|
|
485
|
+
|
|
486
|
+
if (isSQLite) {
|
|
487
|
+
await db.query(sql`CREATE TABLE posts (
|
|
488
|
+
id INTEGER PRIMARY KEY,
|
|
489
|
+
title VARCHAR(42),
|
|
490
|
+
long_text TEXT,
|
|
491
|
+
counter INTEGER
|
|
492
|
+
);`)
|
|
493
|
+
} else {
|
|
494
|
+
await db.query(sql`CREATE TABLE posts (
|
|
495
|
+
id SERIAL PRIMARY KEY,
|
|
496
|
+
title VARCHAR(42),
|
|
497
|
+
long_text TEXT,
|
|
498
|
+
counter INTEGER
|
|
499
|
+
);`)
|
|
500
|
+
}
|
|
501
|
+
},
|
|
502
|
+
limit: customLimitConf
|
|
503
|
+
})
|
|
504
|
+
|
|
505
|
+
const entity = mapper.entities.post
|
|
506
|
+
|
|
507
|
+
const posts = []
|
|
508
|
+
|
|
509
|
+
for (let i = 0; i <= 10; i++) {
|
|
510
|
+
posts.push({
|
|
511
|
+
title: 'Dog',
|
|
512
|
+
longText: 'Foo',
|
|
513
|
+
counter: i
|
|
514
|
+
})
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
await entity.insert({
|
|
518
|
+
inputs: posts
|
|
519
|
+
})
|
|
520
|
+
|
|
521
|
+
same(await (await entity.find()).length, customLimitConf.default)
|
|
522
|
+
|
|
523
|
+
same(await (await entity.find({ limit: 1 })).length, 1)
|
|
524
|
+
|
|
525
|
+
same(await (await entity.find({ offset: 3 })).length, customLimitConf.default)
|
|
526
|
+
|
|
527
|
+
same(await (await entity.find({ limit: 1, offset: 0 })).length, 1)
|
|
528
|
+
|
|
529
|
+
same(await (await entity.find({ limit: 0 })).length, 0)
|
|
530
|
+
|
|
531
|
+
try {
|
|
532
|
+
await entity.find({ limit: -1 })
|
|
533
|
+
fail('Expected error for limit not allowed value')
|
|
534
|
+
} catch (e) {
|
|
535
|
+
match(e, new Error('Param limit=-1 not allowed. It must be not negative value.'))
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
same(await (await entity.find({ limit: 1, offset: 0 })).length, 1)
|
|
539
|
+
|
|
540
|
+
try {
|
|
541
|
+
await entity.find({ limit: 1, offset: -1 })
|
|
542
|
+
fail('Expected error for offset not allowed value')
|
|
543
|
+
} catch (e) {
|
|
544
|
+
match(e, new Error('Param offset=-1 not allowed. It must be not negative value.'))
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
try {
|
|
548
|
+
await entity.find({ limit: 200 })
|
|
549
|
+
fail('Expected error for limit exceeding max allowed value')
|
|
550
|
+
} catch (e) {
|
|
551
|
+
match(e, new Error(`Param limit=200 not allowed. Max accepted value ${customLimitConf.max}.`))
|
|
552
|
+
}
|
|
553
|
+
})
|
|
554
|
+
|
|
391
555
|
test('is NULL', async ({ pass, teardown, same, equal }) => {
|
|
392
556
|
const mapper = await connect({
|
|
393
557
|
...connInfo,
|