Skip to content

Commit

Permalink
refactor(wip): add a JSON Schema proxy for easier understanding of sc…
Browse files Browse the repository at this point in the history
…hemas
  • Loading branch information
trieloff committed Dec 5, 2019
1 parent f8a32df commit 1b30584
Show file tree
Hide file tree
Showing 7 changed files with 613 additions and 23 deletions.
8 changes: 7 additions & 1 deletion cli.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const readdirp = require('readdirp');
const Ajv = require('ajv');
const logger = require('@adobe/helix-log');
const {
iter, pipe, filter, map, obj, flat,
iter, pipe, filter, map, obj, flat, list
} = require('ferrum');
const traverse = require('./lib/traverseSchema');
const extract = require('./lib/extractID');
Expand Down Expand Up @@ -155,6 +155,12 @@ readdirp.promise(schemaPath, { root: schemaPath, fileFilter: `*.${schemaExtensio
// remove pure ref schemas
filterRefs,

x => {
const y = list(x);
console.log(JSON.stringify(y, undefined, ' '));
return y;
},

// format titles and descriptions
formatInfo({ extension: schemaExtension }),

Expand Down
24 changes: 13 additions & 11 deletions lib/markdownBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,21 +149,23 @@ function build({ header, links = {} }) {
* Generates the overview table row for a single property definition
* @param {*} param0
*/
function makepropheader([name, definition]) {
return tableRow([
tableCell(text(name)), // Property
tableCell(type(definition)),
tableCell(text('-')),
tableCell(nullable(definition))
]);
function makepropheader(required = []) {
return ([name, definition]) => {
return tableRow([
tableCell(text(name)), // Property
tableCell(type(definition)),
tableCell(text(required.indexOf(name) > -1 ? i18n`Required` : i18n`Optional`)),
tableCell(nullable(definition))
]);
}
}

/**
* Generates the table of contents for a properties
* object.
* @param {*} props
*/
function makeproptable(props) {
function makeproptable(props, required) {
return table('left', [
tableRow([
tableCell(text(i18n`Property`)),
Expand All @@ -172,7 +174,7 @@ function build({ header, links = {} }) {
tableCell(text(i18n`Nullable`)),
tableCell(text(i18n`Defined by`))
]),
...flist(map(iter(props || {}), makepropheader))
...flist(map(iter(props || {}), makepropheader(required)))
]);
}

Expand All @@ -184,7 +186,7 @@ function build({ header, links = {} }) {
if (schema.schema.definitions) {
return [
heading(1, text(i18n`${schema.title} Definitions`)),
makeproptable(schema.schema.definitions),
makeproptable(schema.schema.definitions, schema.schema.required),
];
}
return [];
Expand All @@ -198,7 +200,7 @@ function build({ header, links = {} }) {
if (schema.schema.properties) {
return [
heading(1, text(i18n`${schema.title} Properties`)),
makeproptable(schema.schema.properties),
makeproptable(schema.schema.properties, schema.schema.required),
];
}
return [];
Expand Down
83 changes: 83 additions & 0 deletions lib/schemaProxy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2019 Adobe. All rights reserved.
* This file is licensed to you under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under
* the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
* OF ANY KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
const symbols = {
pointer: Symbol('pointer'),
filename: Symbol('filename'),
id: Symbol('id'),
titles: Symbol('titles'),
};

const handler = ({ root = '', filename = '.', schemas, parent}) => {
const meta = {};

meta[symbols.pointer] = () => root;
meta[symbols.filename] = () => filename;
meta[symbols.id] = (target) => {
// if the schema has it's own ID, use it
if (target.$id) {
return target.$id;
}
if (parent) {
// if we can determine the parent ID (by walking up the tree, use it)
return parent[symbols.id];
}
return undefined;
}
meta[symbols.titles] = (target) => {
if (parent) {
// if we can determine the parent titles
// then we append our own
return [...parent[symbols.titles], target.title];
}
// otherwise, it's just our own
return [target.title];
}

return {
ownKeys: target => Reflect.ownKeys(target),

get: (target, prop, receiver) => {
if (typeof meta[prop] === 'function') {
return meta[prop](target, prop, receiver);
}

const retval = Reflect.get(target, prop, receiver);
if (typeof retval === 'object') {
//console.log('making new proxy from', target, prop, 'receiver', receiver[symbols.id]);
const subschema = new Proxy(retval, handler({
root: `${root}/${prop}`,
parent: receiver,
filename,
schemas }));
return subschema;
}
return retval;
},
};
};

module.exports = {
...symbols
}

module.exports.loader = () => {
const schemas = {
loaded: [],
known: {}
};

return (schema, filename) => {
const proxied = new Proxy(schema, handler({filename, schemas}));
schemas.loaded.push(proxied);
return proxied;
};
}
31 changes: 25 additions & 6 deletions lib/traverseSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,54 +61,73 @@ function traverse(node) {
// turn the schema object into a sequence of key value pairs
iter(schema),
map(([key, subschema]) => {
subschema._definedin = `${id}#${pointer}/${key}`;

if (Array.isArray(subschema) && arrayKeywords.includes(key)) {
// descend into the array
return pipe(
iter(subschema),
enumerate,
map(([index, subsubschema]) => ({
map(([index, subsubschema]) => {
const retval = {
schema: subsubschema,
direct: false,
pointer: `${pointer}/${key}/${index}`,
id,
root: schema,
rootpath,
// TODO add additional properties for tracking
})),
};

subsubschema._definedin = `${id}#${pointer}/${key}/${index}`;


return retval;
}),
);
}
if (subschema && typeof subschema === 'object' && propertyKeywords.includes(key)) {
// descend into the object
return pipe(
iter(subschema),
map(([subkey, subsubschema]) => ({
map(([subkey, subsubschema]) => {
const retval = {
schema: subsubschema,
direct: false,
pointer: `${pointer}/${key}/${subkey}`,
id,
root: schema,
rootpath,
// TODO add additional properties for tracking
})),
};

subsubschema._definedin = `${id}#${pointer}/${key}/${subkey}`;

return retval;
}),
);
}
if (subschema && typeof subschema === 'object' && !ignoreKeywords.includes(key)) {
// this could be a schema, too
return [{
const retval = {
schema: subschema,
direct: false,
pointer: `${pointer}/${key}`,
id,
root: schema,
rootpath,
// TODO add additional properties for tracking
}];
};

return [retval];
}
return [];
}),
flat,
);

schema._definedin = `${id}#${pointer}`;

const itemsarray = list(items, Array);

const subitems = map(items, traverse);
Expand Down
Loading

0 comments on commit 1b30584

Please sign in to comment.