UNPKG

@openfga/syntax-transformer

Version:

Javascript implementation of ANTLR Grammar for the OpenFGA DSL and parser from and to the OpenFGA JSON Syntax

501 lines (500 loc) 22.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ExceptionCollector = exports.createMaximumOneDirectRelationship = exports.createSchemaVersionRequiredError = exports.createInvalidSchemaVersionError = void 0; exports.constructTransformationError = constructTransformationError; const errors_1 = require("../errors"); const keywords_1 = require("../validator/keywords"); const createInvalidName = (props, clause, typeName) => { const { errors, lines, lineIndex, symbol, file, module } = props; const errorMessage = (typeName ? `relation '${symbol}' of type '${typeName}' ` : `type '${symbol}' `) + `does not match naming rule: '${clause}'.`; errors.push(constructValidationError({ message: errorMessage, lines, lineIndex, metadata: { symbol, errorType: errors_1.ValidationError.InvalidName, file, module }, })); }; const createReservedTypeNameError = (props) => { const { errors, lines, lineIndex, symbol, file, module } = props; errors.push(constructValidationError({ message: `a type cannot be named '${keywords_1.Keyword.SELF}' or '${keywords_1.ReservedKeywords.THIS}'.`, lines, lineIndex, metadata: { symbol, errorType: errors_1.ValidationError.ReservedTypeKeywords, file, module }, })); }; const createReservedRelationNameError = (props) => { const { errors, lines, lineIndex, symbol, file, module } = props; errors.push(constructValidationError({ message: `a relation cannot be named '${keywords_1.Keyword.SELF}' or '${keywords_1.ReservedKeywords.THIS}'.`, lines, lineIndex, metadata: { symbol, errorType: errors_1.ValidationError.ReservedRelationKeywords, file, module }, })); }; const createTupleUsersetRequireDirectError = (props) => { const { errors, lines, lineIndex, symbol, file, module, type, relation } = props; errors.push(constructValidationError({ message: `\`${symbol}\` relation used inside from allows only direct relation.`, lines, lineIndex, customResolver: (wordIdx, rawLine, value) => { const clauseStartsAt = rawLine.indexOf("from") + "from".length; wordIdx = clauseStartsAt + rawLine.slice(clauseStartsAt).indexOf(value); return wordIdx; }, metadata: { symbol, errorType: errors_1.ValidationError.TuplesetNotDirect, file, module, typeName: type, relation }, })); }; const createNoEntryPointLoopError = (props, typeName) => { const { errors, lines, lineIndex, symbol, file, module } = props; errors.push(constructValidationError({ message: `\`${symbol}\` is an impossible relation for \`${typeName}\` (potential loop).`, lines, lineIndex, metadata: { symbol, errorType: errors_1.ValidationError.RelationNoEntrypoint, relation: symbol, typeName, file, module }, })); }; const createNoEntryPointError = (props, typeName) => { const { errors, lines, lineIndex, symbol, module, file } = props; errors.push(constructValidationError({ message: `\`${symbol}\` is an impossible relation for \`${typeName}\` (no entrypoint).`, lines, lineIndex, metadata: { symbol, errorType: errors_1.ValidationError.RelationNoEntrypoint, relation: symbol, typeName, module, file }, })); }; // targetType and targetRelation, typeRestriction const createInvalidTypeRelationError = (props, typeName, relationName, offendingRelation, offendingType) => { const { errors, lines, lineIndex, symbol, file, module } = props; errors.push(constructValidationError({ message: `\`${offendingRelation}\` is not a valid relation for \`${typeName}\`.`, lines, lineIndex, metadata: { symbol, errorType: errors_1.ValidationError.InvalidRelationType, relation: relationName, typeName, file, module, offendingType, }, })); }; const createInvalidRelationOnTuplesetError = (props, typeName, typeDef, relationName, offendingRelation, parent) => { const { errors, lines, lineIndex, symbol, file, module } = props; errors.push(constructValidationError({ message: `the \`${offendingRelation}\` relation definition on type \`${typeDef}\` is not valid: \`${offendingRelation}\` does not exist on \`${parent}\`, which is of type \`${typeName}\`.`, lines, lineIndex, metadata: { symbol, errorType: errors_1.ValidationError.InvalidRelationOnTupleset, relation: relationName, typeName: typeDef, file, module, }, })); }; const createInvalidConditionNameInParameterError = (props, typeName, relationName, conditionName) => { const { errors, lines, lineIndex, symbol, module, file } = props; errors.push(constructValidationError({ message: `\`${conditionName}\` is not a defined condition in the model.`, lines, lineIndex, metadata: { symbol, errorType: errors_1.ValidationError.ConditionNotDefined, relation: relationName, typeName, file, module, }, })); }; const createUnusedConditionError = (props) => { const { errors, lines, lineIndex, symbol, module, file } = props; errors.push(constructValidationError({ message: `\`${symbol}\` condition is not used in the model.`, lines, lineIndex, metadata: { symbol, errorType: errors_1.ValidationError.ConditionNotUsed, conditionName: symbol, module, file }, })); }; const createInvalidTypeError = (props, typeName) => { const { errors, lines, lineIndex, symbol, file, module, relation } = props; errors.push(constructValidationError({ message: `\`${symbol}\` is not a valid type.`, lines, lineIndex, metadata: { symbol, errorType: errors_1.ValidationError.InvalidType, typeName: typeName, file, module, relation }, customResolver: (wordIndex, rawLine, symbol) => { // Split line at definition as InvalidType should mark the value, not the key const re = new RegExp("\\b" + symbol + "\\b"); const splitLine = rawLine.split(":"); return splitLine[0].length + splitLine[1].search(re) + 1; }, })); }; const createAssignableRelationMustHaveTypesError = (props) => { const { errors, lines, lineIndex } = props; if (!(lines === null || lines === void 0 ? void 0 : lines.length) || lineIndex === undefined) { const actualValue = ""; errors.push(constructValidationError({ message: `assignable relation '${actualValue}' must have types`, metadata: { symbol: actualValue, errorType: errors_1.ValidationError.AssignableRelationsMustHaveType }, })); return; } const rawLine = lines[lineIndex]; const actualValue = rawLine.includes("[") ? rawLine.slice(rawLine.indexOf("["), rawLine.lastIndexOf("]")) : "self"; errors.push(constructValidationError({ message: `assignable relation '${actualValue}' must have types`, lines, lineIndex, customResolver: (wordIdx, rawLine, symbol) => { wordIdx = rawLine.indexOf(symbol.substring(1)); return wordIdx; }, metadata: { symbol: actualValue, errorType: errors_1.ValidationError.AssignableRelationsMustHaveType }, })); }; const createDuplicateTypeNameError = (props) => { const { errors, lines, lineIndex, symbol, file, module } = props; errors.push(constructValidationError({ message: `the type \`${symbol}\` is a duplicate.`, lines, lineIndex, metadata: { symbol, errorType: errors_1.ValidationError.DuplicatedError, typeName: symbol, file, module }, })); }; const createDuplicateTypeRestrictionError = (props, relationName, typeName) => { const { errors, lines, lineIndex, symbol, file, module } = props; errors.push(constructValidationError({ message: `the type restriction \`${symbol}\` is a duplicate in the relation \`${relationName}\`.`, lines, lineIndex, metadata: { symbol, errorType: errors_1.ValidationError.DuplicatedError, relation: relationName, typeName, file, module }, })); }; const createDuplicateRelationError = (props, relationName, typeName) => { const { errors, lines, lineIndex, symbol, file, module } = props; errors.push(constructValidationError({ message: `the partial relation definition \`${symbol}\` is a duplicate in the relation \`${relationName}\`.`, lines, lineIndex, metadata: { symbol, errorType: errors_1.ValidationError.DuplicatedError, relation: relationName, typeName, file, module }, })); }; const createDuplicateRelationshipDefinitionError = (props) => { const { errors, lines, lineIndex, symbol, file, module } = props; if (!(lines === null || lines === void 0 ? void 0 : lines.length) || lineIndex === undefined) { errors.push(new errors_1.ModelValidationSingleError({ msg: `duplicate relationship definition \`${symbol}\`.`, file, }, { symbol, errorType: errors_1.ValidationError.DuplicatedError, module })); return; } const rawLine = lines[lineIndex]; errors.push(new errors_1.ModelValidationSingleError({ msg: `duplicate relationship definition \`${symbol}\`.`, line: { start: lineIndex, end: lineIndex, }, column: { start: rawLine.indexOf(keywords_1.Keyword.DEFINE), end: rawLine.length, }, file, }, { symbol, errorType: errors_1.ValidationError.DuplicatedError, module })); }; const createAssignableTypeWildcardRelationError = (props) => { const { errors, lines, lineIndex, symbol, file, module, type, relation } = props; errors.push(constructValidationError({ message: `type restriction \`${symbol}\` cannot contain both wildcard and relation`, lines, lineIndex, metadata: { symbol, errorType: errors_1.ValidationError.TypeRestrictionCannotHaveWildcardAndRelation, relation, typeName: type, module, file, }, })); }; const createInvalidRelationError = (props, validRelations) => { const { errors, lines, lineIndex, symbol, file, module, type, relation } = props; const isInValid = !(validRelations === null || validRelations === void 0 ? void 0 : validRelations.includes(symbol)); if (isInValid) { errors.push(constructValidationError({ message: `the relation \`${symbol}\` does not exist.`, lines, lineIndex, metadata: { symbol, errorType: errors_1.ValidationError.MissingDefinition, relation, file, module, typeName: type }, })); } }; const createInvalidSchemaVersionError = (props) => { const { errors, lines, lineIndex, symbol } = props; errors.push(constructValidationError({ message: `invalid schema ${symbol}`, lines, lineIndex, metadata: { symbol, errorType: errors_1.ValidationError.InvalidSchema }, })); }; exports.createInvalidSchemaVersionError = createInvalidSchemaVersionError; const createSchemaVersionRequiredError = (props) => { const { errors, lines, lineIndex, symbol } = props; errors.push(constructValidationError({ message: "schema version required", lines, lineIndex, metadata: { symbol, errorType: errors_1.ValidationError.SchemaVersionRequired }, })); }; exports.createSchemaVersionRequiredError = createSchemaVersionRequiredError; const createMaximumOneDirectRelationship = (props) => { const { errors, lines, lineIndex, symbol } = props; errors.push(constructValidationError({ message: "each relationship must have at most 1 set of direct relations defined.", lines, lineIndex, metadata: { symbol, errorType: errors_1.ValidationError.AssignableRelationsMustHaveType }, customResolver: (wordIdx, rawLine, symbol) => { wordIdx = rawLine.indexOf(symbol.substring(1)); return wordIdx; }, })); }; exports.createMaximumOneDirectRelationship = createMaximumOneDirectRelationship; const createDifferentNestedConditionNameError = (props, condition, nestedConditionName) => { const { errors } = props; errors.push(constructValidationError({ message: `condition key is \`${condition}\` but nested name property is ${nestedConditionName}`, metadata: { symbol: nestedConditionName, errorType: errors_1.ValidationError.DifferentNestedConditionName }, })); }; function createMultipleModuleInSingleFileError(props, file, modules) { const { errors } = props; errors.push(constructValidationError({ message: `file ${file} would contain multiple module definitions (${modules.join(", ")}) when transforming to DSL. Only one module can be defined per file.`, metadata: { symbol: file, errorType: errors_1.ValidationError.MultipleModulesInFile }, })); } function constructValidationError(props) { var _a; const { message, lines, lineIndex, customResolver, metadata } = props; const errorProps = { msg: message, file: metadata.file, }; const errorMetadata = { symbol: metadata === null || metadata === void 0 ? void 0 : metadata.symbol, errorType: metadata.errorType, module: metadata.module, relation: metadata.relation, type: metadata.typeName, offendingType: metadata.offendingType, }; if ((lines === null || lines === void 0 ? void 0 : lines.length) && lineIndex != undefined) { const rawLine = lines[lineIndex]; const re = new RegExp("\\b" + metadata.symbol + "\\b"); let wordIdx = rawLine === null || rawLine === void 0 ? void 0 : rawLine.search(re); if (isNaN(wordIdx) || wordIdx === -1) { wordIdx = 0; } if (typeof customResolver === "function") { wordIdx = customResolver(wordIdx, rawLine, metadata.symbol); } errorProps.line = { start: lineIndex, end: lineIndex }; errorProps.column = { start: wordIdx, end: wordIdx + (((_a = metadata.symbol) === null || _a === void 0 ? void 0 : _a.length) || 0) }; } return new errors_1.ModelValidationSingleError(errorProps, errorMetadata); } class ExceptionCollector { constructor(errors, lines) { this.errors = errors; this.lines = lines; } raiseInvalidName(symbol, clause, typeName, lineIndex, metadata) { createInvalidName({ errors: this.errors, lines: this.lines, lineIndex, symbol, file: metadata === null || metadata === void 0 ? void 0 : metadata.file, module: metadata === null || metadata === void 0 ? void 0 : metadata.module }, clause, typeName); } raiseReservedTypeName(symbol, lineIndex, metadata) { createReservedTypeNameError({ errors: this.errors, lines: this.lines, lineIndex, symbol, file: metadata === null || metadata === void 0 ? void 0 : metadata.file, module: metadata === null || metadata === void 0 ? void 0 : metadata.module, }); } raiseReservedRelationName(symbol, lineIndex, metadata) { createReservedRelationNameError({ errors: this.errors, lines: this.lines, lineIndex, symbol, file: metadata === null || metadata === void 0 ? void 0 : metadata.file, module: metadata === null || metadata === void 0 ? void 0 : metadata.module, }); } raiseTupleUsersetRequiresDirect(symbol, type, relation, meta, lineIndex) { createTupleUsersetRequireDirectError({ errors: this.errors, lines: this.lines, lineIndex, symbol, file: meta.file, module: meta.module, relation, type, }); } raiseDuplicateTypeName(symbol, meta, lineIndex) { createDuplicateTypeNameError({ errors: this.errors, lines: this.lines, lineIndex, symbol, module: meta.module, file: meta.file, }); } raiseDuplicateTypeRestriction(symbol, relationName, typeName, meta, lineIndex) { createDuplicateTypeRestrictionError({ errors: this.errors, lines: this.lines, lineIndex, symbol, module: meta.module, file: meta.file }, relationName, typeName); } raiseDuplicateType(symbol, relationName, typeName, meta, lineIndex) { createDuplicateRelationError({ errors: this.errors, lines: this.lines, lineIndex, symbol, module: meta.module, file: meta.file }, relationName, typeName); } raiseDuplicateRelationshipDefinition(symbol, meta, lineIndex) { createDuplicateRelationshipDefinitionError({ errors: this.errors, lines: this.lines, lineIndex, symbol, module: meta.module, file: meta.file, }); } raiseNoEntryPointLoop(symbol, typeName, meta, lineIndex) { createNoEntryPointLoopError({ errors: this.errors, lines: this.lines, lineIndex, symbol, module: meta.module, file: meta.file }, typeName); } raiseNoEntryPoint(symbol, typeName, meta, lineIndex) { createNoEntryPointError({ errors: this.errors, lines: this.lines, lineIndex, symbol, file: meta.file, module: meta.module }, typeName); } raiseInvalidRelationOnTupleset(symbol, typeName, typeDef, relationName, offendingRelation, parent, lineIndex, meta) { createInvalidRelationOnTuplesetError({ errors: this.errors, lines: this.lines, lineIndex, symbol, file: meta === null || meta === void 0 ? void 0 : meta.file, module: meta === null || meta === void 0 ? void 0 : meta.module, }, typeName, typeDef, relationName, offendingRelation, parent); } raiseInvalidTypeRelation(symbol, typeName, relationName, offendingRelation, offendingType, lineIndex, meta) { createInvalidTypeRelationError({ errors: this.errors, lines: this.lines, lineIndex, symbol, file: meta === null || meta === void 0 ? void 0 : meta.file, module: meta === null || meta === void 0 ? void 0 : meta.module, }, typeName, relationName, offendingRelation, offendingType); } raiseInvalidType(symbol, typeName, relation, meta, lineIndex) { createInvalidTypeError({ errors: this.errors, lines: this.lines, lineIndex, symbol, module: meta.module, file: meta.file, relation }, typeName); } raiseAssignableRelationMustHaveTypes(symbol, lineIndex) { createAssignableRelationMustHaveTypesError({ errors: this.errors, lines: this.lines, lineIndex, symbol }); } raiseAssignableTypeWildcardRelation(symbol, type, relation, meta, lineIndex) { createAssignableTypeWildcardRelationError({ errors: this.errors, lines: this.lines, lineIndex, symbol, relation, type, module: meta.module, file: meta.file, }); } raiseInvalidRelationError(symbol, type, relation, validRelations, lineIndex, metadata) { createInvalidRelationError({ errors: this.errors, lines: this.lines, lineIndex, symbol, file: metadata === null || metadata === void 0 ? void 0 : metadata.file, module: metadata === null || metadata === void 0 ? void 0 : metadata.module, type, relation, }, validRelations); } raiseInvalidSchemaVersion(symbol, lineIndex) { (0, exports.createInvalidSchemaVersionError)({ errors: this.errors, lines: this.lines, lineIndex, symbol }); } raiseSchemaVersionRequired(symbol, lineIndex) { (0, exports.createSchemaVersionRequiredError)({ errors: this.errors, lines: this.lines, lineIndex, symbol }); } raiseMaximumOneDirectRelationship(symbol, lineIndex) { (0, exports.createMaximumOneDirectRelationship)({ errors: this.errors, lines: this.lines, lineIndex, symbol }); } raiseInvalidConditionNameInParameter(symbol, typeName, relationName, conditionName, meta, lineIndex) { createInvalidConditionNameInParameterError({ errors: this.errors, lines: this.lines, lineIndex, symbol, module: meta.module, file: meta.file }, typeName, relationName, conditionName); } raiseUnusedCondition(symbol, meta, lineIndex) { createUnusedConditionError({ errors: this.errors, lines: this.lines, lineIndex, symbol, module: meta.module, file: meta.file, }); } raiseDifferentNestedConditionName(condition, nestedConditionName) { createDifferentNestedConditionNameError({ errors: this.errors, symbol: condition, }, condition, nestedConditionName); } raiseMultipleModulesInSingleFile(file, modules) { createMultipleModuleInSingleFileError({ errors: this.errors, lines: this.lines, symbol: file, }, file, Array.from(modules)); } } exports.ExceptionCollector = ExceptionCollector; function constructTransformationError(props) { var _a; const { message, lines, lineIndex, metadata } = props; if (!(lines === null || lines === void 0 ? void 0 : lines.length) || lineIndex === undefined) { return new errors_1.ModuleTransformationSingleError({ msg: message, }, { symbol: metadata === null || metadata === void 0 ? void 0 : metadata.symbol }); } const rawLine = lines[lineIndex]; const re = new RegExp("\\b" + metadata.symbol + "\\b"); let wordIdx = rawLine === null || rawLine === void 0 ? void 0 : rawLine.search(re); if (isNaN(wordIdx) || wordIdx === -1) { wordIdx = 0; } return new errors_1.ModuleTransformationSingleError({ line: { start: lineIndex, end: lineIndex }, column: { start: wordIdx, end: wordIdx + (((_a = metadata.symbol) === null || _a === void 0 ? void 0 : _a.length) || 0) }, msg: message, file: metadata.file, }, { symbol: metadata === null || metadata === void 0 ? void 0 : metadata.symbol }); }