diff --git a/lib/codegen/fromcto/loopback/loopbackvisitor.js b/lib/codegen/fromcto/loopback/loopbackvisitor.js deleted file mode 100644 index 339da9b4..00000000 --- a/lib/codegen/fromcto/loopback/loopbackvisitor.js +++ /dev/null @@ -1,584 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const debug = require('debug')('concerto-core:loopbackvisitor'); -const util = require('util'); - -/** - * Convert a fully qualified type name, for example org.example.mynetwork.MyAsset, - * into a name that is safe for use as a LoopBack model name. - * @private - * @param {String} fqn The fully qualified type name. - * @returns {String} A name that is safe for use as a LoopBack model name. - */ -function loopbackify(fqn) { - return fqn.replace(/\./g, '_'); -} - -/** - * Convert the contents of a {@link ModelManager} instance to a set of LoopBack - * Definition Language model files - one per concrete asset and transaction type. - * Set a fileWriter property (instance of {@link FileWriter}) on the parameters - * object to control where the generated code is written to disk. - * @private - * @class - * @memberof module:concerto-codegen - */ -class LoopbackVisitor { - - /** - * Constructor. - * @param {boolean} [namespaces] - whether or not namespaces should be used. - */ - constructor(namespaces) { - this.namespaces = !!namespaces; - } - - /** - * Visitor design pattern - * @param {Object} thing - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @public - */ - visit(thing, parameters) { - if (thing.isModelManager?.()) { - return this.visitModelManager(thing, parameters); - } else if (thing.isModelFile?.()) { - return this.visitModelFile(thing, parameters); - } else if (thing.isAsset?.()) { - return this.visitAssetDeclaration(thing, parameters); - } else if (thing.isParticipant?.()) { - return this.visitParticipantDeclaration(thing, parameters); - } else if (thing.isConcept?.()) { - return this.visitConceptDeclaration(thing, parameters); - } else if (thing.isMapDeclaration?.()) { - return; - } else if (thing.isTransaction?.()) { - return this.visitTransactionDeclaration(thing, parameters); - } else if (thing.isEvent?.()) { - return this.visitEventDeclaration(thing, parameters); - } else if (thing.isEnum?.()) { - return this.visitEnumDeclaration(thing, parameters); - } else if (thing.isTypeScalar?.()) { - return this.visitField(thing.getScalarField(), parameters); - } else if (thing.isField?.()) { - return this.visitField(thing, parameters); - } else if (thing.isRelationship?.()) { - return this.visitRelationshipDeclaration(thing, parameters); - } else if (thing.isEnumValue?.()) { - return this.visitEnumValueDeclaration(thing, parameters); - } else { - throw new Error('Unrecognised type: ' + typeof thing + ', value: ' + util.inspect(thing, { showHidden: true, depth: 1 })); - } - } - - /** - * Visitor design pattern - * @param {ModelManager} modelManager - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visitModelManager(modelManager, parameters) { - debug('entering visitModelManager'); - - // Save the model manager so that we have access to it later. - parameters.modelManager = modelManager; - - // Visit all of the files in the model manager. - let jsonSchemas = []; - modelManager.getModelFiles().forEach((modelFile) => { - jsonSchemas = jsonSchemas.concat(modelFile.accept(this, parameters)); - }); - return jsonSchemas; - - } - - /** - * Visitor design pattern - * @param {ModelFile} modelFile - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visitModelFile(modelFile, parameters) { - debug('entering visitModelFile', modelFile.getNamespace()); - - // Save the model file so that we have access to it later. - parameters.modelFile = modelFile; - - // Visit all of the asset and transaction declarations, but ignore the abstract ones. - let jsonSchemas = []; - modelFile.getAssetDeclarations() - .concat(modelFile.getConceptDeclarations()) - .concat(modelFile.getParticipantDeclarations()) - .concat(modelFile.getTransactionDeclarations()) - .forEach((declaration) => { - parameters.first = true; - jsonSchemas.push(declaration.accept(this, parameters)); - }); - return jsonSchemas; - - } - - /** - * Visitor design pattern - * @param {AssetDeclaration} assetDeclaration - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visitAssetDeclaration(assetDeclaration, parameters) { - debug('entering visitAssetDeclaration', assetDeclaration.getName()); - - // If this is the first declaration, then we are building a schema for this asset. - let jsonSchema = {}; - let name = this.namespaces ? assetDeclaration.getFullyQualifiedName() : assetDeclaration.getName(); - if (parameters.first) { - jsonSchema = { - $first: true, - name: loopbackify(name), - description: `An asset named ${assetDeclaration.getName()}`, - plural: name, - base: 'PersistedModel', - idInjection: false, - options: { - validateUpsert: true, - composer: { - type: 'asset' - } - }, - properties: {}, - validations: [], - relations: {}, - acls: [], - methods: [] - }; - parameters.first = false; - } else { - jsonSchema.type = 'Object'; - } - - // Apply all the common schema elements. - return this.visitClassDeclarationCommon(assetDeclaration, parameters, jsonSchema); - - } - - /** - * Visitor design pattern - * @param {ParticipantDeclaration} participantDeclaration - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visitParticipantDeclaration(participantDeclaration, parameters) { - debug('entering visitParticipantDeclaration', participantDeclaration.getName()); - - // If this is the first declaration, then we are building a schema for this participant. - let jsonSchema = {}; - let name = this.namespaces ? participantDeclaration.getFullyQualifiedName() : participantDeclaration.getName(); - if (parameters.first) { - jsonSchema = { - $first: true, - name: loopbackify(name), - description: `A participant named ${participantDeclaration.getName()}`, - plural: name, - base: 'PersistedModel', - idInjection: false, - options: { - validateUpsert: true, - composer: { - type: 'participant' - } - }, - properties: {}, - validations: [], - relations: {}, - acls: [], - methods: [] - }; - parameters.first = false; - } else { - jsonSchema.type = 'Object'; - } - - // Apply all the common schema elements. - return this.visitClassDeclarationCommon(participantDeclaration, parameters, jsonSchema); - - } - - /** - * Visitor design pattern - * @param {ConceptDeclaration} conceptDeclaration - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visitConceptDeclaration(conceptDeclaration, parameters) { - debug('entering visitConceptDeclaration', conceptDeclaration.getName()); - - // If this is the top declaration, then we are building a schema for this concept. - let jsonSchema = {}; - let name = this.namespaces ? conceptDeclaration.getFullyQualifiedName() : conceptDeclaration.getName(); - if (parameters.first) { - jsonSchema = { - $first: true, - name: loopbackify(name), - description: `A concept named ${conceptDeclaration.getName()}`, - plural: name, - // Concepts are not PersistedModel instances as they cannot exist by themselves. - // base: 'PersistedModel', - idInjection: false, - options: { - validateUpsert: true, - composer: { - type: 'concept' - } - }, - properties: {}, - validations: [], - relations: {}, - acls: [], - methods: [] - }; - parameters.first = false; - } else { - jsonSchema.type = 'Object'; - } - - // Apply all the common schema elements. - return this.visitClassDeclarationCommon(conceptDeclaration, parameters, jsonSchema); - - } - - /** - * Visitor design pattern - * @param {TransactionDeclaration} transactionDeclaration - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visitTransactionDeclaration(transactionDeclaration, parameters) { - debug('entering visitTransactionDeclaration', transactionDeclaration.getName()); - - // If this is the top declaration, then we are building a schema for this transaction. - let jsonSchema = {}; - let name = this.namespaces ? transactionDeclaration.getFullyQualifiedName() : transactionDeclaration.getName(); - if (parameters.first) { - jsonSchema = { - $first: true, - name: loopbackify(name), - description: `A transaction named ${transactionDeclaration.getName()}`, - plural: name, - base: 'PersistedModel', - idInjection: false, - options: { - validateUpsert: true, - composer: { - type: 'transaction' - } - }, - properties: {}, - validations: [], - relations: {}, - acls: [], - methods: [] - }; - parameters.first = false; - } else { - jsonSchema.type = 'Object'; - } - - // Apply all the common schema elements. - return this.visitClassDeclarationCommon(transactionDeclaration, parameters, jsonSchema); - - } - - /** - * Visitor design pattern - * @param {EventDeclaration} eventDeclaration - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visitEventDeclaration(eventDeclaration, parameters) { - debug('entering visitEventDeclaration', eventDeclaration.getName()); - return null; - } - - /** - * Visitor design pattern - * @param {ClassDeclaration} classDeclaration - the object being visited - * @param {Object} parameters - the parameter - * @param {Object} jsonSchema - the base JSON Schema object to use - * @return {Object} the result of visiting or null - * @private - */ - visitClassDeclarationCommon(classDeclaration, parameters, jsonSchema) { - debug('entering visitClassDeclarationCommon', classDeclaration.getName()); - - // remember that we have visited this fqn - // in case one of our properties is of the same type (issue #2193) - parameters[classDeclaration.getFullyQualifiedName()] = 'visited'; - - // Add information from the class declaration into the composer section. - if (jsonSchema.options && jsonSchema.options.composer) { - Object.assign(jsonSchema.options.composer, { - namespace: classDeclaration.getNamespace(), - name: classDeclaration.getName(), - fqn: classDeclaration.getFullyQualifiedName(), - abstract: classDeclaration.isAbstract() - }); - } - - // Set the required properties into the schema. - Object.assign(jsonSchema, { - properties: {} - }); - - // If no description exists, add it now. - if (!jsonSchema.description) { - jsonSchema.description = `An instance of ${classDeclaration.getName()}`; - } - - // Every class declaration has a $class property. - jsonSchema.properties.$class = { - type: 'string', - default: classDeclaration.getFullyQualifiedName(), - required: false, - description: 'The class identifier for this type' - }; - - // Walk over all of the properties of this class and its super classes. - classDeclaration.getProperties().forEach((property) => { - - // Get the schema for the property - jsonSchema.properties[property.getName()] = property.accept(this, parameters); - }); - - // For transaction declarations, we need to change the model slightly. - if (classDeclaration.getIdentifierFieldName()) { - // The ID field will be supplied at submission time, not by the client. - jsonSchema.forceId = true; - const identifierFieldName = classDeclaration.getIdentifierFieldName(); - jsonSchema.properties[identifierFieldName].generated = true; - jsonSchema.properties[identifierFieldName].required = false; - } - - // If this is a top level schema, now we need to write it to disk. - if (jsonSchema.$first) { - delete jsonSchema.$first; - let fileContents = JSON.stringify(jsonSchema, null, 4); - if (parameters.fileWriter) { - let name = this.namespaces ? classDeclaration.getFullyQualifiedName() : classDeclaration.getName(); - let fileName = `${name}.json`; - parameters.fileWriter.openFile(fileName); - parameters.fileWriter.write(fileContents); - parameters.fileWriter.closeFile(); - } - } - - // Return the created schema. - return jsonSchema; - - } - - /** - * Given a primitive Concerto type returns the corresponding loopback type - * @param {string} type - the concerto primitive type name - * @return {string} the loopback type - * @private - */ - static toLoopbackType(type) { - - let result = 'string'; - - switch (type) { - case 'String': - result = 'string'; - break; - case 'Double': - case 'Integer': - case 'Long': - result= 'number'; - break; - case 'DateTime': - result = 'date'; - break; - case 'Boolean': - result = 'boolean'; - break; - } - - return result; - } - - /** - * Visitor design pattern - * @param {Field} field - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visitField(field, parameters) { - debug('entering visitField', field.getName()); - - // Is this a primitive typed property? - let jsonSchema; - if (field.isPrimitive()) { - - // Render the type as JSON Schema. - jsonSchema = {}; - jsonSchema.type = LoopbackVisitor.toLoopbackType(field.getType()); - - // If this field has a default value, add it. - if (field.getDefaultValue()) { - jsonSchema.default = field.getDefaultValue(); - } - - // If this is the identifying field, mark it as such. - if (field.getName() === field.getParent().getIdentifierFieldName()) { - jsonSchema.id = true; - jsonSchema.description = 'The instance identifier for this type'; - } - - // Is this an enumeration? - } else if (field.isTypeEnum()) { - - // Look up the type of the property. - let type = field.getParent().getModelFile().getType(field.getType()); - - // Visit it, and grab the schema. - jsonSchema = type.accept(this, parameters); - - // If this field has a default value, add it. - if (field.getDefaultValue()) { - jsonSchema.default = field.getDefaultValue(); - } - - // Not primitive, so must be a class! - } else { - - // Render the type as JSON Schema. - let typeName = this.namespaces ? field.getFullyQualifiedTypeName() : field.getType(); - jsonSchema = { - type: loopbackify(typeName) - }; - - // Look up the type of the property. - let type = field.getParent().getModelFile().getType(field.getType()); - - // Visit it, but ignore the response. - // We do not visit types that have already been visited to prevent recursion (issue #2193) - if(!parameters[field.getFullyQualifiedTypeName()]) { - type.accept(this, parameters); - } - } - - // Is the type an array? - if (field.isArray()) { - - // Set the type to an array of the already set type from above. - jsonSchema.type = [ jsonSchema.type ]; - - // Array properties have to be optional and have a default value as LoopBack does not cope with - // the difference between a required array property and an empty array property (issue #3438). - jsonSchema.default = []; - jsonSchema.required = false; - - } else { - - // Is the field required? - jsonSchema.required = !field.isOptional(); - - } - - // Return the schema. - return jsonSchema; - - } - - /** - * Visitor design pattern - * @param {EnumDeclaration} enumDeclaration - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visitEnumDeclaration(enumDeclaration, parameters) { - debug('entering visitEnumDeclaration', enumDeclaration.getName()); - - // Create the schema. - let jsonSchema = { - type: 'string' - }; - - // Walk over all of the properties which should just be enum value declarations. - jsonSchema.enum = enumDeclaration.getProperties().map((property) => { - return property.accept(this, parameters); - }); - - // Return the schema. - return jsonSchema; - - } - - /** - * Visitor design pattern - * @param {EnumValueDeclaration} enumValueDeclaration - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visitEnumValueDeclaration(enumValueDeclaration, parameters) { - debug('entering visitEnumValueDeclaration', enumValueDeclaration.getName()); - - // The schema in this case is just the name of the value. - return enumValueDeclaration.getName(); - - } - - /** - * Visitor design pattern - * @param {RelationshipDeclaration} relationshipDeclaration - the object being visited - * @param {Object} parameters - the parameter - * @return {Object} the result of visiting or null - * @private - */ - visitRelationshipDeclaration(relationshipDeclaration, parameters) { - debug('entering visitRelationship', relationshipDeclaration.getName()); - - // Create the schema. - let jsonSchema = { - type: 'any', - description: `The identifier of an instance of ${relationshipDeclaration.getName()}`, - required: !relationshipDeclaration.isOptional() - }; - - // Is the type an array? - if (relationshipDeclaration.isArray()) { - jsonSchema.type = [ jsonSchema.type ]; - } - - // Return the schema. - return jsonSchema; - - } - -} - -module.exports = LoopbackVisitor; diff --git a/test/codegen/fromcto/loopback/loopbackvisitor.js b/test/codegen/fromcto/loopback/loopbackvisitor.js deleted file mode 100644 index 1905efdf..00000000 --- a/test/codegen/fromcto/loopback/loopbackvisitor.js +++ /dev/null @@ -1,1367 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const chai = require('chai'); -const should = chai.should(); -const sinon = require('sinon'); - -let LoopbackVisitor= require('../../../../lib/codegen/fromcto/loopback/loopbackvisitor.js'); - -const AssetDeclaration = require('@accordproject/concerto-core').AssetDeclaration; -const ParticipantDeclaration = require('@accordproject/concerto-core').ParticipantDeclaration; -const EventDeclaration = require('@accordproject/concerto-core').EventDeclaration; -const ClassDeclaration = require('@accordproject/concerto-core').ClassDeclaration; -const EnumDeclaration = require('@accordproject/concerto-core').EnumDeclaration; -const ConceptDeclaration = require('@accordproject/concerto-core').ConceptDeclaration; -const EnumValueDeclaration = require('@accordproject/concerto-core').EnumValueDeclaration; -const Field = require('@accordproject/concerto-core').Field; -const ModelFile = require('@accordproject/concerto-core').ModelFile; -const ModelManager = require('@accordproject/concerto-core').ModelManager; -const RelationshipDeclaration = require('@accordproject/concerto-core').RelationshipDeclaration; -const TransactionDeclaration = require('@accordproject/concerto-core').TransactionDeclaration; -const FileWriter = require('@accordproject/concerto-util').FileWriter; - -describe('LoopbackVisitor', () => { - let loopbackVisit; - let mockFileWriter; - beforeEach(() => { - loopbackVisit = new LoopbackVisitor(); - mockFileWriter = sinon.createStubInstance(FileWriter); - }); - - describe('visit', () => { - let param; - beforeEach(() => { - param = { - property1: 'value1' - }; - }); - - it('should call visitModelManager for a ModelManager', () => { - let thing = sinon.createStubInstance(ModelManager); - thing.isModelManager.returns(true); - let mockSpecialVisit = sinon.stub(loopbackVisit, 'visitModelManager'); - mockSpecialVisit.returns('Duck'); - - loopbackVisit.visit(thing, param).should.deep.equal('Duck'); - - mockSpecialVisit.calledWith(thing, param).should.be.ok; - }); - - it('should call visitModelFile for a ModelFile', () => { - let thing = sinon.createStubInstance(ModelFile); - thing.isModelFile.returns(true); - let mockSpecialVisit = sinon.stub(loopbackVisit, 'visitModelFile'); - mockSpecialVisit.returns('Duck'); - - loopbackVisit.visit(thing, param).should.deep.equal('Duck'); - - mockSpecialVisit.calledWith(thing, param).should.be.ok; - }); - - it('should call visitAssetDeclaration for a AssetDeclaration', () => { - let thing = sinon.createStubInstance(AssetDeclaration); - thing.isAsset.returns(true); - let mockSpecialVisit = sinon.stub(loopbackVisit, 'visitAssetDeclaration'); - mockSpecialVisit.returns('Duck'); - - loopbackVisit.visit(thing, param).should.deep.equal('Duck'); - - mockSpecialVisit.calledWith(thing, param).should.be.ok; - }); - - it('should call visitParticipantDeclaration for a ParticipantDeclaration', () => { - let thing = sinon.createStubInstance(ParticipantDeclaration); - thing.isParticipant.returns(true); - let mockSpecialVisit = sinon.stub(loopbackVisit, 'visitParticipantDeclaration'); - mockSpecialVisit.returns('Duck'); - - loopbackVisit.visit(thing, param).should.deep.equal('Duck'); - - mockSpecialVisit.calledWith(thing, param).should.be.ok; - }); - - it('should call visitConceptDeclaration for a ConceptDeclaration', () => { - let thing = sinon.createStubInstance(ConceptDeclaration); - thing.isConcept.returns(true); - let mockSpecialVisit = sinon.stub(loopbackVisit, 'visitConceptDeclaration'); - mockSpecialVisit.returns('Duck'); - - loopbackVisit.visit(thing, param).should.deep.equal('Duck'); - - mockSpecialVisit.calledWith(thing, param).should.be.ok; - }); - - it('should call visitTransactionDeclaration for a TransactionDeclaration', () => { - let thing = sinon.createStubInstance(TransactionDeclaration); - thing.isTransaction.returns(true); - let mockSpecialVisit = sinon.stub(loopbackVisit, 'visitTransactionDeclaration'); - mockSpecialVisit.returns('Duck'); - - loopbackVisit.visit(thing, param).should.deep.equal('Duck'); - - mockSpecialVisit.calledWith(thing, param).should.be.ok; - }); - - it('should call visitEventDeclaration for a EventDeclaration', () => { - let thing = sinon.createStubInstance(EventDeclaration); - thing.isEvent.returns(true); - let mockSpecialVisit = sinon.stub(loopbackVisit, 'visitEventDeclaration'); - mockSpecialVisit.returns('Duck'); - - loopbackVisit.visit(thing, param).should.deep.equal('Duck'); - - mockSpecialVisit.calledWith(thing, param).should.be.ok; - }); - - it('should call visitEnumDeclaration for a EnumDeclaration', () => { - let thing = sinon.createStubInstance(EnumDeclaration); - thing.isEnum.returns(true); - let mockSpecialVisit = sinon.stub(loopbackVisit, 'visitEnumDeclaration'); - mockSpecialVisit.returns('Duck'); - - loopbackVisit.visit(thing, param).should.deep.equal('Duck'); - - mockSpecialVisit.calledWith(thing, param).should.be.ok; - }); - - it('should call visitField for a Field', () => { - let thing = sinon.createStubInstance(Field); - thing.isField.returns(true); - let mockSpecialVisit = sinon.stub(loopbackVisit, 'visitField'); - mockSpecialVisit.returns('Duck'); - - loopbackVisit.visit(thing, param).should.deep.equal('Duck'); - - mockSpecialVisit.calledWith(thing, param).should.be.ok; - }); - - it('should call visitRelationshipDeclaration for a RelationshipDeclaration', () => { - let thing = sinon.createStubInstance(RelationshipDeclaration); - thing.isRelationship.returns(true); - let mockSpecialVisit = sinon.stub(loopbackVisit, 'visitRelationshipDeclaration'); - mockSpecialVisit.returns('Duck'); - - loopbackVisit.visit(thing, param).should.deep.equal('Duck'); - - mockSpecialVisit.calledWith(thing, param).should.be.ok; - }); - - it('should call visitEnumValueDeclaration for a EnumValueDeclaration', () => { - let thing = sinon.createStubInstance(EnumValueDeclaration); - thing.isEnumValue.returns(true); - let mockSpecialVisit = sinon.stub(loopbackVisit, 'visitEnumValueDeclaration'); - mockSpecialVisit.returns('Duck'); - - loopbackVisit.visit(thing, param).should.deep.equal('Duck'); - - mockSpecialVisit.calledWith(thing, param).should.be.ok; - }); - - it('should throw an error when an unrecognised type is supplied', () => { - let thing = 'Something of unrecognised type'; - - (() => { - loopbackVisit.visit(thing, param); - }).should.throw('Unrecognised type: string, value: \'Something of unrecognised type\''); - }); - }); - - describe('visitModelManager', () => { - it('should return a value of the concatenated output of each modelFiles accept', () => { - let param = {}; - - let mockModelFile = sinon.createStubInstance(ModelFile); - mockModelFile.isModelFile.returns(true); - mockModelFile.accept.returns(['Duck', 'Duck']); - let mockModelFile2 = sinon.createStubInstance(ModelFile); - mockModelFile2.isModelFile.returns(true); - mockModelFile2.accept.returns(['Duck', 'Goose']); - - let mockModelManager = sinon.createStubInstance(ModelManager); - mockModelManager.isModelManager.returns(true); - mockModelManager.getModelFiles.returns([mockModelFile, mockModelFile2]); - - loopbackVisit.visitModelManager(mockModelManager, param).should.deep.equal(['Duck', 'Duck', 'Duck', 'Goose']); - mockModelFile.accept.withArgs(loopbackVisit, param).calledOnce.should.be.ok; - mockModelFile2.accept.withArgs(loopbackVisit, param).calledOnce.should.be.ok; - param.should.deep.equal({ - modelManager: mockModelManager - }); - }); - }); - - describe('visitModelFile', () => { - it('should return an array of each declaration\'s accept', () => { - let param = {}; - - let mockAssetDeclaration = sinon.createStubInstance(AssetDeclaration); - mockAssetDeclaration.isAsset.returns(true); - mockAssetDeclaration.accept.returns('Duck'); - - let mockConceptDeclaration = sinon.createStubInstance(ConceptDeclaration); - mockConceptDeclaration.isConcept.returns(true); - mockConceptDeclaration.accept.returns('Duck'); - - let mockParticipantDeclaration = sinon.createStubInstance(ParticipantDeclaration); - mockParticipantDeclaration.isParticipant.returns(true); - mockParticipantDeclaration.accept.returns('Duck'); - - let mockTransactionDeclaration = sinon.createStubInstance(TransactionDeclaration); - mockTransactionDeclaration.isTransaction.returns(true); - mockTransactionDeclaration.accept.returns('Goose'); - - let mockModelFile = sinon.createStubInstance(ModelFile); - mockModelFile.isModelFile.returns(true); - mockModelFile.getNamespace.returns; - mockModelFile.getAssetDeclarations.returns([mockAssetDeclaration]); - mockModelFile.getTransactionDeclarations.returns([mockTransactionDeclaration]); - mockModelFile.getConceptDeclarations.returns([mockConceptDeclaration]); - mockModelFile.getParticipantDeclarations.returns([mockParticipantDeclaration]); - - loopbackVisit.visitModelFile(mockModelFile, param).should.deep.equal(['Duck', 'Duck', 'Duck', 'Goose']); - - param.should.deep.equal({ - first: true, - modelFile: mockModelFile - }); - }); - }); - - describe('visitAssetDeclaration', () => { - it('should return the value of visitClassDeclarationCommon using a schema with just type', () => { - let param = {}; - - let mockAssetDeclaration = sinon.createStubInstance(AssetDeclaration); - mockAssetDeclaration.isAsset.returns(true); - mockAssetDeclaration.getFullyQualifiedName.returns('org.acme.Person.Bob'); - mockAssetDeclaration.getName.returns('Bob'); - - let mockVisitClassDeclarationCommon = sinon.stub(loopbackVisit, 'visitClassDeclarationCommon'); - mockVisitClassDeclarationCommon.withArgs(mockAssetDeclaration, param, { - type: 'Object' - }).returns('Class Declaration'); - - loopbackVisit.visitAssetDeclaration(mockAssetDeclaration, param).should.deep.equal('Class Declaration'); - }); - - it('should return the value of visitClassDeclarationCommon buiulding the schema', () => { - let param = { - first: true - }; - - let mockAssetDeclaration = sinon.createStubInstance(AssetDeclaration); - mockAssetDeclaration.isAsset.returns(true); - mockAssetDeclaration.getFullyQualifiedName.returns('org.acme.Person.Bob'); - mockAssetDeclaration.getName.returns('Bob'); - - let expectedSchema = { - $first: true, - name: 'Bob', - description: 'An asset named Bob', - plural: 'Bob', - base: 'PersistedModel', - idInjection: false, - options: { - validateUpsert: true, - composer: { - type: 'asset' - } - }, - properties: {}, - validations: [], - relations: {}, - acls: [], - methods: [] - }; - - let mockVisitClassDeclarationCommon = sinon.stub(loopbackVisit, 'visitClassDeclarationCommon'); - mockVisitClassDeclarationCommon.withArgs(mockAssetDeclaration, param, expectedSchema).returns('Class Declaration'); - - loopbackVisit.visitAssetDeclaration(mockAssetDeclaration, param).should.deep.equal('Class Declaration'); - param.should.have.property('first', false); - }); - - it('should return the value of visitClassDeclarationCommon buiulding the schema using the fullyQualifiedName', () => { - let param = { - first: true - }; - - let mockAssetDeclaration = sinon.createStubInstance(AssetDeclaration); - mockAssetDeclaration.isAsset.returns(true); - mockAssetDeclaration.getFullyQualifiedName.returns('org.acme.Person.Bob'); - mockAssetDeclaration.getName.returns('Bob'); - - let expectedSchema = { - $first: true, - name: 'org_acme_Person_Bob', - description: 'An asset named Bob', - plural: 'org.acme.Person.Bob', - base: 'PersistedModel', - idInjection: false, - options: { - validateUpsert: true, - composer: { - type: 'asset' - } - }, - properties: {}, - validations: [], - relations: {}, - acls: [], - methods: [] - }; - - let mockVisitClassDeclarationCommon = sinon.stub(loopbackVisit, 'visitClassDeclarationCommon'); - mockVisitClassDeclarationCommon.withArgs(mockAssetDeclaration, param, expectedSchema).returns('Class Declaration'); - - loopbackVisit.namespaces = true; - - loopbackVisit.visitAssetDeclaration(mockAssetDeclaration, param).should.deep.equal('Class Declaration'); - param.should.have.property('first', false); - }); - }); - - describe('visitParticipantDeclaration', () => { - it('should return the value of visitClassDeclarationCommon using a schema with just type', () => { - let param = {}; - - let mockParticipantDeclaration = sinon.createStubInstance(ParticipantDeclaration); - mockParticipantDeclaration.isParticipant.returns(true); - mockParticipantDeclaration.getFullyQualifiedName.returns('org.acme.Person.Bob'); - mockParticipantDeclaration.getName.returns('Bob'); - - let mockVisitClassDeclarationCommon = sinon.stub(loopbackVisit, 'visitClassDeclarationCommon'); - mockVisitClassDeclarationCommon.withArgs(mockParticipantDeclaration, param, { - type: 'Object' - }).returns('Class Declaration'); - - loopbackVisit.visitParticipantDeclaration(mockParticipantDeclaration, param).should.deep.equal('Class Declaration'); - }); - - it('should return the value of visitClassDeclarationCommon buiulding the schema', () => { - let param = { - first: true - }; - - let mockParticipantDeclaration = sinon.createStubInstance(ParticipantDeclaration); - mockParticipantDeclaration.isParticipant.returns(true); - mockParticipantDeclaration.getFullyQualifiedName.returns('org.acme.Person.Bob'); - mockParticipantDeclaration.getName.returns('Bob'); - - let expectedSchema = { - $first: true, - name: 'Bob', - description: 'A participant named Bob', - plural: 'Bob', - base: 'PersistedModel', - idInjection: false, - options: { - validateUpsert: true, - composer: { - type: 'participant' - } - }, - properties: {}, - validations: [], - relations: {}, - acls: [], - methods: [] - }; - - let mockVisitClassDeclarationCommon = sinon.stub(loopbackVisit, 'visitClassDeclarationCommon'); - mockVisitClassDeclarationCommon.withArgs(mockParticipantDeclaration, param, expectedSchema).returns('Class Declaration'); - - loopbackVisit.visitParticipantDeclaration(mockParticipantDeclaration, param).should.deep.equal('Class Declaration'); - param.should.have.property('first', false); - }); - - it('should return the value of visitClassDeclarationCommon buiulding the schema using the fullyQualifiedName', () => { - let param = { - first: true - }; - - let mockParticipantDeclaration = sinon.createStubInstance(ParticipantDeclaration); - mockParticipantDeclaration.isParticipant.returns(true); - mockParticipantDeclaration.getFullyQualifiedName.returns('org.acme.Person.Bob'); - mockParticipantDeclaration.getName.returns('Bob'); - - let expectedSchema = { - $first: true, - name: 'org_acme_Person_Bob', - description: 'A participant named Bob', - plural: 'org.acme.Person.Bob', - base: 'PersistedModel', - idInjection: false, - options: { - validateUpsert: true, - composer: { - type: 'participant' - } - }, - properties: {}, - validations: [], - relations: {}, - acls: [], - methods: [] - }; - - let mockVisitClassDeclarationCommon = sinon.stub(loopbackVisit, 'visitClassDeclarationCommon'); - mockVisitClassDeclarationCommon.withArgs(mockParticipantDeclaration, param, expectedSchema).returns('Class Declaration'); - - loopbackVisit.namespaces = true; - - loopbackVisit.visitParticipantDeclaration(mockParticipantDeclaration, param).should.deep.equal('Class Declaration'); - param.should.have.property('first', false); - }); - }); - - describe('visitConceptDeclaration', () => { - it('should return the value of visitClassDeclarationCommon using a schema with just type', () => { - let param = {}; - - let mockConceptDeclaration = sinon.createStubInstance(ConceptDeclaration); - mockConceptDeclaration.isConcept.returns(true); - mockConceptDeclaration.getFullyQualifiedName.returns('org.acme.Person.Bob'); - mockConceptDeclaration.getName.returns('Bob'); - - let mockVisitClassDeclarationCommon = sinon.stub(loopbackVisit, 'visitClassDeclarationCommon'); - mockVisitClassDeclarationCommon.withArgs(mockConceptDeclaration, param, { - type: 'Object' - }).returns('Class Declaration'); - - loopbackVisit.visitConceptDeclaration(mockConceptDeclaration, param).should.deep.equal('Class Declaration'); - }); - - it('should return the value of visitClassDeclarationCommon buiulding the schema', () => { - let param = { - first: true - }; - - let mockConceptDeclaration = sinon.createStubInstance(ConceptDeclaration); - mockConceptDeclaration.isConcept.returns(true); - mockConceptDeclaration.getFullyQualifiedName.returns('org.acme.Person.Bob'); - mockConceptDeclaration.getName.returns('Bob'); - - let expectedSchema = { - $first: true, - name: 'Bob', - description: 'A concept named Bob', - plural: 'Bob', - idInjection: false, - options: { - validateUpsert: true, - composer: { - type: 'concept' - } - }, - properties: {}, - validations: [], - relations: {}, - acls: [], - methods: [] - }; - - let mockVisitClassDeclarationCommon = sinon.stub(loopbackVisit, 'visitClassDeclarationCommon'); - mockVisitClassDeclarationCommon.withArgs(mockConceptDeclaration, param, expectedSchema).returns('Class Declaration'); - - loopbackVisit.visitConceptDeclaration(mockConceptDeclaration, param).should.deep.equal('Class Declaration'); - param.should.have.property('first', false); - }); - - it('should return the value of visitClassDeclarationCommon buiulding the schema using the fullyQualifiedName', () => { - let param = { - first: true - }; - - let mockConceptDeclaration = sinon.createStubInstance(ConceptDeclaration); - mockConceptDeclaration.isConcept.returns(true); - mockConceptDeclaration.getFullyQualifiedName.returns('org.acme.Person.Bob'); - mockConceptDeclaration.getName.returns('Bob'); - - let expectedSchema = { - $first: true, - name: 'org_acme_Person_Bob', - description: 'A concept named Bob', - plural: 'org.acme.Person.Bob', - idInjection: false, - options: { - validateUpsert: true, - composer: { - type: 'concept' - } - }, - properties: {}, - validations: [], - relations: {}, - acls: [], - methods: [] - }; - - let mockVisitClassDeclarationCommon = sinon.stub(loopbackVisit, 'visitClassDeclarationCommon'); - mockVisitClassDeclarationCommon.withArgs(mockConceptDeclaration, param, expectedSchema).returns('Class Declaration'); - - loopbackVisit.namespaces = true; - - loopbackVisit.visitConceptDeclaration(mockConceptDeclaration, param).should.deep.equal('Class Declaration'); - param.should.have.property('first', false); - }); - }); - - describe('visitTransactionDeclaration', () => { - it('should return the value of visitClassDeclarationCommon using a schema with just type', () => { - let param = {}; - - let mockTransactionDeclaration = sinon.createStubInstance(TransactionDeclaration); - mockTransactionDeclaration.isTransaction.returns(true); - mockTransactionDeclaration.getFullyQualifiedName.returns('org.acme.Person.Bob'); - mockTransactionDeclaration.getName.returns('Bob'); - - let mockVisitClassDeclarationCommon = sinon.stub(loopbackVisit, 'visitClassDeclarationCommon'); - mockVisitClassDeclarationCommon.withArgs(mockTransactionDeclaration, param, { - type: 'Object' - }).returns('Class Declaration'); - - loopbackVisit.visitTransactionDeclaration(mockTransactionDeclaration, param).should.deep.equal('Class Declaration'); - }); - - it('should return the value of visitClassDeclarationCommon buiulding the schema', () => { - let param = { - first: true - }; - - let mockTransactionDeclaration = sinon.createStubInstance(TransactionDeclaration); - mockTransactionDeclaration.isTransaction.returns(true); - mockTransactionDeclaration.getFullyQualifiedName.returns('org.acme.Person.Bob'); - mockTransactionDeclaration.getName.returns('Bob'); - - let expectedSchema = { - $first: true, - name: 'Bob', - description: 'A transaction named Bob', - plural: 'Bob', - base: 'PersistedModel', - idInjection: false, - options: { - validateUpsert: true, - composer: { - type: 'transaction' - } - }, - properties: {}, - validations: [], - relations: {}, - acls: [], - methods: [] - }; - - let mockVisitClassDeclarationCommon = sinon.stub(loopbackVisit, 'visitClassDeclarationCommon'); - mockVisitClassDeclarationCommon.withArgs(mockTransactionDeclaration, param, expectedSchema).returns('Class Declaration'); - - loopbackVisit.visitTransactionDeclaration(mockTransactionDeclaration, param).should.deep.equal('Class Declaration'); - param.should.have.property('first', false); - }); - - it('should return the value of visitClassDeclarationCommon buiulding the schema using the fullyQualifiedName', () => { - let param = { - first: true - }; - - let mockTransactionDeclaration = sinon.createStubInstance(TransactionDeclaration); - mockTransactionDeclaration.isTransaction.returns(true); - mockTransactionDeclaration.getFullyQualifiedName.returns('org.acme.Person.Bob'); - mockTransactionDeclaration.getName.returns('Bob'); - - let expectedSchema = { - $first: true, - name: 'org_acme_Person_Bob', - description: 'A transaction named Bob', - plural: 'org.acme.Person.Bob', - base: 'PersistedModel', - idInjection: false, - options: { - validateUpsert: true, - composer: { - type: 'transaction' - } - }, - properties: {}, - validations: [], - relations: {}, - acls: [], - methods: [] - }; - - let mockVisitClassDeclarationCommon = sinon.stub(loopbackVisit, 'visitClassDeclarationCommon'); - mockVisitClassDeclarationCommon.withArgs(mockTransactionDeclaration, param, expectedSchema).returns('Class Declaration'); - - loopbackVisit.namespaces = true; - - loopbackVisit.visitTransactionDeclaration(mockTransactionDeclaration, param).should.deep.equal('Class Declaration'); - param.should.have.property('first', false); - }); - }); - - describe('visitEventDeclaration', () => { - it('should return null', () => { - let mockEventDeclaration = sinon.createStubInstance(EventDeclaration); - mockEventDeclaration.isEvent.returns(true); - mockEventDeclaration.getName.returns('Bob'); - - should.equal(loopbackVisit.visitEventDeclaration(mockEventDeclaration, {}), null); - }); - }); - - describe('visitClassDeclarationCommon', () => { - it('should return a created JSONSchema', () => { - let param = {}; - let jsonSchema = { - title: 'A schema', - description: ' A particularly good schema' - }; - - let mockClassDeclaration = sinon.createStubInstance(ClassDeclaration); - mockClassDeclaration.isClassDeclaration.returns(true); - mockClassDeclaration.getName.returns('Person'); - mockClassDeclaration.getFullyQualifiedName.returns('org.acme.Person'); - mockClassDeclaration.getProperties.returns([ - { - getName: () => { - return 'Bob'; - }, - isOptional: () => { - return true; - }, - accept: () => { - return 'Guineapig'; - } - }, - { - getName: () => { - return 'Trevor'; - }, - isOptional: () => { - return false; - }, - accept: () => { - return 'Goldfish'; - } - } - ]); - - loopbackVisit.visitClassDeclarationCommon(mockClassDeclaration, param, jsonSchema).should.deep.equal({ - title: 'A schema', - description: ' A particularly good schema', - properties: { - $class: { - type: 'string', - default: 'org.acme.Person', - description: 'The class identifier for this type', - required: false - }, - Bob: 'Guineapig', - Trevor: 'Goldfish' - } - }); - }); - - it('should return a created JSONSchema autogenerating the description', () => { - let param = {}; - let jsonSchema = { - title: 'A schema' - }; - - let mockClassDeclaration = sinon.createStubInstance(ClassDeclaration); - mockClassDeclaration.isClassDeclaration.returns(true); - mockClassDeclaration.getName.returns('Person'); - mockClassDeclaration.getFullyQualifiedName.returns('org.acme.Person'); - mockClassDeclaration.getProperties.returns([ - { - getName: () => { - return 'Bob'; - }, - isOptional: () => { - return true; - }, - accept: () => { - return 'Guineapig'; - } - }, - { - getName: () => { - return 'Trevor'; - }, - isOptional: () => { - return false; - }, - accept: () => { - return 'Goldfish'; - } - } - ]); - - loopbackVisit.visitClassDeclarationCommon(mockClassDeclaration, param, jsonSchema).should.deep.equal({ - title: 'A schema', - description: 'An instance of Person', - properties: { - $class: { - type: 'string', - default: 'org.acme.Person', - description: 'The class identifier for this type', - required: false - }, - Bob: 'Guineapig', - Trevor: 'Goldfish' - } - }); - }); - - it('should return a created JSONSchema adding to the composer options', () => { - let param = {}; - let jsonSchema = { - title: 'A schema', - description: 'A particularly good schema', - options: { - composer: {} - } - }; - - let mockClassDeclaration = sinon.createStubInstance(ClassDeclaration); - mockClassDeclaration.isClassDeclaration.returns(true); - mockClassDeclaration.getName.returns('Person'); - mockClassDeclaration.getFullyQualifiedName.returns('org.acme.Person'); - mockClassDeclaration.getNamespace.returns('org.acme'); - mockClassDeclaration.isAbstract.returns(true); - mockClassDeclaration.getProperties.returns([ - { - getName: () => { - return 'Bob'; - }, - isOptional: () => { - return true; - }, - accept: () => { - return 'Guineapig'; - } - }, - { - getName: () => { - return 'Trevor'; - }, - isOptional: () => { - return false; - }, - accept: () => { - return 'Goldfish'; - } - } - ]); - - loopbackVisit.visitClassDeclarationCommon(mockClassDeclaration, param, jsonSchema).should.deep.equal({ - title: 'A schema', - description: 'A particularly good schema', - options: { - composer: { - namespace: 'org.acme', - name: 'Person', - fqn: 'org.acme.Person', - abstract: true - } - }, - properties: { - $class: { - type: 'string', - default: 'org.acme.Person', - description: 'The class identifier for this type', - required: false - }, - Bob: 'Guineapig', - Trevor: 'Goldfish' - } - }); - }); - - it('should return a created top level JSON Schema and write it to a file with the name of the FQN', () => { - let param = { - fileWriter: mockFileWriter - }; - let jsonSchema = { - $first: true, - $schema: true, - title: 'A schema', - description: ' A particularly good schema' - }; - - let mockClassDeclaration = sinon.createStubInstance(ClassDeclaration); - mockClassDeclaration.isClassDeclaration.returns(true); - mockClassDeclaration.getName.returns('Person'); - mockClassDeclaration.getFullyQualifiedName.returns('org.acme.Person'); - mockClassDeclaration.getProperties.returns([ - { - getName: () => { - return 'Bob'; - }, - isOptional: () => { - return true; - }, - accept: () => { - return 'Guineapig'; - } - }, - { - getName: () => { - return 'Trevor'; - }, - isOptional: () => { - return false; - }, - accept: () => { - return 'Goldfish'; - } - } - ]); - let expectedResult = { - $schema: true, - title: 'A schema', - description: ' A particularly good schema', - properties: { - $class: { - type: 'string', - default: 'org.acme.Person', - required: false, - description: 'The class identifier for this type' - }, - Bob: 'Guineapig', - Trevor: 'Goldfish' - } - }; - - loopbackVisit.namespaces = true; - - loopbackVisit.visitClassDeclarationCommon(mockClassDeclaration, param, jsonSchema).should.deep.equal(expectedResult); - - param.fileWriter.openFile.withArgs('org.acme.Person.json').calledOnce.should.be.ok; - param.fileWriter.write.withArgs(JSON.stringify(expectedResult, null, 4)).calledOnce.should.be.ok; - param.fileWriter.closeFile.calledOnce.should.be.ok; - }); - - it('should return a created top level JSON Schema and write it to a file with the name of the FQN', () => { - let param = { - fileWriter: null - }; - let jsonSchema = { - $first: true, - $schema: true, - title: 'A schema', - description: ' A particularly good schema' - }; - - let mockClassDeclaration = sinon.createStubInstance(ClassDeclaration); - mockClassDeclaration.isClassDeclaration.returns(true); - mockClassDeclaration.getName.returns('Person'); - mockClassDeclaration.getFullyQualifiedName.returns('org.acme.Person'); - mockClassDeclaration.getProperties.returns([ - { - getName: () => { - return 'Bob'; - }, - isOptional: () => { - return true; - }, - accept: () => { - return 'Guineapig'; - } - }, - { - getName: () => { - return 'Trevor'; - }, - isOptional: () => { - return false; - }, - accept: () => { - return 'Goldfish'; - } - } - ]); - let expectedResult = { - $schema: true, - title: 'A schema', - description: ' A particularly good schema', - properties: { - $class: { - type: 'string', - default: 'org.acme.Person', - required: false, - description: 'The class identifier for this type' - }, - Bob: 'Guineapig', - Trevor: 'Goldfish' - } - }; - - loopbackVisit.namespaces = true; - loopbackVisit.visitClassDeclarationCommon(mockClassDeclaration, param, jsonSchema).should.deep.equal(expectedResult); - }); - - it('should return a created top level JSONSchema and write it to a file with the name of the class name', () => { - let param = { - fileWriter: mockFileWriter - }; - let jsonSchema = { - $first: true, - $schema: true, - title: 'A schema', - description: ' A particularly good schema' - }; - - let mockClassDeclaration = sinon.createStubInstance(ClassDeclaration); - mockClassDeclaration.isClassDeclaration.returns(true); - mockClassDeclaration.getName.returns('Person'); - mockClassDeclaration.getFullyQualifiedName.returns('org.acme.Person'); - mockClassDeclaration.getProperties.returns([ - { - getName: () => { - return 'Bob'; - }, - isOptional: () => { - return true; - }, - accept: () => { - return 'Guineapig'; - } - }, - { - getName: () => { - return 'Trevor'; - }, - isOptional: () => { - return false; - }, - accept: () => { - return 'Goldfish'; - } - } - ]); - let expectedResult = { - $schema: true, - title: 'A schema', - description: ' A particularly good schema', - properties: { - $class: { - type: 'string', - default: 'org.acme.Person', - required: false, - description: 'The class identifier for this type' - }, - Bob: 'Guineapig', - Trevor: 'Goldfish' - } - }; - loopbackVisit.visitClassDeclarationCommon(mockClassDeclaration, param, jsonSchema).should.deep.equal(expectedResult); - - param.fileWriter.openFile.withArgs('Person.json').calledOnce.should.be.ok; - param.fileWriter.write.withArgs(JSON.stringify(expectedResult, null, 4)).calledOnce.should.be.ok; - param.fileWriter.closeFile.calledOnce.should.be.ok; - }); - - it('should return a created top level JSONSchema with extra data if a TransactionDeclaration', () => { - let param = {}; - let jsonSchema = { - $schema: true, - title: 'A schema', - description: ' A particularly good schema', - properties: { - noseLength: {} - } - }; - - let mockTransactionDeclaration = sinon.createStubInstance(TransactionDeclaration); - mockTransactionDeclaration.isTransaction.returns(true); - mockTransactionDeclaration.getName.returns('Person'); - mockTransactionDeclaration.getFullyQualifiedName.returns('org.acme.Person'); - mockTransactionDeclaration.getIdentifierFieldName.returns('Bob'); - mockTransactionDeclaration.getProperties.returns([ - { - getName: () => { - return 'Bob'; - }, - isOptional: () => { - return true; - }, - accept: () => { - return { - prop: 'Guineapig' - }; - } - }, - { - getName: () => { - return 'Trevor'; - }, - isOptional: () => { - return false; - }, - accept: () => { - return { - prop: 'Goldfish' - }; - } - }, - ]); - let expectedResult = { - $schema: true, - title: 'A schema', - description: ' A particularly good schema', - properties: { - $class: { - type: 'string', - default: 'org.acme.Person', - required: false, - description: 'The class identifier for this type' - }, - Bob: { - prop: 'Guineapig', - generated: true, - required: false - }, - Trevor: { - prop: 'Goldfish' - }, - }, - forceId: true, - - }; - loopbackVisit.visitClassDeclarationCommon(mockTransactionDeclaration, param, jsonSchema).should.deep.equal(expectedResult); - }); - }); - - describe('toLoopbackType', () => { - it('should return date for DateTime', () => { - LoopbackVisitor.toLoopbackType('DateTime').should.deep.equal('date'); - }); - - it('should return boolean for Boolean', () => { - LoopbackVisitor.toLoopbackType('Boolean').should.deep.equal('boolean'); - }); - - it('should return String for String', () => { - LoopbackVisitor.toLoopbackType('String').should.deep.equal('string'); - }); - - it('should return number for Double', () => { - LoopbackVisitor.toLoopbackType('Double').should.deep.equal('number'); - }); - - it('should return number for Long', () => { - LoopbackVisitor.toLoopbackType('Long').should.deep.equal('number'); - }); - - it('should return number for Integer', () => { - LoopbackVisitor.toLoopbackType('Integer').should.deep.equal('number'); - }); - - it('should return the string as default', () => { - LoopbackVisitor.toLoopbackType('Penguin').should.deep.equal('string'); - }); - }); - - describe('visitField', () => { - it('should return a JSON schema for a primitive', () => { - let param = {}; - - let mockField = sinon.createStubInstance(Field); - mockField.isField.returns(true); - mockField.getName.returns('Horse'); - mockField.isPrimitive.returns(true); - mockField.getType.returns('String'); - mockField.getParent.returns({ - getIdentifierFieldName: () => { - return 'Farmer'; - } - }); - mockField.isOptional.returns(false); - - loopbackVisit.visitField(mockField, param).should.deep.equal({ - type: 'string', - required: true - }); - }); - - it('should return a JSON schema for a primitive with a default value and when it is an identifying field', () => { - let param = {}; - - let mockField = sinon.createStubInstance(Field); - mockField.isField.returns(true); - mockField.getName.returns('Farmer'); - mockField.isPrimitive.returns(true); - mockField.getType.returns('String'); - mockField.getParent.returns({ - getIdentifierFieldName: () => { - return 'Farmer'; - } - }); - mockField.getDefaultValue.returns('Ploughed'); - mockField.isOptional.returns(false); - - loopbackVisit.visitField(mockField, param).should.deep.equal({ - type: 'string', - default: 'Ploughed', - id: true, - description: 'The instance identifier for this type', - required: true - }); - }); - - it('should return a JSON schema for an enumeration', () => { - let param = {}; - - let mockModelFile = sinon.createStubInstance(ModelFile); - mockModelFile.accept.withArgs(loopbackVisit, param).returns({ - type: 'Square' - }); - mockModelFile.isModelFile.returns(true); - mockModelFile.getType.withArgs('Acreage').returns({ - accept: mockModelFile.accept - }); - - let mockField = sinon.createStubInstance(Field); - mockField.isField.returns(true); - mockField.getName.returns('Horse'); - mockField.isTypeEnum.returns(true); - mockField.getType.returns('Acreage'); - mockField.getParent.returns({ - getModelFile: () => { - return mockModelFile; - } - }); - mockField.isOptional.returns(false); - - loopbackVisit.visitField(mockField, param).should.deep.equal({ - type: 'Square', - required: true - }); - }); - - it('should return a JSON schema for an enumeration with a default value', () => { - let param = {}; - - let mockModelFile = sinon.createStubInstance(ModelFile); - mockModelFile.accept.withArgs(loopbackVisit, param).returns({ - type: 'Square' - }); - mockModelFile.isModelFile.returns(true); - mockModelFile.getType.withArgs('Acreage').returns({ - accept: mockModelFile.accept - }); - - let mockField = sinon.createStubInstance(Field); - mockField.isField.returns(true); - mockField.getName.returns('Horse'); - mockField.isTypeEnum.returns(true); - mockField.getType.returns('Acreage'); - mockField.getParent.returns({ - getModelFile: () => { - return mockModelFile; - } - }); - mockField.isOptional.returns(false); - mockField.getDefaultValue.returns('Trampled'); - - loopbackVisit.visitField(mockField, param).should.deep.equal({ - type: 'Square', - default: 'Trampled', - required: true - }); - }); - - it('should return a JSON schema for a class using type', () => { - let param = {}; - - let mockModelFile = sinon.createStubInstance(ModelFile); - mockModelFile.isModelFile.returns(true); - mockModelFile.getType.withArgs('Acreage').returns({ - accept: mockModelFile.accept - }); - - let mockField = sinon.createStubInstance(Field); - mockField.isField.returns(true); - mockField.getName.returns('Horse'); - mockField.getType.returns('Acreage'); - mockField.getFullyQualifiedTypeName.returns('org.acme.Horse.Acreage'); - mockField.getParent.returns({ - getModelFile: () => { - return mockModelFile; - } - }); - mockField.isOptional.returns(false); - - loopbackVisit.visitField(mockField, param).should.deep.equal({ - type: 'Acreage', - required: true - }); - - mockModelFile.accept.withArgs(loopbackVisit, param).calledOnce.should.be.ok; - }); - - it('should return a JSON schema for a class using FQN', () => { - let param = {}; - - let mockModelFile = sinon.createStubInstance(ModelFile); - mockModelFile.isModelFile.returns(true); - mockModelFile.getType.withArgs('Acreage').returns({ - accept: mockModelFile.accept - }); - - let mockField = sinon.createStubInstance(Field); - mockField.isField.returns(true); - mockField.getName.returns('Horse'); - mockField.getType.returns('Acreage'); - mockField.getFullyQualifiedTypeName.returns('org.acme.Horse.Acreage'); - mockField.getParent.returns({ - getModelFile: () => { - return mockModelFile; - } - }); - mockField.isOptional.returns(false); - - loopbackVisit.namespaces = true; - - loopbackVisit.visitField(mockField, param).should.deep.equal({ - type: 'org_acme_Horse_Acreage', - required: true - }); - - mockModelFile.accept.withArgs(loopbackVisit, param).calledOnce.should.be.ok; - }); - - it('should return a JSON schema for a class without calling accept', () => { - let param = { - 'org.acme.Horse.Acreage': 'Something' - }; - - let mockModelFile = sinon.createStubInstance(ModelFile); - mockModelFile.isModelFile.returns(true); - mockModelFile.getType.withArgs('Acreage').returns({ - accept: mockModelFile.accept - }); - - let mockField = sinon.createStubInstance(Field); - mockField.isField.returns(true); - mockField.getName.returns('Horse'); - mockField.getType.returns('Acreage'); - mockField.getFullyQualifiedTypeName.returns('org.acme.Horse.Acreage'); - mockField.getParent.returns({ - getModelFile: () => { - return mockModelFile; - } - }); - mockField.isOptional.returns(false); - - loopbackVisit.visitField(mockField, param).should.deep.equal({ - type: 'Acreage', - required: true - }); - - mockModelFile.accept.callCount.should.deep.equal(0); - }); - - it('should return a JSON schema for a primitive that is an array', () => { - let param = {}; - - let mockField = sinon.createStubInstance(Field); - mockField.isField.returns(true); - mockField.getName.returns('Horse'); - mockField.isPrimitive.returns(true); - mockField.getType.returns('String'); - mockField.getParent.returns({ - getIdentifierFieldName: () => { - return 'Farmer'; - } - }); - mockField.isOptional.returns(false); - mockField.isArray.returns(true); - - loopbackVisit.visitField(mockField, param).should.deep.equal({ - type: ['string'], - required: false, - default: [] - }); - }); - }); - - describe('visitEnumDeclaration', () => { - it('should create a JSON schema for an enum declaration', () => { - let param = {}; - - let mockEnumDecl = sinon.createStubInstance(EnumDeclaration); - mockEnumDecl.accept.withArgs(loopbackVisit, param).returns('Duck'); - mockEnumDecl.isEnum.returns(true); - mockEnumDecl.getProperties.returns([ - { - accept: mockEnumDecl.accept - }, - { - accept: mockEnumDecl.accept - } - ]); - - loopbackVisit.visitEnumDeclaration(mockEnumDecl, param).should.deep.equal({ - type: 'string', - enum: ['Duck', 'Duck'] - }); - }); - }); - - describe('visitEnumValueDeclaration', () => { - it('should return the enumValueDeclaration\'s name', () => { - let mockEnumValDecl = sinon.createStubInstance(EnumValueDeclaration); - mockEnumValDecl.isEnumValue.returns(true); - mockEnumValDecl.getName.returns('Bob'); - - loopbackVisit.visitEnumValueDeclaration(mockEnumValDecl, {}).should.deep.equal('Bob'); - }); - }); - - describe('visitRelationshipDeclaration', () => { - it('should return a JSONSchema for a relationship', () => { - let mockRelationshipDeclaration = sinon.createStubInstance(RelationshipDeclaration); - mockRelationshipDeclaration.isRelationship.returns(true); - mockRelationshipDeclaration.getName.returns('Bob'); - mockRelationshipDeclaration.isOptional.returns(false); - - loopbackVisit.visitRelationshipDeclaration(mockRelationshipDeclaration, {}).should.deep.equal({ - type: 'any', - description: 'The identifier of an instance of Bob', - required: true - }); - }); - - it('should return a JSONSchema for a relationship that is an array', () => { - let mockRelationshipDeclaration = sinon.createStubInstance(RelationshipDeclaration); - mockRelationshipDeclaration.isRelationship.returns(true); - mockRelationshipDeclaration.getName.returns('Bob'); - mockRelationshipDeclaration.isOptional.returns(false); - mockRelationshipDeclaration.isArray.returns(true); - - loopbackVisit.visitRelationshipDeclaration(mockRelationshipDeclaration, {}).should.deep.equal({ - type: ['any'], - description: 'The identifier of an instance of Bob', - required: true - }); - }); - }); -}); \ No newline at end of file diff --git a/test/codegen/fromcto/loopback/loopbackvisitorcircular.js b/test/codegen/fromcto/loopback/loopbackvisitorcircular.js deleted file mode 100644 index ec31d3d0..00000000 --- a/test/codegen/fromcto/loopback/loopbackvisitorcircular.js +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -'use strict'; - -const fs = require('fs'); -const FileWriter = require('@accordproject/concerto-util').FileWriter; -const ModelManager = require('@accordproject/concerto-core').ModelManager; -const LoopbackVisitor = require('../../../../lib/codegen/fromcto/loopback/loopbackvisitor'); -const path = require('path'); - -require('chai').should(); -const sinon = require('sinon'); - -describe('LoopbackVisitor with Circular Model', () => { - - let mockFileWriter; - let modelManager; - let visitor; - - let sandbox; - - [undefined, true, false].forEach((namespaces) => { - - describe(`namespaces = ${namespaces}`, () => { - - beforeEach(() => { - mockFileWriter = sinon.createStubInstance(FileWriter); - modelManager = new ModelManager(); - modelManager.addCTOModel(fs.readFileSync(path.resolve(__dirname, '../data/model/circular.cto'), 'utf8'), 'circular.cto'); - visitor = new LoopbackVisitor(namespaces); - sandbox = sinon.createSandbox(); - }); - - afterEach(() => { - sandbox.restore(); - }); - - describe('#visit', () => { - - it('should generate Loopback model files for each type when given a model manager', () => { - - // Visit all of the loaded model files and check that they were all generated - const schemas = modelManager.accept(visitor, { fileWriter: mockFileWriter }); - schemas.length.should.equal(29); - }); - - }); - - }); - }); - -});