diff --git a/lib/metaschema-config/config.js b/lib/metaschema-config/config.js index a0057828..cb4cac62 100644 --- a/lib/metaschema-config/config.js +++ b/lib/metaschema-config/config.js @@ -30,6 +30,7 @@ const { actionCreator, } = require('./action'); +const { addContext, postprocessContext } = require('./context'); const { preprocessResources, addResources } = require('./resource'); const { addApplication, processApplication } = require('./application'); @@ -139,6 +140,7 @@ module.exports = { localDecorators, pathToType: { domains: 'domains', + context: 'context', category: 'category', action: 'action', form: 'form', @@ -151,6 +153,7 @@ module.exports = { prepare: ms => { ms.categories = new Map(); ms.domains = new Map(); + ms.context = new Map(); ms.actions = new Map(); ms.resources = { common: new Map(), @@ -186,6 +189,10 @@ module.exports = { ...config.processors.domains, validateSchema: [validateSchema], }, + context: { + add: [addContext], + postprocess: [postprocessContext], + }, category: { add: [...config.processors.category.add, addCategory], postprocess: [ @@ -222,10 +229,11 @@ module.exports = { processOrder: { domains: 0, category: 1, - form: 2, - action: 3, - resource: 4, - application: 5, + context: 2, + form: 3, + action: 4, + resource: 5, + application: 6, }, }, }; diff --git a/lib/metaschema-config/context.js b/lib/metaschema-config/context.js new file mode 100644 index 00000000..e6e116a9 --- /dev/null +++ b/lib/metaschema-config/context.js @@ -0,0 +1,45 @@ +'use strict'; + +const { SchemaValidationError } = require('metaschema').errors; + +const addContext = (context, ms) => { + const errors = []; + const duplicateContext = ms.context.size > 0; + if (duplicateContext) { + errors.push( + new SchemaValidationError('duplicate', context.name, { + type: 'context', + value: context.name, + }) + ); + } else { + Object.entries(context.definition).forEach(([name, field]) => + ms.context.set(name, field) + ); + } + return errors; +}; + +const postprocessContext = (context, ms) => { + const errors = []; + for (const [fieldName, field] of Object.entries(context.definition)) { + const domain = ms.domains.get(field.domain); + if (domain) { + field.definition = domain; + } else { + errors.push( + new SchemaValidationError( + 'unresolved', + `${context.name}.${fieldName}`, + { type: 'domain', value: field.domain } + ) + ); + } + } + return errors; +}; + +module.exports = { + addContext, + postprocessContext, +}; diff --git a/lib/metaschema-config/utils.js b/lib/metaschema-config/utils.js index 803a3dec..d8eebc96 100644 --- a/lib/metaschema-config/utils.js +++ b/lib/metaschema-config/utils.js @@ -6,34 +6,23 @@ const { extractDecorator, } = require('metaschema'); +const propToTypes = { + domain: 'domains', + category: 'category', + context: 'context', +}; + +const dataTypes = Object.keys(propToTypes); +const getDefinition = { + domain: domain => domain, + category: category => category.definition, + context: context => context, +}; + const processFields = (ms, category, fields, source) => { const errors = []; for (const [key, field] of Object.entries(fields)) { - if (field.domain) { - const def = ms.domains.get(field.domain); - if (!def) { - errors.push( - new SchemaValidationError('unresolved', `${source}.${key}`, { - type: 'domain', - value: field.domain, - }) - ); - } else { - Object.assign(field, { domain: field.domain, definition: def }); - } - } else if (field.category) { - const cat = ms.categories.get(field.category); - if (!cat) { - errors.push( - new SchemaValidationError('unresolved', `${source}.${key}`, { - type: 'category', - value: field.category, - }) - ); - } else { - field.definition = cat.definition; - } - } else if (typeof field === 'string') { + if (typeof field === 'string') { const src = `${source}.${key}`; if (!category) { errors.push( @@ -44,11 +33,25 @@ const processFields = (ms, category, fields, source) => { continue; } try { - const def = extractByPath(category, field.field, ms, src); - Object.assign(field, def); + Object.assign(field, extractByPath(category, field.field, ms, src)); } catch (error) { errors.push(error); } + } else { + for (const type of dataTypes) { + const value = field[type]; + if (value) { + const def = ms[propToTypes[type]].get(value); + if (!def) { + const info = { type, value }; + errors.push( + new SchemaValidationError('unresolved', `${source}.${key}`, info) + ); + } else { + field.definition = getDefinition[type](def); + } + } + } } } diff --git a/test/fixtures/context/duplicate/context.context b/test/fixtures/context/duplicate/context.context new file mode 100644 index 00000000..02e94e61 --- /dev/null +++ b/test/fixtures/context/duplicate/context.context @@ -0,0 +1,3 @@ +({ + Nomen: { domain: 'Nomen' }, +}); diff --git a/test/fixtures/context/duplicate/custom.domains b/test/fixtures/context/duplicate/custom.domains new file mode 100644 index 00000000..a16a5cf8 --- /dev/null +++ b/test/fixtures/context/duplicate/custom.domains @@ -0,0 +1,3 @@ +({ + Nomen: { type: 'string' }, +}); diff --git a/test/fixtures/context/duplicate/duplicateDomain.context b/test/fixtures/context/duplicate/duplicateDomain.context new file mode 100644 index 00000000..ca0433ca --- /dev/null +++ b/test/fixtures/context/duplicate/duplicateDomain.context @@ -0,0 +1,3 @@ +({ + DuplicateNomen: { domain: 'Nomen' }, +}); diff --git a/test/fixtures/context/unresolved/context.context b/test/fixtures/context/unresolved/context.context new file mode 100644 index 00000000..02e94e61 --- /dev/null +++ b/test/fixtures/context/unresolved/context.context @@ -0,0 +1,3 @@ +({ + Nomen: { domain: 'Nomen' }, +}); diff --git a/test/ms.context.js b/test/ms.context.js new file mode 100644 index 00000000..82cb07a5 --- /dev/null +++ b/test/ms.context.js @@ -0,0 +1,32 @@ +'use strict'; + +const metaschema = require('metaschema'); +const metatests = require('metatests'); + +const { options, config } = require('../lib/metaschema-config/config'); + +const { MetaschemaError, SchemaValidationError } = metaschema.errors; + +metatests.test('metaschema context error on duplicated domains', async test => { + await test.rejects( + metaschema.fs.load('test/fixtures/context/duplicate', options, config), + new MetaschemaError([ + new SchemaValidationError('duplicate', 'duplicateDomain', { + type: 'context', + value: 'duplicateDomain', + }), + ]) + ); +}); + +metatests.test('metaschema context error on unresolved domain', async test => { + await test.rejects( + metaschema.fs.load('test/fixtures/context/unresolved', options, config), + new MetaschemaError([ + new SchemaValidationError('unresolved', 'context.Nomen', { + type: 'domain', + value: 'Nomen', + }), + ]) + ); +});