/* * 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 Resource = require('composer-common').Resource; const Util = require('composer-common').Util; /** * Class representing an Abstract Registry. * <p><a href="./diagrams/registry.svg"><img src="./diagrams/registry.svg" style="width:100%;"/></a></p> * @abstract * @class * @memberof module:composer-client */ class Registry { /** * Get a list of all existing registries. * * @protected * @param {SecurityContext} securityContext The user's security context. * @param {string} registryType The type of this registry. * @return {Promise} A promise that will be resolved with an array of JSON * objects representing the registries. */ static getAllRegistries(securityContext, registryType) { Util.securityCheck(securityContext); if (!registryType) { throw new Error('registryType not specified'); } return Util.queryChainCode(securityContext, 'getAllRegistries', [registryType]) .then((buffer) => { return JSON.parse(buffer.toString()); }); } /** * Get an existing registry. * * @protected * @param {SecurityContext} securityContext The user's security context. * @param {string} registryType The type of this registry. * @param {string} id The unique identifier of the registry. * @return {Promise} A promise that will be resolved with a JSON object * representing the registry. */ static getRegistry(securityContext, registryType, id) { Util.securityCheck(securityContext); if (!registryType) { throw new Error('registryType not specified'); } else if (!id) { throw new Error('id not specified'); } return Util.queryChainCode(securityContext, 'getRegistry', [registryType, id]) .then((buffer) => { return JSON.parse(buffer.toString()); }); } /** * Determines whether a registry exists. * * @protected * @param {SecurityContext} securityContext The user's security context. * @param {string} registryType The type of this registry. * @param {string} id The unique identifier of the registry. * @return {Promise} A promise that will be resolved with true/false depending on whether the registry exists */ static existsRegistry(securityContext, registryType, id) { Util.securityCheck(securityContext); if (!registryType) { throw new Error('registryType not specified'); } else if (!id) { throw new Error('id not specified'); } return Util.queryChainCode(securityContext, 'existsRegistry', [registryType, id]) .then((buffer) => { return JSON.parse(buffer.toString()); }); } /** * Add a new asset registry. * * @protected * @param {SecurityContext} securityContext The user's security context. * @param {string} registryType The type of this registry. * @param {string} id The unique identifier of the registry. * @param {string} name The name of the registry. * @return {Promise} A promise that will be resolved with a JSON object * representing the registry. */ static addRegistry(securityContext, registryType, id, name) { Util.securityCheck(securityContext); if (!registryType) { throw new Error('registryType not specified'); } else if (!id) { throw new Error('id not specified'); } else if (!name) { throw new Error('name not specified'); } return Util.invokeChainCode(securityContext, 'addRegistry', [registryType, id, name]) .then(() => { return { id: id, name: name }; }); } /** * Create a registry. * * <strong>Note: Only to be called by framework code. Applications should * retrieve instances from {@link BusinessNetworkConnection}</strong> * </p> * * @protected * @param {string} registryType The type of this registry. * @param {string} id The unique identifier of the registry. * @param {string} name The display name for the registry. * @param {SecurityContext} securityContext The users security context. * @param {ModelManager} modelManager The ModelManager to use for this registry. * @param {Factory} factory The factory to use for this registry. * @param {Serializer} serializer The Serializer to use for this registry. */ constructor(registryType, id, name, securityContext, modelManager, factory, serializer) { if (!registryType) { throw new Error('registryType not specified'); } else if (!id) { throw new Error('id not specified'); } else if (!name) { throw new Error('name not specified'); } else if (!securityContext) { throw new Error('securityContext not specified'); } else if (!modelManager) { throw new Error('modelManager not specified'); } else if (!factory) { throw new Error('factory not specified'); } else if (!serializer) { throw new Error('serializer not specified'); } this.registryType = registryType; this.id = id; this.name = name; this.securityContext = securityContext; this.modelManager = modelManager; this.factory = factory; this.serializer = serializer; } /** * Adds a list of new resources to the registry. * * @param {Resource[]} resources The resources to be added to the registry. * @return {Promise} A promise that will be resolved when the resource is * added to the registry. */ addAll(resources) { Util.securityCheck(this.securityContext); if (!resources) { throw new Error('resources not specified'); } let serializedResources = resources.map((resource) => { return this.serializer.toJSON(resource); }); return Util.invokeChainCode(this.securityContext, 'addAllResourcesToRegistry', [this.registryType, this.id, JSON.stringify(serializedResources)]); } /** * Adds a new resource to the registry. * * @param {Resource} resource The resource to be added to the registry. * @return {Promise} A promise that will be resolved when the resource is * added to the registry. */ add(resource) { Util.securityCheck(this.securityContext); if (!resource) { throw new Error('resource not specified'); } let serializedResource = this.serializer.toJSON(resource); return Util.invokeChainCode(this.securityContext, 'addResourceToRegistry', [this.registryType, this.id, JSON.stringify(serializedResource)]); } /** * Updates a list of resources in the registry. * * @param {Resource[]} resources The resources to be updated in the asset registry. * @return {Promise} A promise that will be resolved when the resource is * added to the registry. */ updateAll(resources) { Util.securityCheck(this.securityContext); if (!resources) { throw new Error('resources not specified'); } let serializedResources = resources.map((resource) => { return this.serializer.toJSON(resource); }); return Util.invokeChainCode(this.securityContext, 'updateAllResourcesInRegistry', [this.registryType, this.id, JSON.stringify(serializedResources)]); } /** * Updates a resource in the registry. * * @param {Resource} resource The resource to be updated in the registry. * @return {Promise} A promise that will be resolved when the resource is * updated in the registry. */ update(resource) { Util.securityCheck(this.securityContext); if (!resource) { throw new Error('resource not specified'); } let serializedResource = this.serializer.toJSON(resource); return Util.invokeChainCode(this.securityContext, 'updateResourceInRegistry', [this.registryType, this.id, JSON.stringify(serializedResource)]); } /** * Removes a list of resources from the registry. * * @param {(Resource[]|string[])} resources The resources, or the unique identifiers of the resources. * @return {Promise} A promise that will be resolved when the resource is * added to the registry. */ removeAll(resources) { Util.securityCheck(this.securityContext); if (!resources) { throw new Error('resources not specified'); } let data = resources.map((resource) => { if (resource instanceof Resource) { return resource.getIdentifier(); } else { return resource; } }); return Util.invokeChainCode(this.securityContext, 'removeAllResourcesFromRegistry', [this.registryType, this.id, JSON.stringify(data)]); } /** * Remove an asset with a given type and id from the registry. * * @param {(Resource|string)} resource The resource, or the unique identifier of the resource. * @return {Promise} A promise that will be resolved when the resource is * removed from the registry. */ remove(resource) { Util.securityCheck(this.securityContext); if (!resource) { throw new Error('resource not specified'); } let id; if (resource instanceof Resource) { id = resource.getIdentifier(); } else { id = resource; } return Util.invokeChainCode(this.securityContext, 'removeResourceFromRegistry', [this.registryType, this.id, id]); } /** * Get all of the resources in the registry. * * @return {Promise} A promise that will be resolved with an array of JSON * objects representing the resources. */ getAll() { Util.securityCheck(this.securityContext); return Util.queryChainCode(this.securityContext, 'getAllResourcesInRegistry', [this.registryType, this.id]) .then((buffer) => { return JSON.parse(buffer.toString()); }) .then((resources) => { return resources.map((resource) => { return this.serializer.fromJSON(resource); }); }); } /** * Get a specific resource in the registry. * * @param {string} id The unique identifier of the resource. * @return {Promise} A promise that will be resolved with a JSON object * representing the resource. */ get(id) { Util.securityCheck(this.securityContext); if (!id) { throw new Error('id not specified'); } return Util.queryChainCode(this.securityContext, 'getResourceInRegistry', [this.registryType, this.id, id]) .then((buffer) => { return JSON.parse(buffer.toString()); }) .then((resource) => { return this.serializer.fromJSON(resource); }); } /** * Determines whether a specific resource exists in the registry. * * @param {string} id The unique identifier of the resource. * @return {Promise} A promise that will be resolved with true/false depending on whether the resource exists. */ exists(id) { Util.securityCheck(this.securityContext); if (!id) { throw new Error('id not specified'); } return Util.queryChainCode(this.securityContext, 'existsResourceInRegistry', [this.registryType, this.id, id]) .then((buffer) => { return JSON.parse(buffer.toString()); }); } /** * Find resources in the registry that match the specified JSONata expression. * The JSONata expression is applied to each resource in the registry, and * resources are returned if the JSONata expression returns a truthy value for that * resource. * * @param {string} expression The JSONata expression. * @return {Promise} A promise that will be resolved with an array of {@link * Resource} instances representing the assets that match the query. */ find(expression) { Util.securityCheck(this.securityContext); if (!expression) { throw new Error('expression not specified'); } return Util.queryChainCode(this.securityContext, 'findResourcesInRegistry', [this.registryType, this.id, expression]) .then((buffer) => { return JSON.parse(buffer.toString()); }) .then((resources) => { return resources.map((resource) => { return this.serializer.fromJSON(resource); }); }); } /** * Execute a query against all resources in the registry. The JSONata * expression is applied to each resource in the registry, and the result * of the JSONata expression is returned if the result is truthy. The result * is a JavaScript object, and should only be used for visualization * purposes. You cannot use the {@link add} or {@link update} functions with * data returned by this function. * * @param {string} expression The JSONata expression. * @return {Promise} A promise that will be resolved with an array of JavaScript * objects representing the resources and all of their resolved relationships. */ query(expression) { Util.securityCheck(this.securityContext); if (!expression) { throw new Error('expression not specified'); } return Util.queryChainCode(this.securityContext, 'queryResourcesInRegistry', [this.registryType, this.id, expression]) .then((buffer) => { return JSON.parse(buffer.toString()); }); } /** * Get all of the resources in the registry, and resolve all of their relationships * to other assets, participants, and transactions. The result is a JavaScript * object, and should only be used for visualization purposes. You cannot use * the {@link add} or {@link update} functions with a resolved resource. * * @return {Promise} A promise that will be resolved with an array of JavaScript * objects representing the resources and all of their resolved relationships. */ resolveAll() { Util.securityCheck(this.securityContext); return Util.queryChainCode(this.securityContext, 'resolveAllResourcesInRegistry', [this.registryType, this.id]) .then((buffer) => { return JSON.parse(buffer.toString()); }); } /** * Get a specific resource in the registry, and resolve all of its relationships * to other assets, participants, and transactions. The result is a JavaScript * object, and should only be used for visualization purposes. You cannot use * the {@link add} or {@link update} functions with a resolved resource. * * @param {string} id The unique identifier of the asset. * @return {Promise} A promise that will be resolved with a JavaScript object * representing the resource and all of its resolved relationships. */ resolve(id) { Util.securityCheck(this.securityContext); if (!id) { throw new Error('id not specified'); } return Util.queryChainCode(this.securityContext, 'resolveResourceInRegistry', [this.registryType, this.id, id]) .then((buffer) => { return JSON.parse(buffer.toString()); }); } } module.exports = Registry;