Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Use Globalize for error formatting #917

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
Draft
26 changes: 20 additions & 6 deletions packages/concerto-core/lib/basemodelmanager.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,12 @@ class BaseModelManager {
const existingModelFileName = this.modelFiles[modelFile.getNamespace()].getName();
const postfix = existingModelFileName ? ` in file ${existingModelFileName}` : '';
const prefix = modelFile.getName() ? ` specified in file ${modelFile.getName()}` : '';
let errMsg = `Namespace ${modelFile.getNamespace()}${prefix} is already declared${postfix}`;
throw new Error(errMsg);
let formatter = Globalize.messageFormatter('basemodelmanager-namespaceexists');
throw new Error(formatter({
namespace: modelFile.getNamespace(),
prefix: prefix,
postfix: postfix
}));
}

/**
Expand All @@ -219,7 +223,7 @@ class BaseModelManager {
debug(NAME, 'addModelFile', modelFile, fileName);

if(this.isStrict() && !modelFile.getVersion()) {
throw new Error('Cannot add an unversioned namespace when \'strict\' is true');
throw new Error(Globalize.formatMessage('basemodelmanager-unversionednamespace'));
}

if (!this.modelFiles[modelFile.getNamespace()]) {
Expand Down Expand Up @@ -252,7 +256,11 @@ class BaseModelManager {
const { version: metamodelVersion } = ModelUtil.parseNamespace(MetaModelNamespace);

if (modelFileVersion !== metamodelVersion){
throw new MetamodelException(`Model file version ${modelFileVersion} does not match metamodel version ${metamodelVersion}`);
let formatter = Globalize.messageFormatter('basemodelmanager-metamodelversion');
throw new MetamodelException(formatter({
modelFileVersion: modelFileVersion,
metamodelVersion: metamodelVersion
}));
}

const alreadyHasMetamodel = !!this.getModelFile(MetaModelNamespace);
Expand Down Expand Up @@ -326,7 +334,10 @@ class BaseModelManager {
} else {
let existing = this.modelFiles[modelFile.getNamespace()];
if (!existing) {
throw new Error(`Model file for namespace ${modelFile.getNamespace()} not found`);
let formatter = Globalize.messageFormatter('basemodelmanager-modelfilenotfound');
throw new Error(formatter({
namespace: modelFile.getNamespace()
}));
}
if (!disableValidation) {
modelFile.validate();
Expand All @@ -342,7 +353,10 @@ class BaseModelManager {
*/
deleteModelFile(namespace) {
if (!this.modelFiles[namespace]) {
throw new Error('Model file does not exist');
let formatter = Globalize.messageFormatter('basemodelmanager-modelfilenotfound');
throw new Error(formatter({
namespace: namespace
}));
} else {
delete this.modelFiles[namespace];
}
Expand Down
23 changes: 18 additions & 5 deletions packages/concerto-core/lib/concerto.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const URIJS = require('urijs');
const RESOURCE_SCHEME = 'resource';
const { TypedStack } = require('@accordproject/concerto-util');
const ObjectValidator = require('./serializer/objectvalidator');
const Globalize = require('./globalize');

// Types needed for TypeScript generation.
/* eslint-disable no-unused-vars */
Expand Down Expand Up @@ -81,7 +82,7 @@ class Concerto {
*/
getTypeDeclaration(obj) {
if (!obj.$class) {
throw new Error('Input object does not have a $class attribute.');
throw new Error(Globalize.formatMessage('concerto-gettypedeclaration-noclassattribute'));
}

return this.modelManager.getType(obj.$class);
Expand All @@ -96,7 +97,10 @@ class Concerto {
const typeDeclaration = this.getTypeDeclaration(obj);
const idField = typeDeclaration.getIdentifierFieldName();
if (!idField) {
throw new Error(`Object does not have an identifier: ${JSON.stringify(obj)}`);
let formatter = Globalize.messageFormatter('concerto-getidentifier-noidfield');
throw new Error(formatter({
object: JSON.stringify(obj)
}));
}
return obj[idField];
}
Expand Down Expand Up @@ -170,15 +174,24 @@ class Concerto {
try {
uriComponents = URIJS.parse(uri);
} catch (err) {
throw new Error('Invalid URI: ' + uri);
let formatter = Globalize.messageFormatter('concerto-invaliduri');
throw new Error(formatter({
uri: uri
}));
}

const scheme = uriComponents.protocol;
if (scheme && scheme !== RESOURCE_SCHEME) {
throw new Error('Invalid URI scheme: ' + uri);
let formatter = Globalize.messageFormatter('concerto-invalidurischeme');
throw new Error(formatter({
uri: uri
}));
}
if (uriComponents.username || uriComponents.password || uriComponents.port || uriComponents.query) {
throw new Error('Invalid resource URI format: ' + uri);
let formatter = Globalize.messageFormatter('concerto-invaliduriformat');
throw new Error(formatter({
uri: uri
}));
}

return {
Expand Down
23 changes: 16 additions & 7 deletions packages/concerto-core/lib/factory.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,17 @@ class Factory {
}
// if regex on identifier field & provided id does not match regex, throw error
if(idFullField?.validator?.regex && (idFullField.validator?.regex.test(id) === false)) {
throw new Error('Provided id does not match regex: ' + idFullField?.validator?.regex);
let formatter = Globalize.messageFormatter('factory-newinstance-invalididentifierregex');
throw new Error(formatter({
regex: idFullField?.validator?.regex,
}));
}
}
} else if(id) {
throw new Error('Type is not identifiable ' + classDecl.getFullyQualifiedName());
let formatter = Globalize.messageFormatter('factory-newinstance-typenotidentifiable');
throw new Error(formatter({
type: classDecl.getFullyQualifiedName()
}));
}

let newObj = null;
Expand Down Expand Up @@ -195,7 +201,10 @@ class Factory {
const fqn = ModelUtil.getFullyQualifiedName(ns, type);
const classDecl = this.modelManager.getType(fqn);
if(!classDecl.isIdentified()) {
throw new Error(`Cannot create a relationship to ${fqn}, it is not identifiable.`);
let formatter = Globalize.messageFormatter('factory-newrelationship-notidentifiable');
throw new Error(formatter({
fqn: fqn
}));
}
return new Relationship(this.modelManager, classDecl, ns, type, id);
}
Expand All @@ -215,9 +224,9 @@ class Factory {
*/
newTransaction(ns, type, id, options) {
if (!ns) {
throw new Error('ns not specified');
throw new Error(Globalize.formatMessage('factory-nsnotspecified'));
} else if (!type) {
throw new Error('type not specified');
throw new Error(Globalize.formatMessage('factory-typenotspecified'));
}
let transaction = this.newResource(ns, type, id, options);
const classDeclaration = transaction.getClassDeclaration();
Expand Down Expand Up @@ -245,9 +254,9 @@ class Factory {
*/
newEvent(ns, type, id, options) {
if (!ns) {
throw new Error('ns not specified');
throw new Error(Globalize.formatMessage('factory-nsnotspecified'));
} else if (!type) {
throw new Error('type not specified');
throw new Error(Globalize.formatMessage('factory-typenotspecified'));
}
let event = this.newResource(ns, type, id, options);
const classDeclaration = event.getClassDeclaration();
Expand Down
30 changes: 20 additions & 10 deletions packages/concerto-core/lib/introspect/modelfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,18 @@ class ModelFile extends Decorated {
this.version = null;

if(!ast || typeof ast !== 'object') {
throw new Error('ModelFile expects a Concerto model AST as input.');
throw new Error(Globalize.formatMessage('modelfile-constructor-astnotobject'));
}

this.ast = ast;

if(definitions && typeof definitions !== 'string') {
throw new Error('ModelFile expects an (optional) Concerto model definition as a string.');
throw new Error(Globalize.formatMessage('modelfile-constructor-defnotstring'));
}
this.definitions = definitions;

if(fileName && typeof fileName !== 'string') {
throw new Error('ModelFile expects an (optional) filename as a string.');
throw new Error(Globalize.formatMessage('modelfile-constructor-filenamenotstring'));
}
this.fileName = fileName;

Expand Down Expand Up @@ -482,7 +482,7 @@ class ModelFile extends Decorated {
*/
getLocalType(type) {
if(!this.localTypes) {
throw new Error('Internal error: local types are not yet initialized. Do not try to resolve types inside `process`.');
throw new Error(Globalize.formatMessage('modelfile-getlocaltype-notinit'));
}

if(!type.startsWith(this.getNamespace())) {
Expand Down Expand Up @@ -698,7 +698,11 @@ class ModelFile extends Decorated {
if (semver.satisfies(packageJson.version, this.ast.concertoVersion, { includePrerelease: true })) {
this.concertoVersion = this.ast.concertoVersion;
} else {
throw new Error(`ModelFile expects Concerto version ${this.ast.concertoVersion} but this is ${packageJson.version}`);
let formatter = Globalize.messageFormatter('modelfile-incompatibleversion');
throw new Error(formatter({
concertoVersion: this.ast.concertoVersion,
packageJsonVersion: packageJson.version
}));
}
}
}
Expand All @@ -712,7 +716,10 @@ class ModelFile extends Decorated {
if(this.getModelManager().isStrict()) {
const nsInfo = ModelUtil.parseNamespace(imp.namespace);
if(!nsInfo.version) {
throw new Error(`Cannot use an unversioned import ${imp.namespace} when 'strict' option on Model Manager is set.`);
let formatter = Globalize.messageFormatter('modelfile-unversionedimport');
throw new Error(formatter({
namespace: imp.namespace
}));
}
}
}
Expand All @@ -728,7 +735,10 @@ class ModelFile extends Decorated {
const namespaceParts = nsInfo.name.split('.');
namespaceParts.forEach(part => {
if (!ModelUtil.isValidIdentifier(part)){
throw new IllegalModelException(`Invalid namespace part '${part}'`, this.modelFile, this.ast.location);
let formatter = Globalize.messageFormatter('modelfile-invalidnamespacepart');
throw new IllegalModelException(formatter({
part: part
}), this.modelFile, this.ast.location);
}
});

Expand All @@ -754,7 +764,7 @@ class ModelFile extends Decorated {
switch(imp.$class) {
case `${MetaModelNamespace}.ImportAll`:
if (this.getModelManager().isStrict()){
throw new Error('Wilcard Imports are not permitted in strict mode.');
throw new Error(Globalize.formatMessage('modelfile-wildcardimport-notallowed'));
}
Warning.printDeprecationWarning(
'Wilcard Imports are deprecated in this version of Concerto and will be removed in a future version.',
Expand All @@ -770,7 +780,7 @@ class ModelFile extends Decorated {
if (imp.aliasedTypes) {
imp.aliasedTypes.forEach(({ name, aliasedName }) => {
if(ModelUtil.isPrimitiveType(aliasedName)){
throw new Error('Types cannot be aliased to primitive type');
throw new Error(Globalize.formatMessage('modelfile-aliastype-primitive'));
}
aliasedTypes.set(name, aliasedName);
});
Expand All @@ -789,7 +799,7 @@ class ModelFile extends Decorated {
);
} else {
if (imp.aliasedTypes) {
throw new Error('Aliasing disabled, set importAliasing to true');
throw new Error(Globalize.formatMessage('modelfile-aliastype-disabled'));
}
imp.types.forEach((type) => {
this.importShortNames.set(type,`${imp.namespace}.${type}`);
Expand Down
23 changes: 17 additions & 6 deletions packages/concerto-core/lib/model/resourceid.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ const URIJS = require('urijs');

const ModelUtils = require('../modelutil');

const Globalize = require('../globalize');

const RESOURCE_SCHEME = 'resource';

/**
Expand All @@ -39,13 +41,13 @@ class ResourceId {
*/
constructor(namespace, type, id) {
if (!namespace) {
throw new Error('Missing namespace');
throw new Error(Globalize.formatMessage('resourceid-constructor-missingnamespace'));
}
if (!type) {
throw new Error('Missing type');
throw new Error(Globalize.formatMessage('resourceid-constructor-missingtype'));
}
if (!id) {
throw new Error('Missing id');
throw new Error(Globalize.formatMessage('resourceid-constructor-missingid'));
}

this.namespace = namespace;
Expand Down Expand Up @@ -73,16 +75,25 @@ class ResourceId {
try {
uriComponents = URIJS.parse(uri);
} catch (err){
throw new Error('Invalid URI: ' + uri);
let formatter = Globalize.messageFormatter('concerto-invaliduriformat');
throw new Error(formatter({
uri: uri
}));
}

const scheme = uriComponents.protocol;
// Accept legacy identifiers with missing URI scheme as valid
if (scheme && scheme !== RESOURCE_SCHEME) {
throw new Error('Invalid URI scheme: ' + uri);
let formatter = Globalize.messageFormatter('concerto-invalidurischeme');
throw new Error(formatter({
uri: uri
}));
}
if (uriComponents.username || uriComponents.password || uriComponents.port || uriComponents.query) {
throw new Error('Invalid resource URI format: ' + uri);
let formatter = Globalize.messageFormatter('concerto-invaliduriformat');
throw new Error(formatter({
uri: uri
}));
}

let namespace, type;
Expand Down
25 changes: 16 additions & 9 deletions packages/concerto-core/lib/model/validatedresource.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

const TypedStack = require('@accordproject/concerto-util').TypedStack;
const Resource = require('./resource');
const Globalize = require('../globalize');

/**
* ValidatedResource is a Resource that can validate that property
Expand Down Expand Up @@ -60,9 +61,11 @@ class ValidatedResource extends Resource {
let field = classDeclaration.getProperty(propName);

if (!field) {
throw new Error('The instance with id ' +
this.getIdentifier() + ' trying to set field ' +
propName + ' which is not declared in the model.');
let formatter = Globalize.messageFormatter('validatedresource-fieldnotdefined');
throw new Error(formatter({
id: this.getIdentifier(),
propName: propName
}));
}
// else {
// this.log( 'Validating field ' + field + ' with data ' + value );
Expand All @@ -87,15 +90,19 @@ class ValidatedResource extends Resource {
let field = classDeclaration.getProperty(propName);

if (!field) {
throw new Error('The instance with id ' +
this.getIdentifier() + ' trying to set field ' +
propName + ' which is not declared in the model.');
let formatter = Globalize.messageFormatter('validatedresource-fieldnotdefined');
throw new Error(formatter({
id: this.getIdentifier(),
propName: propName
}));
}

if (!field.isArray()) {
throw new Error('The instance with id ' +
this.getIdentifier() + ' trying to add array item ' +
propName + ' which is not declared as an array in the model.');
let formatter = Globalize.messageFormatter('validatedresource-addarrayvalue-notarray');
throw new Error(formatter({
id: this.getIdentifier(),
propName: propName
}));
}

const parameters = {};
Expand Down
Loading