Skip to content

Commit 4d431c1

Browse files
committed
Fix schema parsing
1 parent 9393a37 commit 4d431c1

11 files changed

+194
-88
lines changed

lib/parser/function_parser.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,17 @@ FunctionParser.definitionFields = {
204204
)
205205
: true
206206
) &&
207+
// (
208+
// 'alternateSchemas' in param
209+
// ? (
210+
// (['object'].indexOf(param.type) !== -1) &&
211+
// Array.isArray(param.alternateSchemas) &&
212+
// param.alternateSchemas.filter(schema => {
213+
// return FunctionParser.definitionFields['params'](schema);
214+
// }).length === param.alternateSchemas.length
215+
// )
216+
// : true
217+
// ) &&
207218
types.list.indexOf(param.type) > -1
208219
}).length === params.length;
209220
},

lib/parser/nodejs/comment_definition_parser.js

Lines changed: 65 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class CommentDefinitionParser {
3030
let field = splitLine.shift();
3131
line = splitLine.join(' ');
3232
if (!field && previous && (previous.field === 'param' || previous.field === 'returns')) {
33-
previous.schema = (previous.schema || []).concat(line);
33+
previous.textSchema = (previous.textSchema || []).concat([[line]]);
3434
return semanticsList;
3535
} else if (DEFINITION_FIELDS.indexOf(field) === -1) {
3636
throw new Error(`Invalid Definition Field: "${field}"`);
@@ -50,17 +50,14 @@ class CommentDefinitionParser {
5050
values: [line.trim()]
5151
});
5252
} else if (previous) {
53-
let lastSchemaItem = previous.schema && previous.schema[previous.schema.length - 1];
54-
let lastSchemaArray = Array.isArray(lastSchemaItem)
55-
? lastSchemaItem
56-
: lastSchemaItem
57-
? [lastSchemaItem.trim()]
58-
: [];
59-
let isEnum = /^{\??enum}/i.test(lastSchemaArray[0]);
53+
let lastSchemaItem = previous.textSchema
54+
? previous.textSchema[previous.textSchema.length - 1]
55+
: [''];
56+
let isEnum = /^{\??enum}/i.test(lastSchemaItem[0].trim());
6057
// Enums inside schemas need to be collected as a single item
6158
if (isEnum) {
62-
lastSchemaArray = lastSchemaArray.concat(line.trim());
63-
previous.schema[previous.schema.length - 1] = lastSchemaArray;
59+
lastSchemaItem = lastSchemaItem.concat(line.trim());
60+
previous.textSchema[previous.textSchema.length - 1] = lastSchemaItem;
6461
} else {
6562
previous.values = previous.values.concat(line.trim());
6663
}
@@ -125,7 +122,8 @@ class CommentDefinitionParser {
125122
return values.join(' ').split(/\s+/);
126123
}
127124

128-
getParameter (values, schema) {
125+
getParameter (values, textSchema = [], depth = 0) {
126+
129127
if (!Array.isArray(values)) {
130128
values = [values];
131129
}
@@ -168,7 +166,7 @@ class CommentDefinitionParser {
168166
}
169167

170168
if (type === 'enum') {
171-
let splitValue = values[0].split(' ').slice(1);
169+
let splitValue = values[0].trim().split(' ').slice(1);
172170
param.name = splitValue.shift();
173171
param.description = splitValue.join(' ');
174172
param.members = this.parseEnumMembers(values.slice(1));
@@ -178,11 +176,15 @@ class CommentDefinitionParser {
178176
let splitValue = value.split(' ');
179177
param.name = splitValue.shift();
180178
param.description = splitValue.join(' ');
181-
if (schema) {
179+
if (textSchema.length) {
182180
if (!['object', 'array'].includes(type)) {
183181
throw new Error(`Can not provide schema for type: "${type}"`);
184182
}
185-
param.schema = this.parseSchema(schema, type === 'object');
183+
let schemas = this.parseSchemas(textSchema, depth, type === 'object');
184+
param.schema = schemas.shift();
185+
if (schemas.length) {
186+
param.alternateSchemas = schemas;
187+
}
186188
}
187189

188190
if (defaultMetafield) {
@@ -197,8 +199,8 @@ class CommentDefinitionParser {
197199
throw new Error(`Options {?}: Not allowed for type "${param.type}" for parameter "${param.name}"`);
198200
}
199201
param.options = {};
200-
var opt = options.split(' ');
201-
var values;
202+
let opt = options.split(' ');
203+
let values;
202204
try {
203205
values = JSON.parse(options);
204206
} catch (e) {
@@ -208,7 +210,7 @@ class CommentDefinitionParser {
208210
if (values.length === 0) {
209211
throw new Error(`Options {?}: Must provide non-zero options length`);
210212
}
211-
var paramType = (param.type === 'object.keyql.query' || param.type === 'object.keyql.order')
213+
let paramType = (param.type === 'object.keyql.query' || param.type === 'object.keyql.order')
212214
? 'string'
213215
: param.type;
214216
values.forEach(v => {
@@ -221,12 +223,12 @@ class CommentDefinitionParser {
221223
if (!opt[0] || opt[0].indexOf('.') === -1) {
222224
throw new Error(`Options {?}: Invalid API call for "${param.name}": "${opt[1]}"`);
223225
} else {
224-
var call = /^(.+?)(?:\((.*)\))?$/gi.exec(opt[0]);
226+
let call = /^(.+?)(?:\((.*)\))?$/gi.exec(opt[0]);
225227
param.options.lib = call[1];
226228
if (call[2]) {
227229
param.options.map = (call[2] || '').split(',').reduce((map, p) => {
228-
var key = p.split('=')[0];
229-
var value = p.split('=').slice(1).join('=');
230+
let key = p.split('=')[0];
231+
let value = p.split('=').slice(1).join('=');
230232
if (!key.match(/^[a-z0-9\_]+$/i) || !value.match(/^[a-z0-9\_]+$/i)) {
231233
throw new Error(`Options {?}: Invalid API call parameters for "${param.name}": "${call[2]}"`);
232234
}
@@ -261,17 +263,17 @@ class CommentDefinitionParser {
261263

262264
let field = data.field;
263265
let values = data.values;
264-
let schema = data.schema;
266+
let textSchema = data.textSchema;
265267

266268
try {
267269

268270
if (field === 'description') {
269271
definition.description = values.join('\n');
270272
} else if (field === 'param') {
271273
definition.params = definition.params || [];
272-
definition.params.push(this.getParameter(values, schema));
274+
definition.params.push(this.getParameter(values, textSchema));
273275
} else if (field === 'returns') {
274-
definition.returns = this.getParameter(values, schema);
276+
definition.returns = this.getParameter(values, textSchema);
275277
} else if (field === 'bg') {
276278
definition.bg = this.getBg(values);
277279
} else if (field === 'acl') {
@@ -317,71 +319,57 @@ class CommentDefinitionParser {
317319

318320
}
319321

320-
parseSchema (schema, multipleKeys) {
321-
if (schema.length && !Array.isArray(schema[0]) && this.getLineDepth(schema[0])) {
322-
throw new Error(`Invalid Schema definition at: "${schema[0]}"`);
322+
parseSchemas (textSchema, depth = 0, allowMultipleEntries = false) {
323+
let schemas = [];
324+
while (textSchema.length) {
325+
let line = textSchema[0][0];
326+
let params = [];
327+
let schema = this._parseSchema(textSchema, params, depth);
328+
if (!allowMultipleEntries && schema.length > 1) {
329+
throw new Error(`Invalid Schema definition at "${line}": Schema for "array" can only support one top-level key that maps to every element.`);
330+
}
331+
schemas.push(schema);
323332
}
324-
325-
let parsedSchema = this._parseSchema(schema, [], 0);
326-
327-
if (!multipleKeys && parsedSchema.length > 1) {
328-
throw new Error('Schema for "array" can only support one top-level key that maps to every element.');
333+
if (!allowMultipleEntries && schemas.length > 1) {
334+
throw new Error(`Invalid Schema definition at "${line}": Schema for "array" does not support OR (alternateSchemas).`);
335+
} else if (schemas.filter(params => !params.length).length > 0) {
336+
throw new Error(`Invalid Schema definition at "${line}": OR (alternateSchemas) requires all schema lengths to be non-zero`);
329337
}
330-
331-
return parsedSchema;
332-
338+
return schemas;
333339
}
334340

335-
_parseSchema (schema, params, lastDepth) {
341+
_parseSchema (textSchema, params = [], depth = 0) {
336342

337-
if (!schema.length) {
338-
return params;
343+
let values = textSchema.shift();
344+
let line = values[0];
345+
let curDepth = this.getLineDepth(line);
346+
if (depth !== curDepth) {
347+
throw new Error(`Invalid Schema definition at: "${line}", invalid line depth (expecting ${depth}, found ${curDepth})`);
339348
}
340-
341-
let line = schema.shift();
342-
let lastParam = params[params.length - 1];
343-
344-
345-
// Just call this.getParameter to collect the enum and continue
346-
if (Array.isArray(line)) {
347-
let param = this.getParameter(line);
348-
349-
if (lastParam && (lastParam.type === 'object' || lastParam.type === 'array')) {
350-
lastParam.schema = this._parseSchema(schema, [param], lastDepth + 1);
351-
return this._parseSchema(schema, params, lastDepth);
349+
if (line.trim() === 'OR') {
350+
if (!textSchema.length) {
351+
throw new Error(`Invalid Schema definition at "${line}": OR (alternateSchemas) can not end a schema definition`);
352352
}
353-
354-
params.push(param);
355-
return this._parseSchema(schema, params, lastDepth);
356-
}
357-
358-
let depth = this.getLineDepth(line);
359-
let param = this.getParameter(line.trim());
360-
let step = depth - lastDepth;
361-
362-
if (!lastParam || step === 0) {
363-
params.push(param);
364-
return this._parseSchema(schema, params, depth);
365-
}
366-
367-
if (step === 1) {
368-
if (lastParam.type !== 'object' && lastParam.type !== 'array') {
369-
throw new Error(`Invalid Schema definition at: "${line}", can only define schemas for an object or array`);
353+
return params;
354+
} else {
355+
let subSchemaEnd = textSchema.findIndex(values => {
356+
let line = values[0];
357+
return this.getLineDepth(line) <= curDepth;
358+
});
359+
let subSchema;
360+
if (subSchemaEnd === -1) {
361+
subSchema = textSchema.splice(0, textSchema.length);
362+
} else if (subSchemaEnd) {
363+
subSchema = textSchema.splice(0, subSchemaEnd);
370364
}
371-
lastParam.schema = this._parseSchema(schema, [param], depth);
372-
return this._parseSchema(schema, params, lastDepth);
373-
}
374-
375-
if (step > 1) {
376-
throw new Error(`Invalid Schema definition at: "${line}", spacing invalid`);
365+
params.push(this.getParameter(values, subSchema, depth + 1));
366+
return textSchema.length
367+
? this._parseSchema(textSchema, params, depth)
368+
: params;
377369
}
378-
379-
schema.unshift(line);
380-
return params;
381-
382370
}
383371

384-
getLineDepth(line) {
372+
getLineDepth (line) {
385373

386374
let depth = (line.length - line.replace(/^\s*/, '').length) / 2;
387375

lib/types.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ const _validations = {
9090
try {
9191
KeyQL.validateOrderObject(v, options);
9292
} catch (e) {
93-
console.log('VALIDATE?', e);
9493
return false;
9594
}
9695
return true;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* Accepts multiple schemas for an object
3+
* @param {object} fileOrFolder
4+
* @ {string} name
5+
* @ {number} size
6+
* @ OR
7+
* @ {string} name2
8+
* @ {number} size2
9+
* @returns {object}
10+
*/
11+
module.exports = async (fileOrFolder) => {
12+
return true;
13+
};
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Accepts multiple schemas for an object
3+
* @param {object} fileOrFolder
4+
* @ OR
5+
* @ {string} name
6+
* @ {number} size
7+
* @returns {object}
8+
*/
9+
module.exports = async (fileOrFolder) => {
10+
return true;
11+
};
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/**
2+
* Accepts multiple schemas for an object
3+
* @param {object} fileOrFolder
4+
* @ {string} name
5+
* @ {number} size
6+
* @ OR
7+
* @returns {object}
8+
*/
9+
module.exports = async (fileOrFolder) => {
10+
return true;
11+
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Accepts multiple schemas for an object
3+
* @param {object} fileOrFolder
4+
* @ {string} name
5+
* @ {number} size
6+
* @ OR
7+
* @ {string} name2
8+
* @ {number} size2
9+
* @ {object} props
10+
* @ {string} label
11+
* @ OR
12+
* @ {number} size
13+
* @returns {object}
14+
*/
15+
module.exports = async (fileOrFolder) => {
16+
return true;
17+
};
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
/**
2+
* Accepts multiple schemas for an object
3+
* @param {object} fileOrFolder
4+
* @ {string} name
5+
* @ {number} size
6+
* @ OR
7+
* @ {string} name2
8+
* @ {number} size2
9+
* @returns {object}
10+
*/
11+
module.exports = async (fileOrFolder) => {
12+
return true;
13+
};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Accepts multiple schemas for an object
3+
* @param {object} fileOrFolder
4+
* @ {string} name
5+
* @ {number} size
6+
* @ OR
7+
* @ {string} name2
8+
* @ {number} size2
9+
* @ {object} props
10+
* @ {string} label
11+
* @ OR
12+
* @ {number} size
13+
* @returns {object}
14+
*/
15+
module.exports = async (fileOrFolder) => {
16+
return true;
17+
};
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/**
2+
* Accepts multiple schemas for an object
3+
* @param {object} fileOrFolder
4+
* @ {string} name
5+
* @ {number} size
6+
* @ OR
7+
* @ {string} name2
8+
* @ {number} size2
9+
* @ {object} props
10+
* @ {string} label
11+
* @ OR
12+
* @ {number} size
13+
* @returns {object} fileOrFolder
14+
* @ {string} name
15+
* @ {number} size
16+
* @ OR
17+
* @ {string} name2
18+
* @ {number} size2
19+
* @ {object} props
20+
* @ {string} label
21+
* @ OR
22+
* @ {number} size
23+
*/
24+
module.exports = async (fileOrFolder) => {
25+
return true;
26+
};

0 commit comments

Comments
 (0)