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 };