UNPKG

@hashgraph/solo

Version:

An opinionated CLI tool to deploy and manage private Hedera Networks.

243 lines 11.3 kB
/** * SPDX-License-Identifier: Apache-2.0 */ import { ComponentType, ConsensusNodeStates } from './enumerations.js'; import { SoloError } from '../../errors.js'; import { BaseComponent } from './components/base_component.js'; import { RelayComponent } from './components/relay_component.js'; import { HaProxyComponent } from './components/ha_proxy_component.js'; import { MirrorNodeComponent } from './components/mirror_node_component.js'; import { EnvoyProxyComponent } from './components/envoy_proxy_component.js'; import { ConsensusNodeComponent } from './components/consensus_node_component.js'; import { MirrorNodeExplorerComponent } from './components/mirror_node_explorer_component.js'; /** * Represent the components in the remote config and handles: * - CRUD operations on the components. * - Validation. * - Conversion FROM and TO plain object. */ export class ComponentsDataWrapper { relays; haProxies; mirrorNodes; envoyProxies; consensusNodes; mirrorNodeExplorers; /** * @param relays - Relay record mapping service name to relay components * @param haProxies - HA Proxies record mapping service name to ha proxies components * @param mirrorNodes - Mirror Nodes record mapping service name to mirror nodes components * @param envoyProxies - Envoy Proxies record mapping service name to envoy proxies components * @param consensusNodes - Consensus Nodes record mapping service name to consensus nodes components * @param mirrorNodeExplorers - Mirror Node Explorers record mapping service name to mirror node explorers components */ constructor(relays = {}, haProxies = {}, mirrorNodes = {}, envoyProxies = {}, consensusNodes = {}, mirrorNodeExplorers = {}) { this.relays = relays; this.haProxies = haProxies; this.mirrorNodes = mirrorNodes; this.envoyProxies = envoyProxies; this.consensusNodes = consensusNodes; this.mirrorNodeExplorers = mirrorNodeExplorers; this.validate(); } /* -------- Modifiers -------- */ /** Used to add new component to their respective group. */ add(serviceName, component) { const self = this; if (!serviceName || typeof serviceName !== 'string') { throw new SoloError(`Service name is required ${serviceName}`); } if (!(component instanceof BaseComponent)) { throw new SoloError('Component must be instance of BaseComponent', null, BaseComponent); } function addComponentCallback(components) { if (self.exists(components, component)) { throw new SoloError('Component exists', null, component.toObject()); } components[serviceName] = component; } self.applyCallbackToComponentGroup(component.type, serviceName, addComponentCallback); } /** Used to edit an existing component from their respective group. */ edit(serviceName, component) { const self = this; if (!serviceName || typeof serviceName !== 'string') { throw new SoloError(`Service name is required ${serviceName}`); } if (!(component instanceof BaseComponent)) { throw new SoloError('Component must be instance of BaseComponent', null, BaseComponent); } function editComponentCallback(components) { if (!components[serviceName]) { throw new SoloError(`Component doesn't exist, name: ${serviceName}`, null, { component }); } components[serviceName] = component; } self.applyCallbackToComponentGroup(component.type, serviceName, editComponentCallback); } /** Used to remove specific component from their respective group. */ remove(serviceName, type) { const self = this; if (!serviceName || typeof serviceName !== 'string') { throw new SoloError(`Service name is required ${serviceName}`); } if (!Object.values(ComponentType).includes(type)) { throw new SoloError(`Invalid component type ${type}`); } function deleteComponentCallback(components) { if (!components[serviceName]) { throw new SoloError(`Component ${serviceName} of type ${type} not found while attempting to remove`); } delete components[serviceName]; } self.applyCallbackToComponentGroup(type, serviceName, deleteComponentCallback); } /* -------- Utilities -------- */ getComponent(type, serviceName) { let component; function getComponentCallback(components) { if (!components[serviceName]) { throw new SoloError(`Component ${serviceName} of type ${type} not found while attempting to read`); } component = components[serviceName]; } this.applyCallbackToComponentGroup(type, serviceName, getComponentCallback); return component; } /** * Method used to map the type to the specific component group * and pass it to a callback to apply modifications */ applyCallbackToComponentGroup(type, serviceName, callback) { switch (type) { case ComponentType.Relay: callback(this.relays); break; case ComponentType.HaProxy: callback(this.haProxies); break; case ComponentType.MirrorNode: callback(this.mirrorNodes); break; case ComponentType.EnvoyProxy: callback(this.envoyProxies); break; case ComponentType.ConsensusNode: callback(this.consensusNodes); break; case ComponentType.MirrorNodeExplorer: callback(this.mirrorNodeExplorers); break; default: throw new SoloError(`Unknown component type ${type}, service name: ${serviceName}`); } this.validate(); } /** * Handles creating instance of the class from plain object. * * @param components - component groups distinguished by their type. */ static fromObject(components) { const relays = {}; const haProxies = {}; const mirrorNodes = {}; const envoyProxies = {}; const consensusNodes = {}; const mirrorNodeExplorers = {}; Object.entries(components).forEach(([type, components]) => { switch (type) { case ComponentType.Relay: Object.entries(components).forEach(([name, component]) => { relays[name] = RelayComponent.fromObject(component); }); break; case ComponentType.HaProxy: Object.entries(components).forEach(([name, component]) => { haProxies[name] = HaProxyComponent.fromObject(component); }); break; case ComponentType.MirrorNode: Object.entries(components).forEach(([name, component]) => { mirrorNodes[name] = MirrorNodeComponent.fromObject(component); }); break; case ComponentType.EnvoyProxy: Object.entries(components).forEach(([name, component]) => { envoyProxies[name] = EnvoyProxyComponent.fromObject(component); }); break; case ComponentType.ConsensusNode: Object.entries(components).forEach(([name, component]) => { consensusNodes[name] = ConsensusNodeComponent.fromObject(component); }); break; case ComponentType.MirrorNodeExplorer: Object.entries(components).forEach(([name, component]) => { mirrorNodeExplorers[name] = MirrorNodeExplorerComponent.fromObject(component); }); break; default: throw new SoloError(`Unknown component type ${type}`); } }); return new ComponentsDataWrapper(relays, haProxies, mirrorNodes, envoyProxies, consensusNodes, mirrorNodeExplorers); } /** Used to create an empty instance used to keep the constructor private */ static initializeEmpty() { return new ComponentsDataWrapper(); } static initializeWithNodes(nodeAliases, cluster, namespace) { const consensusNodeComponents = {}; nodeAliases.forEach((alias, index) => { consensusNodeComponents[alias] = new ConsensusNodeComponent(alias, cluster, namespace, ConsensusNodeStates.REQUESTED, index); }); const componentDataWrapper = new ComponentsDataWrapper(undefined, undefined, undefined, undefined, consensusNodeComponents, undefined); return componentDataWrapper; } /** checks if component exists in the respective group */ exists(components, newComponent) { return Object.values(components).some(component => BaseComponent.compare(component, newComponent)); } validate() { function testComponentsObject(components, expectedInstance) { Object.entries(components).forEach(([name, component]) => { if (!name || typeof name !== 'string') { throw new SoloError(`Invalid component service name ${{ [name]: component?.constructor?.name }}`); } if (!(component instanceof expectedInstance)) { throw new SoloError(`Invalid component type, service name: ${name}, ` + `expected ${expectedInstance?.name}, actual: ${component?.constructor?.name}`, null, { component }); } }); } testComponentsObject(this.relays, RelayComponent); testComponentsObject(this.haProxies, HaProxyComponent); testComponentsObject(this.mirrorNodes, MirrorNodeComponent); testComponentsObject(this.envoyProxies, EnvoyProxyComponent); testComponentsObject(this.consensusNodes, ConsensusNodeComponent); testComponentsObject(this.mirrorNodeExplorers, MirrorNodeExplorerComponent); } toObject() { function transform(components) { const transformedComponents = {}; Object.entries(components).forEach(([name, component]) => { transformedComponents[name] = component.toObject(); }); return transformedComponents; } return { [ComponentType.Relay]: transform(this.relays), [ComponentType.HaProxy]: transform(this.haProxies), [ComponentType.MirrorNode]: transform(this.mirrorNodes), [ComponentType.EnvoyProxy]: transform(this.envoyProxies), [ComponentType.ConsensusNode]: transform(this.consensusNodes), [ComponentType.MirrorNodeExplorer]: transform(this.mirrorNodeExplorers), }; } clone() { const data = this.toObject(); return ComponentsDataWrapper.fromObject(data); } } //# sourceMappingURL=components_data_wrapper.js.map