UNPKG

node-puppetdbquery

Version:

a simple query language parser for PuppetDB v4 API

140 lines (137 loc) 4.27 kB
import timespec from 'timespec'; import { visit } from './ast'; import { formatLocation, capitalizeClass, regexpEscape, capitalize } from './util'; const comparison = (operator, left, right) => { if (operator === '!=' || operator === '!~') { return ['not', [operator[1], left, right]]; } return [operator, left, right]; }; export default (ast) => { const mode = ['fact']; return visit(ast, { visitComparison(path) { this.traverse(path); // Function to handle negating comparisons if (mode[0] === 'fact') { return ['in', 'certname', ['extract', 'certname', ['select_fact_contents', ['and', path.node.left, comparison(path.node.operator, 'value', path.node.right)]]]]; } else if (mode[0] === 'subquery') { let left; if (path.node.left.length === 1) { left = path.node.left[0]; } else { left = path.node.left; } return comparison(path.node.operator, left, path.node.right); } else if (mode[0] === 'resource') { if (path.node.left[0] === 'tag') { return comparison(path.node.operator, path.node.left[0], path.node.right); } return comparison(path.node.operator, ['parameter', path.node.left[0]], path.node.right); } throw Error(`Unknown mode ${mode}`); }, visitBoolean(path) { // returning false to use it as a replacement doesn't work path.replace(path.node.value); return false; }, visitString(path) { return path.node.value; }, visitNumber(path) { return path.node.value; }, visitDate(path) { try { return timespec.parse(path.node.value).toISOString(); } catch (error) { const loc = formatLocation(path.node); throw new Error(`Failed to parse date: "${path.node.value}" at ${loc}`); } }, visitAndExpression(path) { this.traverse(path); return ['and', path.node.left, path.node.right]; }, visitOrExpression(path) { this.traverse(path); return ['or', path.node.left, path.node.right]; }, visitNotExpression(path) { this.traverse(path); return ['not', path.node.expression]; }, visitQuery(path) { this.traverse(path); return path.node.expression; }, visitParentesizedExpression(path) { this.traverse(path); return path.node.expression; }, visitBlockExpression(path) { this.traverse(path); return path.node.expression; }, visitSubquery(path) { mode.unshift('subquery'); this.traverse(path); mode.shift(); return ['in', 'certname', ['extract', 'certname', [`select_${path.node.endpoint}s`, path.node.expression]]]; }, visitRegexpNodeMatch(path) { mode.unshift('regexp'); this.traverse(path); mode.shift(); return ['~', 'certname', regexpEscape(path.node.value.join('.'))]; }, visitIdentifierPath(path) { this.traverse(path); if (mode[0] === 'fact') { return [ (path.node.regexp ? '~>' : '='), 'path', path.node.components, ]; } return path.node.components; }, visitRegexpIdentifier(path) { return path.node.name; }, visitIdentifier(path) { if (path.parentPath.node.regexp) { return regexpEscape(path.node.name); } return path.node.name; }, visitResource(path) { const regexp = (path.node.title.type === 'RegexpIdentifier'); mode.unshift('resource'); this.traverse(path); mode.shift(); let { title } = path.node; if (!regexp && capitalize(path.node.res_type) === 'Class') { title = capitalizeClass(title); } const andExpr = ['and', ['=', 'type', capitalizeClass(path.node.res_type)], [(regexp ? '~' : '='), 'title', title], ['=', 'exported', path.node.exported]]; if (path.node.parameters) { andExpr.push(path.node.parameters); } return ['in', 'certname', ['extract', 'certname', ['select_resources', andExpr]]]; }, }); };