adapt-authoring-jsonschema 0.0.1 → 1.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/.eslintignore +1 -1
- package/.eslintrc +14 -14
- package/.github/ISSUE_TEMPLATE/bug_report.yml +55 -55
- package/.github/ISSUE_TEMPLATE/feature_request.yml +22 -22
- package/.github/dependabot.yml +11 -11
- package/.github/pull_request_template.md +25 -25
- package/.github/workflows/labelled_prs.yml +16 -16
- package/.github/workflows/new.yml +19 -19
- package/.github/workflows/releases.yml +25 -0
- package/adapt-authoring.json +13 -13
- package/bin/check.js +43 -43
- package/conf/config.schema.json +25 -25
- package/docs/plugins/schemas-reference.js +74 -74
- package/docs/plugins/schemas-reference.md +6 -6
- package/docs/schema-examples.md +143 -143
- package/docs/schemas-introduction.md +77 -77
- package/docs/writing-a-schema.md +193 -193
- package/errors/errors.json +52 -52
- package/index.js +5 -5
- package/lib/JsonSchema.js +274 -268
- package/lib/JsonSchemaModule.js +209 -209
- package/lib/Keywords.js +74 -74
- package/lib/XSSDefaults.js +142 -142
- package/lib/typedefs.js +47 -47
- package/package.json +64 -29
- package/schema/base.schema.json +13 -13
package/lib/JsonSchemaModule.js
CHANGED
|
@@ -1,209 +1,209 @@
|
|
|
1
|
-
import _ from 'lodash'
|
|
2
|
-
import { AbstractModule, Hook } from 'adapt-authoring-core'
|
|
3
|
-
import Ajv from 'ajv/dist/2020.js'
|
|
4
|
-
import { glob } from 'glob'
|
|
5
|
-
import JsonSchema from './JsonSchema.js'
|
|
6
|
-
import Keywords from './Keywords.js'
|
|
7
|
-
import path from 'path'
|
|
8
|
-
import safeRegex from 'safe-regex'
|
|
9
|
-
import XSSDefaults from './XSSDefaults.js'
|
|
10
|
-
|
|
11
|
-
const BASE_SCHEMA_PATH = './schema/base.schema.json'
|
|
12
|
-
/**
|
|
13
|
-
* Module which add support for the JSON Schema specification
|
|
14
|
-
* @memberof jsonschema
|
|
15
|
-
* @extends {AbstractModule}
|
|
16
|
-
*/
|
|
17
|
-
class JsonSchemaModule extends AbstractModule {
|
|
18
|
-
/** @override */
|
|
19
|
-
async init () {
|
|
20
|
-
this.app.jsonschema = this
|
|
21
|
-
/**
|
|
22
|
-
* Reference to all registed schemas
|
|
23
|
-
* @type {Object}
|
|
24
|
-
*/
|
|
25
|
-
this.schemas = {}
|
|
26
|
-
/**
|
|
27
|
-
* Temporary store of extension schemas
|
|
28
|
-
* @type {Object}
|
|
29
|
-
*/
|
|
30
|
-
this.schemaExtensions = {}
|
|
31
|
-
/**
|
|
32
|
-
* Invoked when schemas are registered
|
|
33
|
-
* @type {Hook}
|
|
34
|
-
*/
|
|
35
|
-
this.registerSchemasHook = new Hook
|
|
36
|
-
/**
|
|
37
|
-
* Tags and attributes to be whitelisted by the XSS filter
|
|
38
|
-
* @type {Object}
|
|
39
|
-
*/
|
|
40
|
-
this.xssWhitelist = {}
|
|
41
|
-
/**
|
|
42
|
-
* Reference to the Ajv instance
|
|
43
|
-
* @type {external:Ajv}
|
|
44
|
-
*/
|
|
45
|
-
this.validator = new Ajv({
|
|
46
|
-
addUsedSchema: false,
|
|
47
|
-
allErrors: true,
|
|
48
|
-
allowUnionTypes: true,
|
|
49
|
-
loadSchema: this.getSchema.bind(this),
|
|
50
|
-
removeAdditional: 'all',
|
|
51
|
-
strict: false,
|
|
52
|
-
verbose: true,
|
|
53
|
-
keywords: Keywords.all
|
|
54
|
-
})
|
|
55
|
-
this.addStringFormats({
|
|
56
|
-
'date-time': /[A-za-z0-9:+\(\)]+/,
|
|
57
|
-
time: /^(\d{2}):(\d{2}):(\d{2})\+(\d{2}):(\d{2})$/,
|
|
58
|
-
uri: /^(.+):\/\/(www\.)?[-a-zA-Z0-9@:%_\+.~#?&//=]{1,256}/
|
|
59
|
-
})
|
|
60
|
-
await this.resetSchemaRegistry()
|
|
61
|
-
|
|
62
|
-
this.onReady()
|
|
63
|
-
.then(() => this.app.waitForModule('config', 'errors'))
|
|
64
|
-
.then(() => {
|
|
65
|
-
Object.assign(this.xssWhitelist,
|
|
66
|
-
this.getConfig('xssWhitelistOverride') ? {} : XSSDefaults,
|
|
67
|
-
this.getConfig('xssWhitelist'))
|
|
68
|
-
})
|
|
69
|
-
.then(() => this.addStringFormats(this.getConfig('formatOverrides')))
|
|
70
|
-
.then(() => this.registerSchemas())
|
|
71
|
-
.catch(e => this.log('error', e))
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Empties the schema registry (with the exception of the base schema)
|
|
76
|
-
*/
|
|
77
|
-
async resetSchemaRegistry () {
|
|
78
|
-
this.log('debug', 'RESET_SCHEMAS')
|
|
79
|
-
this.schemas = {
|
|
80
|
-
base: await this.createSchema(path.resolve(this.rootDir, BASE_SCHEMA_PATH), { enableCache: true })
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/**
|
|
85
|
-
* Adds string formats to the Ajv validator
|
|
86
|
-
*/
|
|
87
|
-
addStringFormats (formats) {
|
|
88
|
-
Object.entries(formats).forEach(([name, re]) => {
|
|
89
|
-
const isUnsafe = !safeRegex(re)
|
|
90
|
-
if (isUnsafe) this.log('warn', `unsafe RegExp for format '${name}' (${re}), using default`)
|
|
91
|
-
this.validator.addFormat(name, isUnsafe ? /.*/ : re)
|
|
92
|
-
})
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* Adds a new keyword to be used in JSON schemas
|
|
97
|
-
* @param {AjvKeyword} definition
|
|
98
|
-
*/
|
|
99
|
-
addKeyword (definition) {
|
|
100
|
-
try {
|
|
101
|
-
this.validator.addKeyword(definition)
|
|
102
|
-
} catch (e) {
|
|
103
|
-
this.log('warn', `failed to define keyword '${definition.keyword}', ${e}`)
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Searches all Adapt dependencies for any local JSON schemas and registers them for use in the app. Schemas must be located in in a `/schema` folder, and be named appropriately: `*.schema.json`.
|
|
109
|
-
* @return {Promise}
|
|
110
|
-
*/
|
|
111
|
-
async registerSchemas () {
|
|
112
|
-
await this.resetSchemaRegistry()
|
|
113
|
-
await Promise.all(Object.values(this.app.dependencies).map(async d => {
|
|
114
|
-
if(d.name === this.name) return
|
|
115
|
-
const files = await glob('schema/*.schema.json', { cwd: d.rootDir, absolute: true })
|
|
116
|
-
;(await Promise.allSettled(files.map(f => this.registerSchema(f))))
|
|
117
|
-
.filter(r => r.status === 'rejected')
|
|
118
|
-
.forEach(r => this.log('warn', r.reason))
|
|
119
|
-
}))
|
|
120
|
-
this.registerSchemasHook.invoke();
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Registers a single JSON schema for use in the app
|
|
125
|
-
* @param {String} filePath Path to the schema file
|
|
126
|
-
* @param {RegisterSchemaOptions} options Extra options
|
|
127
|
-
* @return {Promise}
|
|
128
|
-
*/
|
|
129
|
-
async registerSchema (filePath, options = {}) {
|
|
130
|
-
if (!_.isString(filePath)) {
|
|
131
|
-
throw this.app.errors.INVALID_PARAMS.setData({ params: ['filePath'] })
|
|
132
|
-
}
|
|
133
|
-
const schema = await this.createSchema(filePath, options)
|
|
134
|
-
|
|
135
|
-
if (this.schemas[schema.name]) {
|
|
136
|
-
if (options.replace) this.deregisterSchema(schema.name)
|
|
137
|
-
else throw this.app.errors.SCHEMA_EXISTS.setData({ schemaName: schema.name, filePath })
|
|
138
|
-
}
|
|
139
|
-
this.schemas[schema.name] = schema
|
|
140
|
-
this.schemaExtensions?.[schema.name]?.forEach(s => schema.addExtension(s))
|
|
141
|
-
if (schema.raw.$patch) this.extendSchema(schema.raw.$patch?.source?.$ref, schema.name)
|
|
142
|
-
|
|
143
|
-
this.log('debug', 'REGISTER_SCHEMA', schema.name, filePath)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* deregisters a single JSON schema
|
|
148
|
-
* @param {String} name Schem name to deregister
|
|
149
|
-
* @return {Promise} Resolves with schema data
|
|
150
|
-
*/
|
|
151
|
-
deregisterSchema (name) {
|
|
152
|
-
if (this.schemas[name]) delete this.schemas[name]
|
|
153
|
-
// remove schema from any extensions lists
|
|
154
|
-
Object.entries(this.schemaExtensions).forEach(([base, extensions]) => {
|
|
155
|
-
this.schemaExtensions[base] = extensions.filter(s => s !== name)
|
|
156
|
-
})
|
|
157
|
-
this.log('debug', 'DEREGISTER_SCHEMA', name)
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Creates a new JsonSchema instance
|
|
162
|
-
* @param {String} filePath Path to the schema file
|
|
163
|
-
* @param {Object} options Options passed to JsonSchema constructor
|
|
164
|
-
* @returns {JsonSchema}
|
|
165
|
-
*/
|
|
166
|
-
createSchema (filePath, options) {
|
|
167
|
-
const schema = new JsonSchema({
|
|
168
|
-
enableCache: this.getConfig('enableCache'),
|
|
169
|
-
filePath,
|
|
170
|
-
validator: this.validator,
|
|
171
|
-
xssWhitelist: this.xssWhitelist,
|
|
172
|
-
...options
|
|
173
|
-
})
|
|
174
|
-
this.schemaExtensions?.[schema.name]?.forEach(s => schema.addExtension(s))
|
|
175
|
-
delete this.schemaExtensions?.[schema.name]
|
|
176
|
-
return schema.load()
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
/**
|
|
180
|
-
* Extends an existing schema with extra properties
|
|
181
|
-
* @param {String} baseSchemaName The name of the schema to extend
|
|
182
|
-
* @param {String} extSchemaName The name of the schema to extend with
|
|
183
|
-
*/
|
|
184
|
-
extendSchema (baseSchemaName, extSchemaName) {
|
|
185
|
-
const baseSchema = this.schemas[baseSchemaName]
|
|
186
|
-
if (baseSchema) {
|
|
187
|
-
baseSchema.addExtension(extSchemaName)
|
|
188
|
-
} else {
|
|
189
|
-
if (!this.schemaExtensions[baseSchemaName]) this.schemaExtensions[baseSchemaName] = []
|
|
190
|
-
this.schemaExtensions[baseSchemaName].push(extSchemaName)
|
|
191
|
-
}
|
|
192
|
-
this.log('debug', 'EXTEND_SCHEMA', baseSchemaName, extSchemaName)
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Retrieves the specified schema. Recursively applies any schema merge/patch schemas. Will returned cached data if enabled.
|
|
197
|
-
* @param {String} schemaName The name of the schema to return
|
|
198
|
-
* @param {LoadSchemaOptions} options
|
|
199
|
-
* @param {Boolean} options.compiled If false, the raw schema will be returned
|
|
200
|
-
* @return {Promise} The compiled schema validation function (default) or the raw schema
|
|
201
|
-
*/
|
|
202
|
-
async getSchema (schemaName, options = {}) {
|
|
203
|
-
const schema = this.schemas[schemaName]
|
|
204
|
-
if (!schema) throw this.app.errors.MISSING_SCHEMA.setData({ schemaName })
|
|
205
|
-
return schema.build(options)
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
export default JsonSchemaModule
|
|
1
|
+
import _ from 'lodash'
|
|
2
|
+
import { AbstractModule, Hook } from 'adapt-authoring-core'
|
|
3
|
+
import Ajv from 'ajv/dist/2020.js'
|
|
4
|
+
import { glob } from 'glob'
|
|
5
|
+
import JsonSchema from './JsonSchema.js'
|
|
6
|
+
import Keywords from './Keywords.js'
|
|
7
|
+
import path from 'path'
|
|
8
|
+
import safeRegex from 'safe-regex'
|
|
9
|
+
import XSSDefaults from './XSSDefaults.js'
|
|
10
|
+
|
|
11
|
+
const BASE_SCHEMA_PATH = './schema/base.schema.json'
|
|
12
|
+
/**
|
|
13
|
+
* Module which add support for the JSON Schema specification
|
|
14
|
+
* @memberof jsonschema
|
|
15
|
+
* @extends {AbstractModule}
|
|
16
|
+
*/
|
|
17
|
+
class JsonSchemaModule extends AbstractModule {
|
|
18
|
+
/** @override */
|
|
19
|
+
async init () {
|
|
20
|
+
this.app.jsonschema = this
|
|
21
|
+
/**
|
|
22
|
+
* Reference to all registed schemas
|
|
23
|
+
* @type {Object}
|
|
24
|
+
*/
|
|
25
|
+
this.schemas = {}
|
|
26
|
+
/**
|
|
27
|
+
* Temporary store of extension schemas
|
|
28
|
+
* @type {Object}
|
|
29
|
+
*/
|
|
30
|
+
this.schemaExtensions = {}
|
|
31
|
+
/**
|
|
32
|
+
* Invoked when schemas are registered
|
|
33
|
+
* @type {Hook}
|
|
34
|
+
*/
|
|
35
|
+
this.registerSchemasHook = new Hook
|
|
36
|
+
/**
|
|
37
|
+
* Tags and attributes to be whitelisted by the XSS filter
|
|
38
|
+
* @type {Object}
|
|
39
|
+
*/
|
|
40
|
+
this.xssWhitelist = {}
|
|
41
|
+
/**
|
|
42
|
+
* Reference to the Ajv instance
|
|
43
|
+
* @type {external:Ajv}
|
|
44
|
+
*/
|
|
45
|
+
this.validator = new Ajv({
|
|
46
|
+
addUsedSchema: false,
|
|
47
|
+
allErrors: true,
|
|
48
|
+
allowUnionTypes: true,
|
|
49
|
+
loadSchema: this.getSchema.bind(this),
|
|
50
|
+
removeAdditional: 'all',
|
|
51
|
+
strict: false,
|
|
52
|
+
verbose: true,
|
|
53
|
+
keywords: Keywords.all
|
|
54
|
+
})
|
|
55
|
+
this.addStringFormats({
|
|
56
|
+
'date-time': /[A-za-z0-9:+\(\)]+/,
|
|
57
|
+
time: /^(\d{2}):(\d{2}):(\d{2})\+(\d{2}):(\d{2})$/,
|
|
58
|
+
uri: /^(.+):\/\/(www\.)?[-a-zA-Z0-9@:%_\+.~#?&//=]{1,256}/
|
|
59
|
+
})
|
|
60
|
+
await this.resetSchemaRegistry()
|
|
61
|
+
|
|
62
|
+
this.onReady()
|
|
63
|
+
.then(() => this.app.waitForModule('config', 'errors'))
|
|
64
|
+
.then(() => {
|
|
65
|
+
Object.assign(this.xssWhitelist,
|
|
66
|
+
this.getConfig('xssWhitelistOverride') ? {} : XSSDefaults,
|
|
67
|
+
this.getConfig('xssWhitelist'))
|
|
68
|
+
})
|
|
69
|
+
.then(() => this.addStringFormats(this.getConfig('formatOverrides')))
|
|
70
|
+
.then(() => this.registerSchemas())
|
|
71
|
+
.catch(e => this.log('error', e))
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Empties the schema registry (with the exception of the base schema)
|
|
76
|
+
*/
|
|
77
|
+
async resetSchemaRegistry () {
|
|
78
|
+
this.log('debug', 'RESET_SCHEMAS')
|
|
79
|
+
this.schemas = {
|
|
80
|
+
base: await this.createSchema(path.resolve(this.rootDir, BASE_SCHEMA_PATH), { enableCache: true })
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Adds string formats to the Ajv validator
|
|
86
|
+
*/
|
|
87
|
+
addStringFormats (formats) {
|
|
88
|
+
Object.entries(formats).forEach(([name, re]) => {
|
|
89
|
+
const isUnsafe = !safeRegex(re)
|
|
90
|
+
if (isUnsafe) this.log('warn', `unsafe RegExp for format '${name}' (${re}), using default`)
|
|
91
|
+
this.validator.addFormat(name, isUnsafe ? /.*/ : re)
|
|
92
|
+
})
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Adds a new keyword to be used in JSON schemas
|
|
97
|
+
* @param {AjvKeyword} definition
|
|
98
|
+
*/
|
|
99
|
+
addKeyword (definition) {
|
|
100
|
+
try {
|
|
101
|
+
this.validator.addKeyword(definition)
|
|
102
|
+
} catch (e) {
|
|
103
|
+
this.log('warn', `failed to define keyword '${definition.keyword}', ${e}`)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Searches all Adapt dependencies for any local JSON schemas and registers them for use in the app. Schemas must be located in in a `/schema` folder, and be named appropriately: `*.schema.json`.
|
|
109
|
+
* @return {Promise}
|
|
110
|
+
*/
|
|
111
|
+
async registerSchemas () {
|
|
112
|
+
await this.resetSchemaRegistry()
|
|
113
|
+
await Promise.all(Object.values(this.app.dependencies).map(async d => {
|
|
114
|
+
if(d.name === this.name) return
|
|
115
|
+
const files = await glob('schema/*.schema.json', { cwd: d.rootDir, absolute: true })
|
|
116
|
+
;(await Promise.allSettled(files.map(f => this.registerSchema(f))))
|
|
117
|
+
.filter(r => r.status === 'rejected')
|
|
118
|
+
.forEach(r => this.log('warn', r.reason))
|
|
119
|
+
}))
|
|
120
|
+
this.registerSchemasHook.invoke();
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Registers a single JSON schema for use in the app
|
|
125
|
+
* @param {String} filePath Path to the schema file
|
|
126
|
+
* @param {RegisterSchemaOptions} options Extra options
|
|
127
|
+
* @return {Promise}
|
|
128
|
+
*/
|
|
129
|
+
async registerSchema (filePath, options = {}) {
|
|
130
|
+
if (!_.isString(filePath)) {
|
|
131
|
+
throw this.app.errors.INVALID_PARAMS.setData({ params: ['filePath'] })
|
|
132
|
+
}
|
|
133
|
+
const schema = await this.createSchema(filePath, options)
|
|
134
|
+
|
|
135
|
+
if (this.schemas[schema.name]) {
|
|
136
|
+
if (options.replace) this.deregisterSchema(schema.name)
|
|
137
|
+
else throw this.app.errors.SCHEMA_EXISTS.setData({ schemaName: schema.name, filePath })
|
|
138
|
+
}
|
|
139
|
+
this.schemas[schema.name] = schema
|
|
140
|
+
this.schemaExtensions?.[schema.name]?.forEach(s => schema.addExtension(s))
|
|
141
|
+
if (schema.raw.$patch) this.extendSchema(schema.raw.$patch?.source?.$ref, schema.name)
|
|
142
|
+
|
|
143
|
+
this.log('debug', 'REGISTER_SCHEMA', schema.name, filePath)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* deregisters a single JSON schema
|
|
148
|
+
* @param {String} name Schem name to deregister
|
|
149
|
+
* @return {Promise} Resolves with schema data
|
|
150
|
+
*/
|
|
151
|
+
deregisterSchema (name) {
|
|
152
|
+
if (this.schemas[name]) delete this.schemas[name]
|
|
153
|
+
// remove schema from any extensions lists
|
|
154
|
+
Object.entries(this.schemaExtensions).forEach(([base, extensions]) => {
|
|
155
|
+
this.schemaExtensions[base] = extensions.filter(s => s !== name)
|
|
156
|
+
})
|
|
157
|
+
this.log('debug', 'DEREGISTER_SCHEMA', name)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Creates a new JsonSchema instance
|
|
162
|
+
* @param {String} filePath Path to the schema file
|
|
163
|
+
* @param {Object} options Options passed to JsonSchema constructor
|
|
164
|
+
* @returns {JsonSchema}
|
|
165
|
+
*/
|
|
166
|
+
createSchema (filePath, options) {
|
|
167
|
+
const schema = new JsonSchema({
|
|
168
|
+
enableCache: this.getConfig('enableCache'),
|
|
169
|
+
filePath,
|
|
170
|
+
validator: this.validator,
|
|
171
|
+
xssWhitelist: this.xssWhitelist,
|
|
172
|
+
...options
|
|
173
|
+
})
|
|
174
|
+
this.schemaExtensions?.[schema.name]?.forEach(s => schema.addExtension(s))
|
|
175
|
+
delete this.schemaExtensions?.[schema.name]
|
|
176
|
+
return schema.load()
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Extends an existing schema with extra properties
|
|
181
|
+
* @param {String} baseSchemaName The name of the schema to extend
|
|
182
|
+
* @param {String} extSchemaName The name of the schema to extend with
|
|
183
|
+
*/
|
|
184
|
+
extendSchema (baseSchemaName, extSchemaName) {
|
|
185
|
+
const baseSchema = this.schemas[baseSchemaName]
|
|
186
|
+
if (baseSchema) {
|
|
187
|
+
baseSchema.addExtension(extSchemaName)
|
|
188
|
+
} else {
|
|
189
|
+
if (!this.schemaExtensions[baseSchemaName]) this.schemaExtensions[baseSchemaName] = []
|
|
190
|
+
this.schemaExtensions[baseSchemaName].push(extSchemaName)
|
|
191
|
+
}
|
|
192
|
+
this.log('debug', 'EXTEND_SCHEMA', baseSchemaName, extSchemaName)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Retrieves the specified schema. Recursively applies any schema merge/patch schemas. Will returned cached data if enabled.
|
|
197
|
+
* @param {String} schemaName The name of the schema to return
|
|
198
|
+
* @param {LoadSchemaOptions} options
|
|
199
|
+
* @param {Boolean} options.compiled If false, the raw schema will be returned
|
|
200
|
+
* @return {Promise} The compiled schema validation function (default) or the raw schema
|
|
201
|
+
*/
|
|
202
|
+
async getSchema (schemaName, options = {}) {
|
|
203
|
+
const schema = this.schemas[schemaName]
|
|
204
|
+
if (!schema) throw this.app.errors.MISSING_SCHEMA.setData({ schemaName })
|
|
205
|
+
return schema.build(options)
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export default JsonSchemaModule
|
package/lib/Keywords.js
CHANGED
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
import { App } from 'adapt-authoring-core'
|
|
2
|
-
import bytes from 'bytes'
|
|
3
|
-
import ms from 'ms'
|
|
4
|
-
import path from 'path'
|
|
5
|
-
/**
|
|
6
|
-
* Adds some useful schema keywords
|
|
7
|
-
* @memberof jsonschema
|
|
8
|
-
* @extends {AbstractModule}
|
|
9
|
-
*/
|
|
10
|
-
class Keywords {
|
|
11
|
-
static get all () {
|
|
12
|
-
const keywords = {
|
|
13
|
-
isBytes: function () {
|
|
14
|
-
return (value, { parentData, parentDataProperty }) => {
|
|
15
|
-
try {
|
|
16
|
-
parentData[parentDataProperty] = bytes.parse(value)
|
|
17
|
-
return true
|
|
18
|
-
} catch (e) {
|
|
19
|
-
return false
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
isDate: function () {
|
|
24
|
-
return (value, { parentData, parentDataProperty }) => {
|
|
25
|
-
try {
|
|
26
|
-
parentData[parentDataProperty] = new Date(value)
|
|
27
|
-
return true
|
|
28
|
-
} catch (e) {
|
|
29
|
-
return false
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
},
|
|
33
|
-
isDirectory: function () {
|
|
34
|
-
const doReplace = value => {
|
|
35
|
-
const app = App.instance
|
|
36
|
-
return [
|
|
37
|
-
['$ROOT', app.rootDir],
|
|
38
|
-
['$DATA', app.getConfig('dataDir')],
|
|
39
|
-
['$TEMP', app.getConfig('tempDir')]
|
|
40
|
-
].reduce((m, [k, v]) => {
|
|
41
|
-
return m.startsWith(k) ? path.resolve(v, m.replace(k, '').slice(1)) : m
|
|
42
|
-
}, value)
|
|
43
|
-
}
|
|
44
|
-
return (value, { parentData, parentDataProperty }) => {
|
|
45
|
-
try {
|
|
46
|
-
parentData[parentDataProperty] = doReplace(value)
|
|
47
|
-
} catch (e) {}
|
|
48
|
-
return true
|
|
49
|
-
}
|
|
50
|
-
},
|
|
51
|
-
isTimeMs: function () {
|
|
52
|
-
return (value, { parentData, parentDataProperty }) => {
|
|
53
|
-
try {
|
|
54
|
-
parentData[parentDataProperty] = ms(value)
|
|
55
|
-
return true
|
|
56
|
-
} catch (e) {
|
|
57
|
-
return false
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return Object.entries(keywords).map(([keyword, compile]) => {
|
|
63
|
-
return {
|
|
64
|
-
keyword,
|
|
65
|
-
type: 'string',
|
|
66
|
-
modifying: true,
|
|
67
|
-
schemaType: 'boolean',
|
|
68
|
-
compile
|
|
69
|
-
}
|
|
70
|
-
})
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export default Keywords
|
|
1
|
+
import { App } from 'adapt-authoring-core'
|
|
2
|
+
import bytes from 'bytes'
|
|
3
|
+
import ms from 'ms'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
/**
|
|
6
|
+
* Adds some useful schema keywords
|
|
7
|
+
* @memberof jsonschema
|
|
8
|
+
* @extends {AbstractModule}
|
|
9
|
+
*/
|
|
10
|
+
class Keywords {
|
|
11
|
+
static get all () {
|
|
12
|
+
const keywords = {
|
|
13
|
+
isBytes: function () {
|
|
14
|
+
return (value, { parentData, parentDataProperty }) => {
|
|
15
|
+
try {
|
|
16
|
+
parentData[parentDataProperty] = bytes.parse(value)
|
|
17
|
+
return true
|
|
18
|
+
} catch (e) {
|
|
19
|
+
return false
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
isDate: function () {
|
|
24
|
+
return (value, { parentData, parentDataProperty }) => {
|
|
25
|
+
try {
|
|
26
|
+
parentData[parentDataProperty] = new Date(value)
|
|
27
|
+
return true
|
|
28
|
+
} catch (e) {
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
isDirectory: function () {
|
|
34
|
+
const doReplace = value => {
|
|
35
|
+
const app = App.instance
|
|
36
|
+
return [
|
|
37
|
+
['$ROOT', app.rootDir],
|
|
38
|
+
['$DATA', app.getConfig('dataDir')],
|
|
39
|
+
['$TEMP', app.getConfig('tempDir')]
|
|
40
|
+
].reduce((m, [k, v]) => {
|
|
41
|
+
return m.startsWith(k) ? path.resolve(v, m.replace(k, '').slice(1)) : m
|
|
42
|
+
}, value)
|
|
43
|
+
}
|
|
44
|
+
return (value, { parentData, parentDataProperty }) => {
|
|
45
|
+
try {
|
|
46
|
+
parentData[parentDataProperty] = doReplace(value)
|
|
47
|
+
} catch (e) {}
|
|
48
|
+
return true
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
isTimeMs: function () {
|
|
52
|
+
return (value, { parentData, parentDataProperty }) => {
|
|
53
|
+
try {
|
|
54
|
+
parentData[parentDataProperty] = ms(value)
|
|
55
|
+
return true
|
|
56
|
+
} catch (e) {
|
|
57
|
+
return false
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return Object.entries(keywords).map(([keyword, compile]) => {
|
|
63
|
+
return {
|
|
64
|
+
keyword,
|
|
65
|
+
type: 'string',
|
|
66
|
+
modifying: true,
|
|
67
|
+
schemaType: 'boolean',
|
|
68
|
+
compile
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export default Keywords
|