@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
JavaScript
"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 });
}