Source: composer-client/lib/registry.js

/*
 * 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;