import BusinessEndpoint from './business_endpoint.js'; import ResourcefulEndpoint from './resourceful_endpoint.js'; /** * Provides details of a Sequoia Service * * This class should not be used directly, but should instead be obtained * from {@link Client#service} * * @param {Transport} transport - Transport instance to use for fetching * @param {Object} data - JSON data returned from the service's raw description * e.g. https://metadata-sandbox.sequoia.piksel.com/descriptor/raw?owner=demo * * @property {Object} data - JSON data returned from the service's raw description * e.g. https://metadata-sandbox.sequoia.piksel.com/descriptor/raw?owner=demo * * @private */ class ServiceDescriptor { constructor(transport, data) { this.transport = transport; this.data = data; } /** * Get a {@link ResourcefulEndpoint} from a ServiceDescriptor * * @param {string} resourceName - e.g. 'contents' (the contents resourceful endpoint * from the meatatdata service) * * @returns {ResourcefulEndpoint} */ resourcefulEndpoint(resourceName) { if (!this.data || !(resourceName in this.data.resourcefuls)) { return null; } const resourceful = this.data.resourcefuls[resourceName]; resourceful.location = `${this.data.location}${resourceful.path}`; resourceful.tenant = this.data.tenant; return new ResourcefulEndpoint(this.transport, resourceful); } /** * Get an array of {@link ResourcefulEndpoint}s from a ServiceDescriptor * * @param {...string} resourceName - e.g. 'assets', 'contents' (the assets and contents resourceful endpoint * from the metadata service) * * @example * const [ assets, contents ] = service.resourcefulEndpoints('assets', 'contents'); * * @returns {Array<ResourcefulEndpoint>} */ resourcefulEndpoints(...resourceName) { const endpoints = resourceName.length ? resourceName : Object.keys(this.data.resourcefuls); return endpoints.map(r => this.resourcefulEndpoint(r)); } /** * Get a {@link BusinessEndpoint} from a ServiceDescriptor * * @param {string} name - e.g. 'feeds' (the feeds business endpoint * from the gateway service) * * @returns {BusinessEndpoint} */ businessEndpoint(endpointName, pathOptions) { const data = Object.assign({}, this.data); const { routes } = data; const endpoint = routes.find(route => route.name === endpointName); if (endpoint) { endpoint.location = this.data.location; endpoint.tenant = this.data.tenant; return new BusinessEndpoint(this.transport, endpoint, pathOptions); } return null; } /** * Get the list of resourcefuls for this ServiceDescriptor * * @todo This method isn't used and also does not return an array - it is not advised to use yet * @todo Should this return an array of ResourcefulEndpoints instead? * * @returns {Object[]} - a list of resourcefuls populated from the descriptor */ resourcefuls() { return this.data.resourcefuls; } } /** * Access the Sequoia Registry based on the access of the current user * * @param {Transport} transport - Transport instance to use for fetching * @param {string} registryUri - URI of the Sequoia registry * e.g. https://registry-reference.sequoia.piksel.com * @param {boolean} cache - Indicate wether or not to cache descriptors * * @property {string} registryUri - URI of the Sequoia registry * e.g. https://registry-reference.sequoia.piksel.com * @property {Object[]} services - Services available in the environment * @property {Object} descriptors - JSON data returned from the service's raw description * e.g. https://metadata-reference.sequoia.piksel.com/descriptor/raw?owner=demo * @property {string} tenant - The name of the current tenancy being used e.g. 'demo' * * See the {@link https://registry-reference.sequoia.piksel.com/docs|Registry docs} for more info * */ class Registry { constructor(transport, registryUri, cache = false) { this.transport = transport; this.registryUri = registryUri; this.services = []; this.descriptors = {}; this.cache = cache; } /** * Fetch the registry for this user in this tenancy * * @param {string} tenant - The name of the tenancy to use e.g. 'demo' * * @returns {Promise} */ fetch(tenant) { this.tenant = tenant; return this.refreshServices(); } getServiceLocation(serviceName) { const service = this.services.find(item => item.name === serviceName); if (service) { return service.location; } return null; } /** * Get ServiceDescriptor information from the registry. * * @deprecated Deprecated since 1.2.0. Use {@link Registry#getServiceDescriptor} * * Rejects the Promise if a service is requested that doesn't exist for this user * (or at all) * * @param {string} serviceName - The name of the service to use e.g. 'metadata' * * @returns {Promise} */ getService(serviceName) { console.warn( 'registry.getService is deprecated. Please use registry.getServiceDescriptor instead.' ); return this.getServiceDescriptor(serviceName); } /** * Get ServiceDescriptor information from the SDK cache. * * Rejects the Promise if a service is requested that doesn't exist for this user * (or at all), or if the descriptor is not in the cache * * @param {string} serviceName - The name of the service to use e.g. 'metadata' * * @returns {Promise} */ getCachedServiceDescriptor(serviceName) { const service = this.services.find(item => item.name === serviceName); if (service && this.descriptors[serviceName]) { return Promise.resolve( new ServiceDescriptor(this.transport, this.descriptors[serviceName]) ); } return Promise.reject( new Error(`No service with name ${serviceName} is in the cache`) ); } /** * Get ServiceDescriptor information from the registry. * * Rejects the Promise if a service is requested that doesn't exist for this user * (or at all) * * @param {string} serviceName - The name of the service to use e.g. 'metadata' * * @returns {Promise} */ getServiceDescriptor(serviceName) { const service = this.services.find(item => item.name === serviceName); if (service) { return this.transport .get(`${service.location}/descriptor/raw?owner=${this.tenant}`) .then((json) => { json.location = service.location; json.owner = service.owner; json.tenant = this.tenant; if (this.cache) { this.descriptors[serviceName] = json; } return new ServiceDescriptor(this.transport, json); }); } return Promise.reject( new Error(`No service with name ${serviceName} exists`) ); } /** * Get multiple ServiceDescriptor information from the registry. * * * @deprecated Deprecated since 1.2.0. Use {@link Registry#getServiceDescriptors} * * Rejects the Promise if a service is requested that doesn't exist for this user * (or at all) * * @param {...string} serviceName - The name of the service to use e.g. 'metadata' * * @returns {Promise} */ getServices(...serviceName) { console.warn( 'registry.getServices is deprecated. Please use registry.getServiceDescriptors instead.' ); return this.getServiceDescriptors(...serviceName); } /** * Get multiple ServiceDescriptor information from the services endpoints. * * Rejects the Promise if a service is requested that doesn't exist for this user * (or at all) * * @param {...string} serviceName - The name of the service to use e.g. 'metadata' * * @returns {Promise} */ getServiceDescriptors(...serviceName) { const services = serviceName.length ? serviceName : this.services.map(s => s.name); return Promise.all(services.map(s => this.getServiceDescriptor(s))); } /** * Get multiple ServiceDescriptor information from the SDK cache, falling back to the services endpoint. * * Rejects the Promise if a service is requested that doesn't exist for this user * (or at all) * * @param {...string} serviceName - The name of the service to use e.g. 'metadata' * * @returns {Promise} */ getCachedServiceDescriptors(...serviceName) { const services = serviceName.length ? serviceName : this.services.map(s => s.name); return Promise.all( services.map(s => this.getCachedServiceDescriptor(s).catch(() => this.getServiceDescriptor(s))) ); } /** * Refresh services that are cached inside the SDK. * * @returns {Promise} */ refreshServices() { return this.transport .get(`${this.registryUri}/services/${this.tenant}`) .then((json) => { this.services = json.services; this.descriptors = {}; return json; }); } } export default Registry; export { ServiceDescriptor };