@ditojs/server 0.272.0 → 0.275.0
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/app/Application.js +48 -8
- package/lib/app/SessionStore.js +3 -1
- package/lib/app/Validator.js +13 -1
- package/lib/app/index.js +7 -1
- package/lib/cli/console.js +14 -4
- package/lib/cli/db/createMigration.js +16 -4
- package/lib/cli/db/index.js +7 -1
- package/lib/cli/db/migrate.js +3 -3
- package/lib/cli/db/reset.js +3 -3
- package/lib/cli/db/rollback.js +3 -3
- package/lib/cli/db/seed.js +14 -6
- package/lib/cli/db/unlock.js +3 -3
- package/lib/cli/index.js +9 -5
- package/lib/controllers/AdminController.js +12 -2
- package/lib/controllers/CollectionController.js +15 -1
- package/lib/controllers/Controller.js +28 -4
- package/lib/controllers/ControllerAction.js +23 -14
- package/lib/controllers/RelationController.js +9 -3
- package/lib/controllers/UserController.js +15 -1
- package/lib/controllers/index.js +7 -1
- package/lib/decorators/index.js +7 -1
- package/lib/decorators/parameters.js +3 -1
- package/lib/decorators/returns.js +3 -1
- package/lib/errors/DatabaseError.js +5 -19
- package/lib/errors/ResponseError.js +4 -16
- package/lib/errors/index.js +7 -1
- package/lib/graph/DitoGraphProcessor.js +5 -1
- package/lib/graph/expression.js +5 -1
- package/lib/graph/graph.js +18 -2
- package/lib/graph/index.js +7 -1
- package/lib/index.js +7 -1
- package/lib/lib/index.js +7 -1
- package/lib/middleware/findRoute.js +7 -1
- package/lib/middleware/index.js +7 -1
- package/lib/middleware/logRequests.js +4 -4
- package/lib/mixins/AssetMixin.js +4 -4
- package/lib/mixins/SessionMixin.js +4 -4
- package/lib/mixins/TimeStampedMixin.js +4 -4
- package/lib/mixins/UserMixin.js +10 -4
- package/lib/mixins/index.js +7 -1
- package/lib/models/Model.js +21 -4
- package/lib/models/definitions/filters.js +9 -1
- package/lib/models/definitions/properties.js +7 -1
- package/lib/models/definitions/scopes.js +9 -1
- package/lib/models/index.js +7 -1
- package/lib/query/QueryBuilder.js +11 -1
- package/lib/query/QueryFilters.js +11 -1
- package/lib/query/index.js +7 -1
- package/lib/schema/formats/index.js +7 -1
- package/lib/schema/index.js +10 -2
- package/lib/schema/keywords/index.js +7 -1
- package/lib/schema/properties.js +11 -1
- package/lib/schema/relations.js +18 -4
- package/lib/services/index.js +7 -1
- package/lib/storage/DiskStorage.js +7 -1
- package/lib/storage/Storage.js +5 -1
- package/lib/storage/index.js +7 -1
- package/lib/utils/emitter.js +5 -1
- package/lib/utils/index.js +7 -1
- package/lib/utils/object.js +9 -3
- package/package.json +31 -31
- package/src/app/Application.js +39 -9
- package/src/cli/console.js +3 -3
- package/src/cli/db/createMigration.js +3 -3
- package/src/cli/db/migrate.js +4 -4
- package/src/cli/db/reset.js +4 -4
- package/src/cli/db/rollback.js +4 -4
- package/src/cli/db/seed.js +6 -6
- package/src/cli/db/unlock.js +2 -2
- package/src/cli/index.js +3 -3
- package/src/controllers/AdminController.js +1 -1
- package/src/controllers/Controller.js +8 -8
- package/src/controllers/ControllerAction.js +7 -13
- package/src/controllers/RelationController.js +2 -2
- package/src/errors/DatabaseError.js +2 -23
- package/src/errors/ResponseError.js +1 -9
- package/src/middleware/logRequests.js +9 -9
- package/src/models/Model.js +10 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ditojs/server",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.275.0",
|
|
4
4
|
"description": "Dito.js Server – Dito.js is a declarative and modern web framework, based on Objection.js, Koa.js and Vue.js",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"repository": "https://github.com/ditojs/dito/tree/master/packages/server",
|
|
@@ -25,34 +25,33 @@
|
|
|
25
25
|
"yarn": ">= 1.0.0"
|
|
26
26
|
},
|
|
27
27
|
"browserslist": [
|
|
28
|
-
"node 14"
|
|
28
|
+
"node >= 14"
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@babel/core": "^7.
|
|
32
|
-
"@babel/runtime": "^7.
|
|
33
|
-
"@ditojs/admin": "^0.
|
|
34
|
-
"@ditojs/router": "^0.
|
|
35
|
-
"@ditojs/utils": "^0.
|
|
31
|
+
"@babel/core": "^7.17.5",
|
|
32
|
+
"@babel/runtime": "^7.17.2",
|
|
33
|
+
"@ditojs/admin": "^0.275.0",
|
|
34
|
+
"@ditojs/router": "^0.275.0",
|
|
35
|
+
"@ditojs/utils": "^0.275.0",
|
|
36
36
|
"@koa/cors": "^3.1.0",
|
|
37
37
|
"@koa/multer": "^3.0.0",
|
|
38
|
-
"@vue/cli-service": "^4.5.
|
|
38
|
+
"@vue/cli-service": "^4.5.15",
|
|
39
39
|
"ajv": "^7.2.4",
|
|
40
40
|
"ajv-formats": "^1.6.1",
|
|
41
|
-
"aws-sdk": "^2.
|
|
42
|
-
"axios": "^0.
|
|
43
|
-
"babel-loader": "^8.2.
|
|
41
|
+
"aws-sdk": "^2.1087.0",
|
|
42
|
+
"axios": "^0.26.0",
|
|
43
|
+
"babel-loader": "^8.2.3",
|
|
44
44
|
"bcryptjs": "^2.4.3",
|
|
45
|
-
"bytes": "^3.1.
|
|
46
|
-
"
|
|
47
|
-
"core-js": "^3.18.1",
|
|
45
|
+
"bytes": "^3.1.2",
|
|
46
|
+
"core-js": "^3.21.1",
|
|
48
47
|
"data-uri-to-buffer": "^3.0.1",
|
|
49
|
-
"eventemitter2": "^6.4.
|
|
48
|
+
"eventemitter2": "^6.4.5",
|
|
50
49
|
"file-type": "^16.5.3",
|
|
51
|
-
"fs-extra": "^10.0.
|
|
50
|
+
"fs-extra": "^10.0.1",
|
|
52
51
|
"html-webpack-tags-plugin": "^2.0.17",
|
|
53
|
-
"image-size": "^1.0.
|
|
54
|
-
"is-svg": "^4.3.
|
|
55
|
-
"koa": "^2.13.
|
|
52
|
+
"image-size": "^1.0.1",
|
|
53
|
+
"is-svg": "^4.3.2",
|
|
54
|
+
"koa": "^2.13.4",
|
|
56
55
|
"koa-bodyparser": "^4.3.0",
|
|
57
56
|
"koa-compose": "^4.1.0",
|
|
58
57
|
"koa-compress": "^5.1.0",
|
|
@@ -66,14 +65,15 @@
|
|
|
66
65
|
"koa-session": "^6.2.0",
|
|
67
66
|
"koa-static": "^5.0.0",
|
|
68
67
|
"koa-webpack": "^6.0.0",
|
|
69
|
-
"mime-types": "^2.1.
|
|
70
|
-
"multer": "^1.4.
|
|
71
|
-
"multer-s3": "^2.
|
|
72
|
-
"nanoid": "^3.1
|
|
73
|
-
"parse-duration": "^1.0.
|
|
68
|
+
"mime-types": "^2.1.34",
|
|
69
|
+
"multer": "^1.4.4",
|
|
70
|
+
"multer-s3": "^2.10.0",
|
|
71
|
+
"nanoid": "^3.3.1",
|
|
72
|
+
"parse-duration": "^1.0.2",
|
|
74
73
|
"passport-local": "^1.0.0",
|
|
75
74
|
"passthrough-counter": "^1.0.0",
|
|
76
|
-
"
|
|
75
|
+
"picocolors": "^1.0.0",
|
|
76
|
+
"pino": "^6.14.0",
|
|
77
77
|
"pino-pretty": "^6.0.0",
|
|
78
78
|
"pluralize": "^8.0.0",
|
|
79
79
|
"repl": "^0.1.3",
|
|
@@ -89,16 +89,16 @@
|
|
|
89
89
|
"objection": "^2.2.0"
|
|
90
90
|
},
|
|
91
91
|
"devDependencies": {
|
|
92
|
-
"@babel/cli": "^7.
|
|
93
|
-
"@babel/preset-env": "^7.
|
|
94
|
-
"@ditojs/babel-preset": "^0.
|
|
92
|
+
"@babel/cli": "^7.17.6",
|
|
93
|
+
"@babel/preset-env": "^7.16.11",
|
|
94
|
+
"@ditojs/babel-preset": "^0.275.0",
|
|
95
95
|
"babel-plugin-dynamic-import-node": "^2.3.3",
|
|
96
96
|
"babel-plugin-module-resolver": "^4.1.0",
|
|
97
97
|
"knex": "^0.21.21",
|
|
98
|
-
"objection": "^2.2.
|
|
99
|
-
"pg": "^8.7.
|
|
98
|
+
"objection": "^2.2.18",
|
|
99
|
+
"pg": "^8.7.3",
|
|
100
100
|
"rimraf": "^3.0.2",
|
|
101
101
|
"sqlite3": "^5.0.2"
|
|
102
102
|
},
|
|
103
|
-
"gitHead": "
|
|
103
|
+
"gitHead": "b1ee07dad73b0e3408cd90798deb7608c8ef096d"
|
|
104
104
|
}
|
package/src/app/Application.js
CHANGED
|
@@ -2,7 +2,7 @@ import Koa from 'koa'
|
|
|
2
2
|
import Knex from 'knex'
|
|
3
3
|
import util from 'util'
|
|
4
4
|
import axios from 'axios'
|
|
5
|
-
import
|
|
5
|
+
import pico from 'picocolors'
|
|
6
6
|
import zlib from 'zlib'
|
|
7
7
|
import pino from 'pino'
|
|
8
8
|
import os from 'os'
|
|
@@ -24,7 +24,12 @@ import { Service } from '@/services'
|
|
|
24
24
|
import { Storage } from '@/storage'
|
|
25
25
|
import { convertSchema } from '@/schema'
|
|
26
26
|
import { formatJson } from '@/utils'
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
ResponseError,
|
|
29
|
+
ValidationError,
|
|
30
|
+
DatabaseError,
|
|
31
|
+
AssetError
|
|
32
|
+
} from '@/errors'
|
|
28
33
|
import SessionStore from './SessionStore'
|
|
29
34
|
import { Validator } from './Validator'
|
|
30
35
|
import {
|
|
@@ -158,7 +163,7 @@ export class Application extends Koa {
|
|
|
158
163
|
}
|
|
159
164
|
if (Object.keys(data).length > 0) {
|
|
160
165
|
console.info(
|
|
161
|
-
|
|
166
|
+
pico.yellow.bold(`\n${modelClass.name}:\n`),
|
|
162
167
|
util.inspect(data, {
|
|
163
168
|
colors: true,
|
|
164
169
|
depth: null,
|
|
@@ -473,11 +478,26 @@ export class Application extends Koa {
|
|
|
473
478
|
}
|
|
474
479
|
}
|
|
475
480
|
|
|
476
|
-
createValidationError({ type, message, errors, options }) {
|
|
481
|
+
createValidationError({ type, message, errors, options, json }) {
|
|
477
482
|
return new ValidationError({
|
|
478
483
|
type,
|
|
479
484
|
message,
|
|
480
|
-
errors: this.validator.parseErrors(errors, options)
|
|
485
|
+
errors: this.validator.parseErrors(errors, options),
|
|
486
|
+
// Only include the JSON data in the error if `log.errors.json`is set.
|
|
487
|
+
json: this.config.log.errors?.json ? json : undefined
|
|
488
|
+
})
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
createDatabaseError(error) {
|
|
492
|
+
// Remove knex SQL query and move to separate `sql` property.
|
|
493
|
+
// TODO: Fix this properly in Knex / Objection instead, see:
|
|
494
|
+
// https://gitter.im/Vincit/objection.js?at=5a68728f5a9ebe4f75ca40b0
|
|
495
|
+
const [, sql, message] = error.message.match(/^([\s\S]*) - ([\s\S]*?)$/) ||
|
|
496
|
+
[null, null, error.message]
|
|
497
|
+
return new DatabaseError(error, {
|
|
498
|
+
message,
|
|
499
|
+
// Only include the SQL query in the error if `log.errors.sql`is set.
|
|
500
|
+
sql: this.config.log.errors?.sql ? sql : undefined
|
|
481
501
|
})
|
|
482
502
|
}
|
|
483
503
|
|
|
@@ -608,6 +628,16 @@ export class Application extends Koa {
|
|
|
608
628
|
}
|
|
609
629
|
}
|
|
610
630
|
this.knex = Knex(knex)
|
|
631
|
+
// Support PostgreSQL type parser mappings in the config.
|
|
632
|
+
if (
|
|
633
|
+
knex.client === 'postgresql' &&
|
|
634
|
+
knex.typeParsers &&
|
|
635
|
+
this.knex.client.driver
|
|
636
|
+
) {
|
|
637
|
+
for (const [type, parser] of Object.entries(knex.typeParsers)) {
|
|
638
|
+
this.knex.client.driver.types.setTypeParser(type, parser)
|
|
639
|
+
}
|
|
640
|
+
}
|
|
611
641
|
if (log.sql) {
|
|
612
642
|
this.setupKnexLogging()
|
|
613
643
|
}
|
|
@@ -826,13 +856,13 @@ export class Application extends Koa {
|
|
|
826
856
|
if (!data) {
|
|
827
857
|
console.info(
|
|
828
858
|
`${
|
|
829
|
-
|
|
859
|
+
pico.red('INFO:')
|
|
830
860
|
} Asset ${
|
|
831
|
-
|
|
861
|
+
pico.green(`'${file.name}'`)
|
|
832
862
|
} is from a foreign source, fetching from ${
|
|
833
|
-
|
|
863
|
+
pico.green(`'${file.url}'`)
|
|
834
864
|
} and adding to storage ${
|
|
835
|
-
|
|
865
|
+
pico.green(`'${storage.name}'`)
|
|
836
866
|
}...`
|
|
837
867
|
)
|
|
838
868
|
const response = await axios.request({
|
package/src/cli/console.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import repl from 'repl'
|
|
2
2
|
import path from 'path'
|
|
3
3
|
import fs from 'fs-extra'
|
|
4
|
-
import
|
|
4
|
+
import pico from 'picocolors'
|
|
5
5
|
import objection from 'objection'
|
|
6
6
|
import { isFunction, deindent } from '@ditojs/utils'
|
|
7
7
|
|
|
@@ -100,11 +100,11 @@ function displayUsage(app, config, details) {
|
|
|
100
100
|
Dito Console
|
|
101
101
|
|
|
102
102
|
Available references:
|
|
103
|
-
- Dito app: ${
|
|
103
|
+
- Dito app: ${pico.cyan('app')}
|
|
104
104
|
${
|
|
105
105
|
modelHandleNames.length > 0
|
|
106
106
|
? ` - Dito models: ${
|
|
107
|
-
modelHandleNames.map(m =>
|
|
107
|
+
modelHandleNames.map(m => pico.cyan(m)).join(', ')
|
|
108
108
|
}`
|
|
109
109
|
: ''
|
|
110
110
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
import fs from 'fs-extra'
|
|
3
|
-
import
|
|
3
|
+
import pico from 'picocolors'
|
|
4
4
|
import { getRelationClass, isThroughRelationClass } from '@/schema'
|
|
5
5
|
import {
|
|
6
6
|
isObject, isArray, isString, deindent, capitalize
|
|
@@ -52,7 +52,7 @@ export async function createMigration(app, name, ...modelNames) {
|
|
|
52
52
|
const file = path.join(migrationDir, filename)
|
|
53
53
|
if (await fs.exists(file)) {
|
|
54
54
|
// This should never happen, but let's be on the safe side here:
|
|
55
|
-
console.info(
|
|
55
|
+
console.info(pico.red(`Migration '${filename}' already exists.`))
|
|
56
56
|
return false
|
|
57
57
|
} else {
|
|
58
58
|
await fs.writeFile(file, deindent`
|
|
@@ -64,7 +64,7 @@ export async function createMigration(app, name, ...modelNames) {
|
|
|
64
64
|
${getCode(dropTables)}
|
|
65
65
|
}
|
|
66
66
|
`)
|
|
67
|
-
console.info(
|
|
67
|
+
console.info(pico.cyan(`Migration '${filename}' successfully created.`))
|
|
68
68
|
return true
|
|
69
69
|
}
|
|
70
70
|
}
|
package/src/cli/db/migrate.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import pico from 'picocolors'
|
|
2
2
|
|
|
3
3
|
export async function migrate(knex) {
|
|
4
4
|
const [batch, log] = await knex.migrate.latest()
|
|
5
5
|
console.info(log.length === 0
|
|
6
|
-
?
|
|
7
|
-
:
|
|
8
|
-
|
|
6
|
+
? pico.cyan('Already up to date')
|
|
7
|
+
: pico.green(`Batch ${batch} run: ${log.length} migrations\n`) +
|
|
8
|
+
pico.cyan(log.join('\n')))
|
|
9
9
|
return true
|
|
10
10
|
}
|
package/src/cli/db/reset.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import pico from 'picocolors'
|
|
2
2
|
import { migrate } from './migrate'
|
|
3
3
|
|
|
4
4
|
export async function reset(knex) {
|
|
@@ -11,10 +11,10 @@ export async function reset(knex) {
|
|
|
11
11
|
migrations.push(...log)
|
|
12
12
|
}
|
|
13
13
|
console.info(migrations.length === 0
|
|
14
|
-
?
|
|
15
|
-
:
|
|
14
|
+
? pico.cyan('Already at the base migration')
|
|
15
|
+
: pico.green(`${batches.length > 1 ? 'Batches' : 'Batch'} ${batches} ` +
|
|
16
16
|
`rolled back: ${migrations.length} migrations\n`) +
|
|
17
|
-
|
|
17
|
+
pico.cyan(migrations.join('\n')))
|
|
18
18
|
await migrate(knex)
|
|
19
19
|
return true
|
|
20
20
|
}
|
package/src/cli/db/rollback.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import pico from 'picocolors'
|
|
2
2
|
|
|
3
3
|
export async function rollback(knex) {
|
|
4
4
|
const [batch, log] = await knex.migrate.rollback()
|
|
5
5
|
console.info(log.length === 0
|
|
6
|
-
?
|
|
7
|
-
:
|
|
8
|
-
|
|
6
|
+
? pico.cyan('Already at the base migration')
|
|
7
|
+
: pico.green(`Batch ${batch} rolled back: ${log.length} migrations\n`) +
|
|
8
|
+
pico.cyan(log.join('\n')))
|
|
9
9
|
return true
|
|
10
10
|
}
|
package/src/cli/db/seed.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from 'path'
|
|
2
2
|
import fs from 'fs-extra'
|
|
3
|
-
import
|
|
3
|
+
import pico from 'picocolors'
|
|
4
4
|
import util from 'util'
|
|
5
5
|
import pluralize from 'pluralize'
|
|
6
6
|
import { isFunction, isArray, camelize } from '@ditojs/utils'
|
|
@@ -57,18 +57,18 @@ async function handleSeed(app, base, seed, modelClass) {
|
|
|
57
57
|
}
|
|
58
58
|
if (isArray(res)) {
|
|
59
59
|
console.info(
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
pico.green(`${base}:`),
|
|
61
|
+
pico.cyan(`${res.length} seed records created.`)
|
|
62
62
|
)
|
|
63
63
|
} else {
|
|
64
64
|
console.info(
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
pico.red(`${base}:`),
|
|
66
|
+
pico.cyan('No seed records created.')
|
|
67
67
|
)
|
|
68
68
|
}
|
|
69
69
|
} catch (err) {
|
|
70
70
|
console.error(
|
|
71
|
-
|
|
71
|
+
pico.red(`${base}:`),
|
|
72
72
|
util.inspect(err, {
|
|
73
73
|
colors: true,
|
|
74
74
|
depth: null,
|
package/src/cli/db/unlock.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import pico from 'picocolors'
|
|
2
2
|
|
|
3
3
|
export async function unlock(knex) {
|
|
4
4
|
await knex.migrate.forceFreeMigrationsLock()
|
|
5
5
|
console.info(
|
|
6
|
-
|
|
6
|
+
pico.green(`Successfully unlocked the migrations lock table`)
|
|
7
7
|
)
|
|
8
8
|
return true
|
|
9
9
|
}
|
package/src/cli/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env babel-node
|
|
2
2
|
|
|
3
3
|
import path from 'path'
|
|
4
|
-
import
|
|
4
|
+
import pico from 'picocolors'
|
|
5
5
|
import Knex from 'knex'
|
|
6
6
|
import { isPlainObject, isFunction, camelize } from '@ditojs/utils'
|
|
7
7
|
import * as db from './db'
|
|
@@ -47,10 +47,10 @@ async function execute() {
|
|
|
47
47
|
} catch (err) {
|
|
48
48
|
if (err instanceof Error) {
|
|
49
49
|
console.error(
|
|
50
|
-
|
|
50
|
+
pico.red(`${err.detail ? `${err.detail}\n` : ''}${err.stack}`)
|
|
51
51
|
)
|
|
52
52
|
} else {
|
|
53
|
-
console.error(
|
|
53
|
+
console.error(pico.red(err))
|
|
54
54
|
}
|
|
55
55
|
process.exit(1)
|
|
56
56
|
}
|
|
@@ -60,7 +60,7 @@ export class AdminController extends Controller {
|
|
|
60
60
|
sendDitoObject(ctx) {
|
|
61
61
|
// Send back the global dito object as JavaScript code.
|
|
62
62
|
ctx.type = 'text/javascript'
|
|
63
|
-
ctx.body = `window.dito = ${formatJson(this.getDitoObject()
|
|
63
|
+
ctx.body = `window.dito = ${formatJson(this.getDitoObject())}`
|
|
64
64
|
}
|
|
65
65
|
|
|
66
66
|
middleware() {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import pico from 'picocolors'
|
|
2
2
|
import { getOwnProperty, getAllKeys, describeFunction } from '@/utils'
|
|
3
3
|
import { EventEmitter } from '@/lib'
|
|
4
4
|
import ControllerAction from './ControllerAction'
|
|
@@ -44,11 +44,11 @@ export class Controller {
|
|
|
44
44
|
this.url = namespace ? `/${namespace}${url}` : url
|
|
45
45
|
this.log(
|
|
46
46
|
`${
|
|
47
|
-
namespace ?
|
|
47
|
+
namespace ? pico.green(`/${namespace}/`) : ''
|
|
48
48
|
}${
|
|
49
|
-
|
|
49
|
+
pico.cyan(path)
|
|
50
50
|
}${
|
|
51
|
-
|
|
51
|
+
pico.white(':')
|
|
52
52
|
}`,
|
|
53
53
|
this.level
|
|
54
54
|
)
|
|
@@ -92,13 +92,13 @@ export class Controller {
|
|
|
92
92
|
setupRoute(verb, url, transacted, authorize, action, handlers) {
|
|
93
93
|
this.log(
|
|
94
94
|
`${
|
|
95
|
-
|
|
95
|
+
pico.magenta(verb.toUpperCase())
|
|
96
96
|
} ${
|
|
97
|
-
|
|
97
|
+
pico.green(this.url)
|
|
98
98
|
}${
|
|
99
|
-
|
|
99
|
+
pico.cyan(url.slice(this.url.length))
|
|
100
100
|
} ${
|
|
101
|
-
|
|
101
|
+
pico.white(this.describeAuthorize(authorize))
|
|
102
102
|
}`,
|
|
103
103
|
this.level + 1
|
|
104
104
|
)
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { formatJson } from '@/utils'
|
|
2
1
|
import { isString, isObject, asArray, clone } from '@ditojs/utils'
|
|
3
2
|
|
|
4
3
|
export default class ControllerAction {
|
|
@@ -166,8 +165,9 @@ export default class ControllerAction {
|
|
|
166
165
|
if (errors.length > 0) {
|
|
167
166
|
throw this.createValidationError({
|
|
168
167
|
type: 'ParameterValidation',
|
|
169
|
-
message:
|
|
170
|
-
errors
|
|
168
|
+
message: 'The provided action parameters are not valid',
|
|
169
|
+
errors,
|
|
170
|
+
json: getData()
|
|
171
171
|
})
|
|
172
172
|
}
|
|
173
173
|
}
|
|
@@ -188,17 +188,11 @@ export default class ControllerAction {
|
|
|
188
188
|
await this.returns.validate(data)
|
|
189
189
|
return getResult()
|
|
190
190
|
} catch (error) {
|
|
191
|
-
// Include full JSON result in responses during tests and development,
|
|
192
|
-
// for easier debugging.
|
|
193
|
-
const message =
|
|
194
|
-
process.env.NODE_ENV === 'test' ||
|
|
195
|
-
process.env.NODE_ENV === 'development'
|
|
196
|
-
? `Invalid result of action: ${formatJson(getResult())}`
|
|
197
|
-
: 'Invalid result of action'
|
|
198
191
|
throw this.createValidationError({
|
|
199
192
|
type: 'ResultValidation',
|
|
200
|
-
message,
|
|
201
|
-
errors: error.errors
|
|
193
|
+
message: 'The returned action result is not valid',
|
|
194
|
+
errors: error.errors,
|
|
195
|
+
json: getResult()
|
|
202
196
|
})
|
|
203
197
|
}
|
|
204
198
|
}
|
|
@@ -237,7 +231,7 @@ export default class ControllerAction {
|
|
|
237
231
|
|
|
238
232
|
coerceValue(type, value, modelOptions) {
|
|
239
233
|
// See if param needs additional coercion:
|
|
240
|
-
if (['date', 'datetime', 'timestamp'].includes(type)) {
|
|
234
|
+
if (value && ['date', 'datetime', 'timestamp'].includes(type)) {
|
|
241
235
|
value = new Date(value)
|
|
242
236
|
} else {
|
|
243
237
|
// See if the defined type(s) require coercion to objects:
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import pico from 'picocolors'
|
|
2
2
|
import { ControllerError } from '@/errors'
|
|
3
3
|
import { CollectionController } from './CollectionController'
|
|
4
4
|
import { setupPropertyInheritance, getScope } from '@/utils'
|
|
@@ -32,7 +32,7 @@ export class RelationController extends CollectionController {
|
|
|
32
32
|
// Initialize:
|
|
33
33
|
this.path = this.app.normalizePath(this.name)
|
|
34
34
|
this.url = `${this.parent.url}/${this.parent.getPath('member', this.path)}`
|
|
35
|
-
this.log(`${
|
|
35
|
+
this.log(`${pico.blue(this.path)}${pico.white(':')}`, this.level)
|
|
36
36
|
// Copy over all fields in the relation object except the ones that are
|
|
37
37
|
// going to be inherited in `setup()` (relation, member, allow), for
|
|
38
38
|
// settings like scope, etc.
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from 'objection'
|
|
9
9
|
|
|
10
10
|
export class DatabaseError extends WrappedError {
|
|
11
|
-
constructor(error) {
|
|
11
|
+
constructor(error, overrides) {
|
|
12
12
|
const status =
|
|
13
13
|
error instanceof CheckViolationError ? 400
|
|
14
14
|
: error instanceof NotNullViolationError ? 400
|
|
@@ -16,28 +16,7 @@ export class DatabaseError extends WrappedError {
|
|
|
16
16
|
: error instanceof DataError ? 400
|
|
17
17
|
: error instanceof DBError ? 500
|
|
18
18
|
: 400
|
|
19
|
-
|
|
20
|
-
// TODO: Fix this properly in Knex / Objection instead, see:
|
|
21
|
-
// https://gitter.im/Vincit/objection.js?at=5a68728f5a9ebe4f75ca40b0
|
|
22
|
-
const [, sql, message] = error.message.match(/^([\s\S]*) - ([\s\S]*?)$/) ||
|
|
23
|
-
[null, null, error.message]
|
|
24
|
-
const overrides = {
|
|
25
|
-
type: error.constructor.name,
|
|
26
|
-
message,
|
|
27
|
-
sql,
|
|
28
|
-
status
|
|
29
|
-
}
|
|
19
|
+
overrides = { type: error.constructor.name, status, ...overrides }
|
|
30
20
|
super(error, overrides, { message: 'Database error', status })
|
|
31
21
|
}
|
|
32
|
-
|
|
33
|
-
toJSON() {
|
|
34
|
-
// Remove SQL query from displayed data in front-end when not in development
|
|
35
|
-
// or test.
|
|
36
|
-
if (process.env.NODE_ENV !== 'development' ||
|
|
37
|
-
process.env.NODE_ENV !== 'test') {
|
|
38
|
-
const { sql, ...data } = this.data
|
|
39
|
-
return data
|
|
40
|
-
}
|
|
41
|
-
return this.data
|
|
42
|
-
}
|
|
43
22
|
}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { isPlainObject, isString } from '@ditojs/utils'
|
|
2
|
-
import { formatJson } from '@/utils'
|
|
3
2
|
|
|
4
3
|
export class ResponseError extends Error {
|
|
5
4
|
constructor(error, defaults = { message: 'Response error', status: 400 }) {
|
|
@@ -22,14 +21,7 @@ export class ResponseError extends Error {
|
|
|
22
21
|
? { message: error }
|
|
23
22
|
: error || {}
|
|
24
23
|
const { status, ...data } = { ...defaults, ...object }
|
|
25
|
-
|
|
26
|
-
if (process.env.NODE_ENV === 'test' && error === object) {
|
|
27
|
-
// Include full JSON error in message during tests, for better reporting.
|
|
28
|
-
const { message: _, ...rest } = data
|
|
29
|
-
if (Object.keys(rest).length > 0) {
|
|
30
|
-
message = `${message}\nError Data:\n${formatJson(rest)}`
|
|
31
|
-
}
|
|
32
|
-
}
|
|
24
|
+
const { message, code } = data
|
|
33
25
|
super(message)
|
|
34
26
|
this.name = this.constructor.name
|
|
35
27
|
this.status = status
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Middleware inspired by 'koa-logger'. Adapted and extended to our needs.
|
|
3
3
|
*/
|
|
4
4
|
import bytes from 'bytes'
|
|
5
|
-
import
|
|
5
|
+
import pico from 'picocolors'
|
|
6
6
|
import Counter from 'passthrough-counter'
|
|
7
7
|
|
|
8
8
|
export function logRequests({ ignoreUrls } = {}) {
|
|
@@ -58,11 +58,11 @@ function logRequest(ctx) {
|
|
|
58
58
|
logger.trace(
|
|
59
59
|
{ req: ctx.req },
|
|
60
60
|
`${
|
|
61
|
-
|
|
61
|
+
pico.gray('<--')
|
|
62
62
|
} ${
|
|
63
|
-
|
|
63
|
+
pico.bold(ctx.method)
|
|
64
64
|
} ${
|
|
65
|
-
|
|
65
|
+
pico.gray(ctx.originalUrl)
|
|
66
66
|
}`
|
|
67
67
|
)
|
|
68
68
|
}
|
|
@@ -93,15 +93,15 @@ function logResponse({ ctx, start, length, err }) {
|
|
|
93
93
|
logger[level](
|
|
94
94
|
{ req: ctx.req, res: ctx.res },
|
|
95
95
|
`${
|
|
96
|
-
|
|
96
|
+
pico.bold(ctx.method)
|
|
97
97
|
} ${
|
|
98
|
-
|
|
98
|
+
pico.gray(ctx.originalUrl)
|
|
99
99
|
} ${
|
|
100
|
-
|
|
100
|
+
pico[statusColor](status)
|
|
101
101
|
} ${
|
|
102
|
-
|
|
102
|
+
pico.gray(formattedTime)
|
|
103
103
|
} ${
|
|
104
|
-
|
|
104
|
+
pico.gray(formattedLength)
|
|
105
105
|
}`
|
|
106
106
|
)
|
|
107
107
|
}
|
package/src/models/Model.js
CHANGED
|
@@ -5,8 +5,11 @@ import { convertSchema, addRelationSchemas, convertRelations } from '@/schema'
|
|
|
5
5
|
import { populateGraph, filterGraph } from '@/graph'
|
|
6
6
|
import { formatJson } from '@/utils'
|
|
7
7
|
import {
|
|
8
|
-
ResponseError,
|
|
9
|
-
|
|
8
|
+
ResponseError,
|
|
9
|
+
GraphError, ModelError,
|
|
10
|
+
NotFoundError,
|
|
11
|
+
RelationError,
|
|
12
|
+
WrappedError
|
|
10
13
|
} from '@/errors'
|
|
11
14
|
import {
|
|
12
15
|
isString, isObject, isArray, isFunction, isPromise, asArray, merge, flatten,
|
|
@@ -223,7 +226,7 @@ export class Model extends objection.Model {
|
|
|
223
226
|
return super.query(trx).onError(err => {
|
|
224
227
|
// TODO: Shouldn't this wrapping happen on the Controller level?
|
|
225
228
|
err = err instanceof ResponseError ? err
|
|
226
|
-
: err instanceof objection.DBError ?
|
|
229
|
+
: err instanceof objection.DBError ? this.app.createDatabaseError(err)
|
|
227
230
|
: new WrappedError(err)
|
|
228
231
|
return Promise.reject(err)
|
|
229
232
|
})
|
|
@@ -730,12 +733,11 @@ export class Model extends objection.Model {
|
|
|
730
733
|
case 'ModelValidation':
|
|
731
734
|
return this.app.createValidationError({
|
|
732
735
|
type,
|
|
733
|
-
message:
|
|
734
|
-
`The provided data for the ${this.name} model is not valid
|
|
735
|
-
formatJson(json)
|
|
736
|
-
}`,
|
|
736
|
+
message:
|
|
737
|
+
message || `The provided data for the ${this.name} model is not valid`,
|
|
737
738
|
errors,
|
|
738
|
-
options
|
|
739
|
+
options,
|
|
740
|
+
json
|
|
739
741
|
})
|
|
740
742
|
case 'RelationExpression':
|
|
741
743
|
case 'UnallowedRelation':
|