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

Manifest schema validator #46

Open
pdehaan opened this issue Apr 8, 2018 · 1 comment
Open

Manifest schema validator #46

pdehaan opened this issue Apr 8, 2018 · 1 comment

Comments

@pdehaan
Copy link
Collaborator

pdehaan commented Apr 8, 2018

Not sure if the manifests will ever change, or the schema... but this was an entertaining lazy-Saturday exercise to create a manifest schema JSON blob and lint the 3 schemas in ./manifests/*.json:

const path = require('path');
const Ajv = require('ajv');
const glob = require('glob');

function getManifestMetricsKeys(manifest) {
    return Object.keys(manifest.extraMetadata.metrics)
        .reduce((obj, key) => {
            obj[key] = {$ref: '#/definitions/metric'};
            return obj;
        }, {});
}

const manifests = glob.sync(path.join(__dirname, '../manifests/*.json'))
    .map(manifestPath => {
        return {
            filename: manifestPath,
            contents: require(manifestPath)
        };
    });

const metricsProperties = manifests.reduce((obj, curr) => Object.assign(obj, getManifestMetricsKeys(curr.contents)), {});

const schema = {
    definitions: {
        // {$ref: "#/definitions/metric"}
        metric: {
            type: 'object',
            properties: {
                title: {type: 'string'},
                description: {type: 'string'},
                type: {
                    type: 'string',
                    enum: ['line', 'table'],
                },
                axes: {
                    type: 'object',
                    properties: {
                        x: {
                            type: 'object',
                            properties: {
                                annotations: {
                                    type: 'array',
                                    items: {
                                        type: 'object',
                                        properties: {
                                            value: {
                                                type: 'string',
                                                format: 'date'
                                            },
                                            label: {type: 'string'}
                                        },
                                        required: ['value', 'label'],
                                        additionalProperties: false,
                                    }
                                }
                            },
                            minItems: 1,
                            additionalProperties: false,
                        },
                        y: {
                            type: 'object',
                            properties: {
                                unit: {type: 'string'},
                                suggestedMin: {type: 'integer'}
                            },
                            required: ['unit'],
                            additionalProperties: false,
                        }
                    },
                    required: ['y'],
                    additionalProperties: false
                },
                columns: {
                    type: 'array',
                    items: {
                        type: 'object',
                        properties: {
                            name: {type: 'string'},
                            unit: {type: 'string'},
                        },
                        required: ['name'],
                        additionalProperties: false,
                    }
                }
            },
            required: ['title', 'type'],
            additionalProperties: false,
        },
        // {$ref: "#/definitions/source"}
        source: {
            type: 'string',
            format: 'uri',
            minLength: 1
        },
        // {$ref: "#/definitions/extraMetadata"}
        extraMetadata: {
            type: 'object',
            properties: {
                title: {type: 'string'},
                defaultCategory: {type: 'string'},
                description: {type: 'string'},
                metrics: {
                    type: 'object',
                    properties: {...metricsProperties},
                    additionalProperties: false,
                },
                dashboard: {$ref: '#/definitions/dashboard'},
            },
            required: ['title', 'metrics', 'dashboard'],
            additionalProperties: false,
        },
        // {$ref: "#/definitions/dashboard"}
        dashboard: {
            type: 'object',
            properties: {
                sectioned: {type: 'boolean'},
                // Only appears in ./manifests/hardware.json
                sections: {
                    type: 'array',
                    items: {
                        type: 'object',
                        properties: {
                            title: {type: 'string'},
                            metrics: {
                                type: 'array',
                                items: {
                                    type: 'string',
                                    enum: [...Object.keys(metricsProperties)],
                                },
                            }
                        },
                        required: ['title', 'metrics'],
                        additionalProperties: false,
                    },
                },
                // Only appears in ./manifests/usage-behavior.json and ./manifests/user-activity.json
                metrics: {
                    type: 'array',
                    items: {
                        type: 'string',
                        enum: [...Object.keys(metricsProperties)],
                    },
                }
            },
            required: ['sectioned'],
            additionalProperties: false,
        },
    },
    type: 'object',
    properties: {
        source: {$ref: '#/definitions/source'},
        extraMetadata: {$ref: '#/definitions/extraMetadata'},
    },
    required: ['source', 'extraMetadata'],
    additionalProperties: false,
};

const ajv = new Ajv({allErrors: true});
const validate = ajv.compile(schema);

manifests.forEach(manifest => {
    if (!validate(manifest.contents)) {
        console.log(`${manifest.filename}:\n${ajv.errorsText(validate.errors)}\n`);
        process.exitCode = 1;
    }
});

Feel free to close this, just posting it here for future reference in case the manifests ever change.

@openjck
Copy link
Contributor

openjck commented Apr 12, 2018

This is really neat. Thank you! I'm not sure I'll get to this before launch but I'll try.

@openjck openjck added this to the M2 milestone May 26, 2018
@openjck openjck removed this from the M2 milestone Aug 24, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants