@ditojs/server 2.0.4 → 2.1.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/package.json +11 -13
- package/src/app/Application.js +228 -212
- package/src/app/Validator.js +53 -43
- package/src/cli/console.js +6 -4
- package/src/cli/db/createMigration.js +59 -30
- package/src/cli/db/migrate.js +6 -4
- package/src/cli/db/reset.js +8 -5
- package/src/cli/db/rollback.js +6 -4
- package/src/cli/db/seed.js +2 -1
- package/src/cli/index.js +1 -1
- package/src/controllers/AdminController.js +100 -84
- package/src/controllers/CollectionController.js +37 -30
- package/src/controllers/Controller.js +83 -43
- package/src/controllers/ControllerAction.js +27 -15
- package/src/controllers/ModelController.js +4 -1
- package/src/controllers/RelationController.js +19 -21
- package/src/controllers/UsersController.js +3 -4
- package/src/decorators/parameters.js +3 -1
- package/src/decorators/scope.js +1 -1
- package/src/errors/ControllerError.js +2 -1
- package/src/errors/DatabaseError.js +20 -11
- package/src/graph/DitoGraphProcessor.js +48 -40
- package/src/graph/expression.js +6 -8
- package/src/graph/graph.js +20 -11
- package/src/lib/EventEmitter.js +12 -12
- package/src/middleware/handleConnectMiddleware.js +16 -10
- package/src/middleware/handleError.js +6 -5
- package/src/middleware/handleSession.js +78 -0
- package/src/middleware/handleUser.js +2 -2
- package/src/middleware/index.js +2 -0
- package/src/middleware/logRequests.js +3 -3
- package/src/middleware/setupRequestStorage.js +14 -0
- package/src/mixins/AssetMixin.js +62 -58
- package/src/mixins/SessionMixin.js +13 -10
- package/src/mixins/TimeStampedMixin.js +33 -29
- package/src/mixins/UserMixin.js +130 -116
- package/src/models/Model.js +245 -194
- package/src/models/definitions/filters.js +14 -13
- package/src/query/QueryBuilder.js +252 -195
- package/src/query/QueryFilters.js +3 -3
- package/src/query/QueryParameters.js +2 -2
- package/src/query/Registry.js +8 -10
- package/src/schema/keywords/_validate.js +10 -8
- package/src/schema/properties.test.js +247 -206
- package/src/schema/relations.js +42 -20
- package/src/schema/relations.test.js +36 -19
- package/src/services/Service.js +8 -14
- package/src/storage/S3Storage.js +5 -3
- package/src/storage/Storage.js +16 -14
- package/src/utils/function.js +7 -4
- package/src/utils/function.test.js +30 -6
- package/src/utils/object.test.js +5 -1
- package/types/index.d.ts +244 -257
- package/src/app/SessionStore.js +0 -31
package/src/schema/relations.js
CHANGED
|
@@ -7,7 +7,12 @@ import {
|
|
|
7
7
|
ManyToManyRelation
|
|
8
8
|
} from 'objection'
|
|
9
9
|
import {
|
|
10
|
-
isObject,
|
|
10
|
+
isObject,
|
|
11
|
+
isArray,
|
|
12
|
+
isString,
|
|
13
|
+
asArray,
|
|
14
|
+
capitalize,
|
|
15
|
+
camelize
|
|
11
16
|
} from '@ditojs/utils'
|
|
12
17
|
import { RelationError } from '../errors/index.js'
|
|
13
18
|
|
|
@@ -45,14 +50,16 @@ class ModelReference {
|
|
|
45
50
|
const modelClass = models[modelName]
|
|
46
51
|
if (!modelClass && !allowUnknown) {
|
|
47
52
|
throw new RelationError(
|
|
48
|
-
`Unknown model reference: ${ref}`
|
|
53
|
+
`Unknown model reference: ${ref}`
|
|
54
|
+
)
|
|
49
55
|
}
|
|
50
56
|
if (!this.modelName) {
|
|
51
57
|
this.modelName = modelName
|
|
52
58
|
this.modelClass = modelClass
|
|
53
59
|
} else if (this.modelName !== modelName) {
|
|
54
60
|
throw new RelationError(
|
|
55
|
-
`Composite keys need to be defined on the same table: ${ref}`
|
|
61
|
+
`Composite keys need to be defined on the same table: ${ref}`
|
|
62
|
+
)
|
|
56
63
|
}
|
|
57
64
|
this.propertyNames.push(propertyName)
|
|
58
65
|
}
|
|
@@ -90,8 +97,10 @@ class ModelReference {
|
|
|
90
97
|
const toName = toRef.modelName
|
|
91
98
|
const toProperties = toRef.propertyNames
|
|
92
99
|
if (fromProperties.length !== toProperties.length) {
|
|
93
|
-
throw new RelationError(
|
|
94
|
-
|
|
100
|
+
throw new RelationError(
|
|
101
|
+
'Unable to create through join for ' +
|
|
102
|
+
`composite keys from '${this}' to '${toRef}'`
|
|
103
|
+
)
|
|
95
104
|
}
|
|
96
105
|
const from = []
|
|
97
106
|
const to = []
|
|
@@ -106,7 +115,8 @@ class ModelReference {
|
|
|
106
115
|
to.push(`${throughName}.${throughTo}`)
|
|
107
116
|
} else {
|
|
108
117
|
throw new RelationError(
|
|
109
|
-
`Unable to create through join from '${this}' to '${to}'`
|
|
118
|
+
`Unable to create through join from '${this}' to '${to}'`
|
|
119
|
+
)
|
|
110
120
|
}
|
|
111
121
|
}
|
|
112
122
|
return {
|
|
@@ -136,11 +146,18 @@ export function convertRelation(schema, models) {
|
|
|
136
146
|
let {
|
|
137
147
|
relation,
|
|
138
148
|
// Dito.js-style relation description:
|
|
139
|
-
from,
|
|
149
|
+
from,
|
|
150
|
+
to,
|
|
151
|
+
through,
|
|
152
|
+
inverse,
|
|
153
|
+
modify,
|
|
154
|
+
scope,
|
|
140
155
|
// Objection.js-style relation description (`modify` is shared)
|
|
141
|
-
join,
|
|
156
|
+
join,
|
|
157
|
+
filter,
|
|
142
158
|
// Pluck Dito.js-related properties that should not end up in `rest`:
|
|
143
|
-
nullable,
|
|
159
|
+
nullable,
|
|
160
|
+
owner,
|
|
144
161
|
...rest
|
|
145
162
|
} = schema || {}
|
|
146
163
|
const relationClass = getRelationClass(relation)
|
|
@@ -178,16 +195,19 @@ export function convertRelation(schema, models) {
|
|
|
178
195
|
through.from = throughFrom.toValue()
|
|
179
196
|
through.to = throughTo.toValue()
|
|
180
197
|
} else {
|
|
181
|
-
throw new RelationError(
|
|
182
|
-
'
|
|
198
|
+
throw new RelationError(
|
|
199
|
+
'Both sides of the `through` definition ' +
|
|
200
|
+
'need to be on the same join model'
|
|
201
|
+
)
|
|
183
202
|
}
|
|
184
203
|
} else {
|
|
185
204
|
// Assume the references are to a join table, and use the settings
|
|
186
205
|
// unmodified.
|
|
187
206
|
}
|
|
188
207
|
} else {
|
|
189
|
-
throw new RelationError(
|
|
190
|
-
'`through.to` definition'
|
|
208
|
+
throw new RelationError(
|
|
209
|
+
'The relation needs a `through.from` and ' + '`through.to` definition'
|
|
210
|
+
)
|
|
191
211
|
}
|
|
192
212
|
} else if (through) {
|
|
193
213
|
throw new RelationError('Unsupported through join definition')
|
|
@@ -230,7 +250,8 @@ export function convertRelations(ownerModelClass, relations, models) {
|
|
|
230
250
|
converted[name] = convertRelation(relation, models)
|
|
231
251
|
} catch (err) {
|
|
232
252
|
throw new RelationError(
|
|
233
|
-
`${ownerModelClass.name}.relations.${name}: ${
|
|
253
|
+
`${ownerModelClass.name}.relations.${name}: ${err.message || err}`
|
|
254
|
+
)
|
|
234
255
|
}
|
|
235
256
|
}
|
|
236
257
|
return converted
|
|
@@ -261,15 +282,16 @@ export function addRelationSchemas(modelClass, properties) {
|
|
|
261
282
|
}
|
|
262
283
|
// Finally the model itself
|
|
263
284
|
anyOf.push({ $ref })
|
|
264
|
-
const items =
|
|
265
|
-
|
|
266
|
-
|
|
285
|
+
const items =
|
|
286
|
+
anyOf.length > 1
|
|
287
|
+
? { anyOf }
|
|
288
|
+
: anyOf[0]
|
|
267
289
|
properties[name] = isOneToOne
|
|
268
290
|
? items
|
|
269
291
|
: {
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
292
|
+
type: 'array',
|
|
293
|
+
items
|
|
294
|
+
}
|
|
273
295
|
}
|
|
274
296
|
return properties
|
|
275
297
|
}
|
|
@@ -7,7 +7,9 @@ import {
|
|
|
7
7
|
} from 'objection'
|
|
8
8
|
import { Model } from '../models/index.js'
|
|
9
9
|
import {
|
|
10
|
-
getRelationClass,
|
|
10
|
+
getRelationClass,
|
|
11
|
+
convertRelation,
|
|
12
|
+
addRelationSchemas
|
|
11
13
|
} from './relations.js'
|
|
12
14
|
|
|
13
15
|
describe('getRelationClass()', () => {
|
|
@@ -40,12 +42,17 @@ describe('convertRelation()', () => {
|
|
|
40
42
|
const models = { ModelOne, ModelTwo }
|
|
41
43
|
|
|
42
44
|
it('converts a belongs-to relation to Objection.js format', () => {
|
|
43
|
-
expect(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
expect(
|
|
46
|
+
convertRelation(
|
|
47
|
+
{
|
|
48
|
+
relation: 'belongs-to',
|
|
49
|
+
from: 'ModelOne.modelTwoId',
|
|
50
|
+
to: 'ModelTwo.id',
|
|
51
|
+
modify: 'myScope'
|
|
52
|
+
},
|
|
53
|
+
models
|
|
54
|
+
)
|
|
55
|
+
).toEqual({
|
|
49
56
|
relation: BelongsToOneRelation,
|
|
50
57
|
modelClass: ModelTwo,
|
|
51
58
|
join: {
|
|
@@ -57,12 +64,17 @@ describe('convertRelation()', () => {
|
|
|
57
64
|
})
|
|
58
65
|
|
|
59
66
|
it('converts a many-to-many relation to Objection.js format', () => {
|
|
60
|
-
expect(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
67
|
+
expect(
|
|
68
|
+
convertRelation(
|
|
69
|
+
{
|
|
70
|
+
relation: 'many-to-many',
|
|
71
|
+
from: 'ModelOne.id',
|
|
72
|
+
to: 'ModelTwo.id',
|
|
73
|
+
inverse: false
|
|
74
|
+
},
|
|
75
|
+
models
|
|
76
|
+
)
|
|
77
|
+
).toEqual({
|
|
66
78
|
relation: ManyToManyRelation,
|
|
67
79
|
modelClass: ModelTwo,
|
|
68
80
|
join: {
|
|
@@ -77,12 +89,17 @@ describe('convertRelation()', () => {
|
|
|
77
89
|
})
|
|
78
90
|
|
|
79
91
|
it('converts an inverse many-to-many relation to Objection.js format', () => {
|
|
80
|
-
expect(
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
92
|
+
expect(
|
|
93
|
+
convertRelation(
|
|
94
|
+
{
|
|
95
|
+
relation: 'many-to-many',
|
|
96
|
+
from: 'ModelTwo.id',
|
|
97
|
+
to: 'ModelOne.id',
|
|
98
|
+
inverse: true
|
|
99
|
+
},
|
|
100
|
+
models
|
|
101
|
+
)
|
|
102
|
+
).toEqual({
|
|
86
103
|
relation: ManyToManyRelation,
|
|
87
104
|
modelClass: ModelOne,
|
|
88
105
|
join: {
|
package/src/services/Service.js
CHANGED
|
@@ -20,27 +20,21 @@ export class Service {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
// @overridable
|
|
23
|
-
async initialize() {
|
|
24
|
-
}
|
|
23
|
+
async initialize() {}
|
|
25
24
|
|
|
26
25
|
// @overridable
|
|
27
|
-
async start() {
|
|
28
|
-
}
|
|
26
|
+
async start() {}
|
|
29
27
|
|
|
30
28
|
// @overridable
|
|
31
|
-
async stop() {
|
|
32
|
-
}
|
|
29
|
+
async stop() {}
|
|
33
30
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const logger = ctx?.logger ?? this.app.logger
|
|
38
|
-
return logger.child({ name: this.#loggerName })
|
|
31
|
+
/** @deprecated Use `instance.logger` instead. */
|
|
32
|
+
getLogger() {
|
|
33
|
+
return this.logger
|
|
39
34
|
}
|
|
40
35
|
|
|
41
36
|
get logger() {
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
return value
|
|
37
|
+
const logger = this.app.requestLocals.logger ?? this.app.logger
|
|
38
|
+
return logger.child({ name: this.#loggerName })
|
|
45
39
|
}
|
|
46
40
|
}
|
package/src/storage/S3Storage.js
CHANGED
|
@@ -63,9 +63,11 @@ export class S3Storage extends Storage {
|
|
|
63
63
|
} else {
|
|
64
64
|
// 3. If that fails, keep collecting all chunks and determine
|
|
65
65
|
// the mimetype using the full data.
|
|
66
|
-
stream.once('end', () =>
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
stream.once('end', () =>
|
|
67
|
+
done(
|
|
68
|
+
getFileTypeFromBuffer(data) || 'application/octet-stream'
|
|
69
|
+
)
|
|
70
|
+
)
|
|
69
71
|
}
|
|
70
72
|
}
|
|
71
73
|
data = data ? Buffer.concat([data, chunk]) : chunk
|
package/src/storage/Storage.js
CHANGED
|
@@ -27,12 +27,10 @@ export class Storage {
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
// @overridable
|
|
30
|
-
async setup() {
|
|
31
|
-
}
|
|
30
|
+
async setup() {}
|
|
32
31
|
|
|
33
32
|
// @overridable
|
|
34
|
-
async initialize() {
|
|
35
|
-
}
|
|
33
|
+
async initialize() {}
|
|
36
34
|
|
|
37
35
|
static register(storageClass) {
|
|
38
36
|
const type = (
|
|
@@ -51,15 +49,18 @@ export class Storage {
|
|
|
51
49
|
// Returns a storage that inherits from this.storage but overrides
|
|
52
50
|
// _handleFile to pass on `config` to the call of `handleUpload()`
|
|
53
51
|
return this.storage
|
|
54
|
-
? Object.setPrototypeOf(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
52
|
+
? Object.setPrototypeOf(
|
|
53
|
+
{
|
|
54
|
+
_handleFile: async (req, file, callback) => {
|
|
55
|
+
try {
|
|
56
|
+
callback(null, await this._handleUpload(req, file, config))
|
|
57
|
+
} catch (err) {
|
|
58
|
+
callback(err)
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
},
|
|
62
|
+
this.storage
|
|
63
|
+
)
|
|
63
64
|
: null
|
|
64
65
|
}
|
|
65
66
|
|
|
@@ -169,7 +170,8 @@ export class Storage {
|
|
|
169
170
|
// TODO: `config.readImageSize` was deprecated in favour of
|
|
170
171
|
// `config.readDimensions` in March 2023. Remove in 1 year.
|
|
171
172
|
config.readImageSize
|
|
172
|
-
) &&
|
|
173
|
+
) &&
|
|
174
|
+
/^(image|video)\//.test(file.mimetype)
|
|
173
175
|
) {
|
|
174
176
|
return this._handleMediaFile(req, file)
|
|
175
177
|
} else {
|
package/src/utils/function.js
CHANGED
|
@@ -6,9 +6,12 @@ export function describeFunction(func) {
|
|
|
6
6
|
)
|
|
7
7
|
if (match) {
|
|
8
8
|
const body = match[5] === '{' ? '{ ... }' : '...'
|
|
9
|
-
return match[2] !== undefined
|
|
10
|
-
|
|
11
|
-
: match[
|
|
12
|
-
|
|
9
|
+
return match[2] !== undefined
|
|
10
|
+
? `${match[1]}function (${match[2]}) ${body}`
|
|
11
|
+
: match[3] !== undefined
|
|
12
|
+
? `${match[1]}(${match[3]}) => ${body}`
|
|
13
|
+
: match[4] !== undefined
|
|
14
|
+
? `${match[1]}${match[4]} => ${body}`
|
|
15
|
+
: ''
|
|
13
16
|
}
|
|
14
17
|
}
|
|
@@ -2,12 +2,20 @@ import { describeFunction } from './function.js'
|
|
|
2
2
|
|
|
3
3
|
describe('describeFunction()', () => {
|
|
4
4
|
it('describes normal functions', () => {
|
|
5
|
-
expect(
|
|
5
|
+
expect(
|
|
6
|
+
describeFunction(function (a, b, c) {
|
|
7
|
+
return a + b + c
|
|
8
|
+
})
|
|
9
|
+
)
|
|
6
10
|
.toBe('function (a, b, c) { ... }')
|
|
7
11
|
})
|
|
8
12
|
|
|
9
13
|
it('describes lambdas with one param and a body', () => {
|
|
10
|
-
expect(
|
|
14
|
+
expect(
|
|
15
|
+
describeFunction(a => {
|
|
16
|
+
return a
|
|
17
|
+
})
|
|
18
|
+
)
|
|
11
19
|
.toBe('a => { ... }')
|
|
12
20
|
})
|
|
13
21
|
|
|
@@ -17,7 +25,11 @@ describe('describeFunction()', () => {
|
|
|
17
25
|
})
|
|
18
26
|
|
|
19
27
|
it('describes lambdas with multiple params and a body', () => {
|
|
20
|
-
expect(
|
|
28
|
+
expect(
|
|
29
|
+
describeFunction((a, b, c) => {
|
|
30
|
+
return a + b + c
|
|
31
|
+
})
|
|
32
|
+
)
|
|
21
33
|
.toBe('(a, b, c) => { ... }')
|
|
22
34
|
})
|
|
23
35
|
|
|
@@ -27,12 +39,20 @@ describe('describeFunction()', () => {
|
|
|
27
39
|
})
|
|
28
40
|
|
|
29
41
|
it('describes async functions', () => {
|
|
30
|
-
expect(
|
|
42
|
+
expect(
|
|
43
|
+
describeFunction(async function (a, b, c) {
|
|
44
|
+
return a + b + c
|
|
45
|
+
})
|
|
46
|
+
)
|
|
31
47
|
.toBe('async function (a, b, c) { ... }')
|
|
32
48
|
})
|
|
33
49
|
|
|
34
50
|
it('describes async lambdas with one param and a body', () => {
|
|
35
|
-
expect(
|
|
51
|
+
expect(
|
|
52
|
+
describeFunction(async a => {
|
|
53
|
+
return a
|
|
54
|
+
})
|
|
55
|
+
)
|
|
36
56
|
.toBe('async a => { ... }')
|
|
37
57
|
})
|
|
38
58
|
|
|
@@ -42,7 +62,11 @@ describe('describeFunction()', () => {
|
|
|
42
62
|
})
|
|
43
63
|
|
|
44
64
|
it('describes async lambdas with multiple params and a body', () => {
|
|
45
|
-
expect(
|
|
65
|
+
expect(
|
|
66
|
+
describeFunction(async (a, b, c) => {
|
|
67
|
+
return a + b + c
|
|
68
|
+
})
|
|
69
|
+
)
|
|
46
70
|
.toBe('async (a, b, c) => { ... }')
|
|
47
71
|
})
|
|
48
72
|
|
package/src/utils/object.test.js
CHANGED