/* * 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 Logger = require('composer-common').Logger; const util = require('util'); const LOG = Logger.getLog('EngineResources'); /** * The JavaScript engine responsible for processing chaincode commands. * @protected * @memberof module:composer-runtime */ class EngineResources { /** * Get all resources in the specified registry. * @param {Context} context The request context. * @param {string[]} args The arguments to pass to the chaincode function. * @return {Promise} A promise that will be resolved when complete, or rejected * with an error. */ getAllResourcesInRegistry(context, args) { const method = 'getAllResourcesInRegistry'; LOG.entry(method, context, args); if (args.length !== 2) { LOG.error(method, 'Invalid arguments', args); throw new Error(util.format('Invalid arguments "%j" to function "%s", expecting "%j"', args, 'getAllResourcesInRegistry', ['registryType', 'registryId'])); } let registryType = args[0]; let registryId = args[1]; return context.getRegistryManager().get(registryType, registryId) .then((registryManager) => { return registryManager.getAll(); }) .then((resources) => { return resources.map((resource) => { return context.getSerializer().toJSON(resource); }); }) .then((result) => { LOG.exit(method, result); return result; }); } /** * Get the specified resource in the specified registry. * @param {Context} context The request context. * @param {string[]} args The arguments to pass to the chaincode function. * @return {Promise} A promise that will be resolved when complete, or rejected * with an error. */ getResourceInRegistry(context, args) { const method = 'getResourceInRegistry'; LOG.entry(method, context, args); if (args.length !== 3) { LOG.error(method, 'Invalid arguments', args); throw new Error(util.format('Invalid arguments "%j" to function "%s", expecting "%j"', args, 'getResourceInRegistry', ['registryType', 'registryId', 'resourceId'])); } let registryType = args[0]; let registryId = args[1]; let resourceId = args[2]; return context.getRegistryManager().get(registryType, registryId) .then((registry) => { return registry.get(resourceId); }) .then((resource) => { return context.getSerializer().toJSON(resource); }) .then((result) => { LOG.exit(method, result); return result; }); } /** * Determine whether the specified resource exists in the specified registry. * @param {Context} context The request context. * @param {string[]} args The arguments to pass to the chaincode function. * @return {Promise} A promise that will be resolved with a boolean indicating whether * the resource exists in the registry. */ existsResourceInRegistry(context, args) { const method = 'existsResourceInRegistry'; LOG.entry(method, context, args); if (args.length !== 3) { LOG.error(method, 'Invalid arguments', args); throw new Error(util.format('Invalid arguments "%j" to function "%s", expecting "%j"', args, 'existsResourceInRegistry', ['registryType', 'registryId', 'resourceId'])); } let registryType = args[0]; let registryId = args[1]; let resourceId = args[2]; return context.getRegistryManager().get(registryType, registryId) .then((registry) => { return registry.exists(resourceId); }); } /** * Add an array of resources to the specified registry. * @param {Context} context The request context. * @param {string[]} args The arguments to pass to the chaincode function. * @return {Promise} A promise that will be resolved when complete, or rejected * with an error. */ addAllResourcesToRegistry(context, args) { const method = 'addAllResourcesToRegistry'; LOG.entry(method, context, args); if (args.length !== 3) { LOG.error(method, 'Invalid arguments', args); throw new Error(util.format('Invalid arguments "%j" to function "%s", expecting "%j"', args, 'addAllResourcesToRegistry', ['registryType', 'registryId', 'serializedResources'])); } let registryType = args[0]; let registryId = args[1]; let serializedResources = JSON.parse(args[2]); let resources = serializedResources.map((serializedResource) => { return context.getSerializer().fromJSON(serializedResource); }); return context.getRegistryManager().get(registryType, registryId) .then((registry) => { return registry.addAll(resources); }) .then(() => { LOG.exit(method); }); } /** * Add a resource to the specified registry. * @param {Context} context The request context. * @param {string[]} args The arguments to pass to the chaincode function. * @return {Promise} A promise that will be resolved when complete, or rejected * with an error. */ addResourceToRegistry(context, args) { const method = 'addResourceToRegistry'; LOG.entry(method, context, args); if (args.length !== 3) { LOG.error(method, 'Invalid arguments', args); throw new Error(util.format('Invalid arguments "%j" to function "%s", expecting "%j"', args, 'addResourceToRegistry', ['registryType', 'registryId', 'serializedResource'])); } let registryType = args[0]; let registryId = args[1]; let serializedResource = JSON.parse(args[2]); let resource = context.getSerializer().fromJSON(serializedResource); return context.getRegistryManager().get(registryType, registryId) .then((registry) => { return registry.add(resource); }) .then(() => { LOG.exit(method); }); } /** * Update an array of resources in the specified registry. * @param {Context} context The request context. * @param {string[]} args The arguments to pass to the chaincode function. * @return {Promise} A promise that will be resolved when complete, or rejected * with an error. */ updateAllResourcesInRegistry(context, args) { const method = 'updateAllResourcesInRegistry'; LOG.entry(method, context, args); if (args.length !== 3) { LOG.error(method, 'Invalid arguments', args); throw new Error(util.format('Invalid arguments "%j" to function "%s", expecting "%j"', args, 'updateAllResourcesInRegistry', ['registryType', 'registryId', 'serializedResources'])); } let registryType = args[0]; let registryId = args[1]; let serializedResources = JSON.parse(args[2]); let resources = serializedResources.map((serializedResource) => { return context.getSerializer().fromJSON(serializedResource); }); return context.getRegistryManager().get(registryType, registryId) .then((registry) => { return registry.updateAll(resources); }) .then(() => { LOG.exit(method); }); } /** * Update a resource in the specified registry. * @param {Context} context The request context. * @param {string[]} args The arguments to pass to the chaincode function. * @return {Promise} A promise that will be resolved when complete, or rejected * with an error. */ updateResourceInRegistry(context, args) { const method = 'updateResourceInRegistry'; LOG.entry(method, context, args); if (args.length !== 3) { LOG.error(method, 'Invalid arguments', args); throw new Error(util.format('Invalid arguments "%j" to function "%s", expecting "%j"', args, 'updateResourceInRegistry', ['registryType', 'registryId', 'serializedResource'])); } let registryType = args[0]; let registryId = args[1]; let serializedResource = JSON.parse(args[2]); let resource = context.getSerializer().fromJSON(serializedResource); return context.getRegistryManager().get(registryType, registryId) .then((registry) => { return registry.update(resource); }) .then(() => { LOG.exit(method); }); } /** * Remove an array of resources from the specified registry. * @param {Context} context The request context. * @param {string[]} args The arguments to pass to the chaincode function. * @return {Promise} A promise that will be resolved when complete, or rejected * with an error. */ removeAllResourcesFromRegistry(context, args) { const method = 'removeAllResourcesFromRegistry'; LOG.entry(method, context, args); if (args.length !== 3) { LOG.error(method, 'Invalid arguments', args); throw new Error(util.format('Invalid arguments "%j" to function "%s", expecting "%j"', args, 'removeAllResourcesFromRegistry', ['registryType', 'registryId', 'resourceIds'])); } let registryType = args[0]; let registryId = args[1]; let resourceIds = JSON.parse(args[2]); return context.getRegistryManager().get(registryType, registryId) .then((registry) => { return registry.removeAll(resourceIds); }) .then(() => { LOG.exit(method); }); } /** * Remove a resource from the specified registry. * @param {Context} context The request context. * @param {string[]} args The arguments to pass to the chaincode function. * @return {Promise} A promise that will be resolved when complete, or rejected * with an error. */ removeResourceFromRegistry(context, args) { const method = 'removeResourceFromRegistry'; LOG.entry(method, context, args); if (args.length !== 3) { LOG.error(method, 'Invalid arguments', args); throw new Error(util.format('Invalid arguments "%j" to function "%s", expecting "%j"', args, 'removeResourceFromRegistry', ['registryType', 'registryId', 'resourceId'])); } let registryType = args[0]; let registryId = args[1]; let resourceId = args[2]; return context.getRegistryManager().get(registryType, registryId) .then((registry) => { return registry.remove(resourceId); }) .then(() => { LOG.exit(method); }); } /** * Get all resources in the specified registry, and recursively resolve all * their relationships to other resources. * @param {Context} context The request context. * @param {string[]} args The arguments to pass to the chaincode function. * @return {Promise} A promise that will be resolved when complete, or rejected * with an error. */ resolveAllResourcesInRegistry(context, args) { const method = 'resolveAllResourcesInRegistry'; LOG.entry(method, context, args); if (args.length !== 2) { LOG.error(method, 'Invalid arguments', args); throw new Error(util.format('Invalid arguments "%j" to function "%s", expecting "%j"', args, 'resolveAllResourcesInRegistry', ['registryType', 'registryId'])); } let registryType = args[0]; let registryId = args[1]; return context.getRegistryManager().get(registryType, registryId) .then((registryManager) => { return registryManager.getAll(); }) .then((resources) => { let resolver = context.getResolver(); return resources.reduce((result, resource) => { return result.then(() => { LOG.debug(method, 'Resolving resource', resource.getFullyQualifiedIdentifier()); return resolver.resolve(resource); }); }, Promise.resolve()) .then(() => { LOG.debug(method, 'Resolved all resources', resources.length); return resources; }); }) .then((resources) => { return resources.map((resource) => { return context.getSerializer().toJSON(resource, { permitResourcesForRelationships: true }); }); }) .then((result) => { LOG.exit(method, result); return result; }); } /** * Get the specified resource in the specified registry, and recursively resolve * all its relationships to other resources. * @param {Context} context The request context. * @param {string[]} args The arguments to pass to the chaincode function. * @return {Promise} A promise that will be resolved when complete, or rejected * with an error. */ resolveResourceInRegistry(context, args) { const method = 'resolveResourceInRegistry'; LOG.entry(method, context, args); if (args.length !== 3) { LOG.error(method, 'Invalid arguments', args); throw new Error(util.format('Invalid arguments "%j" to function "%s", expecting "%j"', args, 'resolveResourceInRegistry', ['registryType', 'registryId', 'resourceId'])); } let registryType = args[0]; let registryId = args[1]; let resourceId = args[2]; return context.getRegistryManager().get(registryType, registryId) .then((registry) => { return registry.get(resourceId); }) .then((resource) => { let resolver = context.getResolver(); LOG.debug(method, 'Resolving resource', resource.getFullyQualifiedIdentifier()); return resolver.resolve(resource); }) .then((resource) => { return context.getSerializer().toJSON(resource, { permitResourcesForRelationships: true }); }) .then((result) => { LOG.exit(method, result); return result; }); } /** * Query all of the the resources in the specified registry using the given * expression, and return all of the resources for which the expression returns * a truthy value. * @param {Context} context The request context. * @param {string[]} args The arguments to pass to the chaincode function. * @return {Promise} A promise that will be resolved when complete, or rejected * with an error. */ findResourcesInRegistry(context, args) { const method = 'findResourcesInRegistry'; LOG.entry(method, context, args); if (args.length !== 3) { LOG.error(method, 'Invalid arguments', args); throw new Error(util.format('Invalid arguments "%j" to function "%s", expecting "%j"', args, 'findResourcesInRegistry', ['registryType', 'registryId', 'expression'])); } let registryType = args[0]; let registryId = args[1]; let expression = args[2]; return context.getRegistryManager().get(registryType, registryId) .then((registryManager) => { return registryManager.getAll(); }) .then((resources) => { let queryExecutor = context.getQueryExecutor(); return queryExecutor.queryAll(expression, resources) .then((queryResults) => { let result = []; queryResults.forEach((queryResult, index) => { let resource = resources[index]; LOG.debug(method, 'Checking query results for resource', resource.getFullyQualifiedIdentifier()); if (queryResult) { LOG.debug(method, 'Query returned truthy value for resource'); result.push(context.getSerializer().toJSON(resource, { convertResourcesToRelationships: true })); } else { LOG.debug(method, 'Query returned falsey value for resource'); } }); LOG.exit(method, result); return result; }); }); } /** * Query all of the the resources in the specified registry using the given * expression, and return all of the truthy results. * @param {Context} context The request context. * @param {string[]} args The arguments to pass to the chaincode function. * @return {Promise} A promise that will be resolved when complete, or rejected * with an error. */ queryResourcesInRegistry(context, args) { const method = 'queryResourcesInRegistry'; LOG.entry(method, context, args); if (args.length !== 3) { LOG.error(method, 'Invalid arguments', args); throw new Error(util.format('Invalid arguments "%j" to function "%s", expecting "%j"', args, 'queryResourcesInRegistry', ['registryType', 'registryId', 'expression'])); } let registryType = args[0]; let registryId = args[1]; let expression = args[2]; return context.getRegistryManager().get(registryType, registryId) .then((registryManager) => { return registryManager.getAll(); }) .then((resources) => { let queryExecutor = context.getQueryExecutor(); return queryExecutor.queryAll(expression, resources) .then((queryResults) => { let result = queryResults.filter((queryResult, index) => { let resource = resources[index]; LOG.debug(method, 'Checking query results for resource', resource.getFullyQualifiedIdentifier()); if (queryResult) { LOG.debug(method, 'Query returned truthy value for resource'); return true; } else { LOG.debug(method, 'Query returned falsey value for resource'); return false; } }); LOG.exit(method, result); return result; }); }); } } module.exports = EngineResources;