Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add context support #427

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Add support for context
nechaido authored and o-rumiantsev committed Jun 27, 2019

Verified

This commit was signed with the committer’s verified signature.
o-rumiantsev Oleksii Rumiantsev
commit a09ffacff91cd93140c94f43e5664231595a8d48
24 changes: 20 additions & 4 deletions lib/metaschema-config/config.js
Original file line number Diff line number Diff line change
@@ -30,6 +30,12 @@ const {
actionCreator,
} = require('./action');

const {
addContext,
postprocessContext,
validateContext,
} = require('./context');

const { preprocessResources, addResources } = require('./resource');
const { addApplication, processApplication } = require('./application');

@@ -139,6 +145,7 @@ module.exports = {
localDecorators,
pathToType: {
domains: 'domains',
context: 'context',
category: 'category',
action: 'action',
form: 'form',
@@ -151,6 +158,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(),
@@ -171,6 +179,8 @@ module.exports = {
resolve: (ms, type, name) => {
if (type === 'domains') {
return ms.domains.get(name);
} else if (type === 'context') {
return ms.context.get(name);
} else if (type === 'category') {
return ms.categories.get(name);
} else {
@@ -186,6 +196,11 @@ module.exports = {
...config.processors.domains,
validateSchema: [validateSchema],
},
context: {
add: [addContext],
postprocess: [postprocessContext],
validateInstance: validateContext,
},
category: {
add: [...config.processors.category.add, addCategory],
postprocess: [
@@ -222,10 +237,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,
},
},
};
52 changes: 52 additions & 0 deletions lib/metaschema-config/context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
'use strict';

const { SchemaValidationError } = require('metaschema').errors;

const addContext = (context, ms) => {
const errors = [];
const isDuplicated = ms.context.size > 0;
if (isDuplicated) {
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;
};

const validateContext = (ms, schema, instance, options) => {
const { domain } = schema;
const error = ms.validate('domains', domain, instance, options);
return error ? error.errors : [];
};

module.exports = {
addContext,
postprocessContext,
validateContext,
};
49 changes: 22 additions & 27 deletions lib/metaschema-config/utils.js
Original file line number Diff line number Diff line change
@@ -6,34 +6,14 @@ const {
extractDecorator,
} = require('metaschema');

const dataTypes = ['domain', 'category', 'context'];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const dataTypes = ['domain', 'category', 'context'];
const dataTypes = Object.keys(propToTypes);

const getDefinition = (type, schema) =>
type === 'category' ? schema.definition : schema;

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 +24,26 @@ 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 path = type === 'domain' ? 'domains' : type;
const def = ms[path].get(value);
if (!def) {
const info = { type, value };
errors.push(
new SchemaValidationError('unresolved', `${source}.${key}`, info)
);
} else {
field.definition = getDefinition(type, def);
}
}
}
}
}

3 changes: 3 additions & 0 deletions lib/metaschema-config/validate.js
Original file line number Diff line number Diff line change
@@ -105,6 +105,9 @@ const validate = (ms, schema, instance, options = {}) => {
if (property.domain) {
const error = ms.validate('domains', property.domain, value, opts);
if (error) errors.push(...error.errors);
} else if (property.context) {
const error = ms.validate('context', property.context, value, opts);
if (error) errors.push(...error.errors);
} else if (property.category) {
errors.push(...validateLink(ms, property, value, opts));
}
3 changes: 3 additions & 0 deletions test/fixtures/context/duplicate/context.context
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
({
Nomen: { domain: 'Nomen' },
});
3 changes: 3 additions & 0 deletions test/fixtures/context/duplicate/custom.domains
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
({
Nomen: { type: 'string' },
});
3 changes: 3 additions & 0 deletions test/fixtures/context/duplicate/duplicateDomain.context
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
({
DuplicateNomen: { domain: 'Nomen' },
});
3 changes: 3 additions & 0 deletions test/fixtures/context/unresolved/context.context
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
({
Nomen: { domain: 'Nomen' },
});
6 changes: 6 additions & 0 deletions test/fixtures/context/valid/PublicAction.action
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Action({
Public: true,
Args: {
valueFromContext: { context: 'NomenFromContext' },
},
});
3 changes: 3 additions & 0 deletions test/fixtures/context/valid/context.context
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
({
NomenFromContext: { domain: 'Nomen' },
});
3 changes: 3 additions & 0 deletions test/fixtures/context/valid/custom.domains
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
({
Nomen: { type: 'string' },
});
58 changes: 58 additions & 0 deletions test/ms.context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
'use strict';

const metaschema = require('metaschema');
const metatests = require('metatests');

const { options, config } = require('../lib/metaschema-config/config');

const {
MetaschemaError,
SchemaValidationError,
ValidationError,
} = metaschema.errors;

metatests.test('metaschema correct context usage', async test => {
const schema = await metaschema.fs.load(
'test/fixtures/context/valid',
options,
config
);

test.assertNot(
schema.validate('action', 'PublicAction', { valueFromContext: 'value' })
);

test.isError(
schema.validate('action', 'PublicAction', { valueFromContext: 10 }),
new MetaschemaError([
new ValidationError('invalidType', 'valueFromContext', {
expected: 'string',
actual: 'number',
}),
])
);
});

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a positive test, to ensure that it is possible to use context args in actions.

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',
}),
])
);
});