UNPKG

@citrineos/ocpprouter

Version:

The ocpprouter module for OCPP v2.0.1. This module is not intended to be used directly, but rather as a dependency for other modules.

224 lines 12.5 kB
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) { var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d; if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc); else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r; return c > 3 && r && Object.defineProperty(target, key, r), r; }; var __metadata = (this && this.__metadata) || function (k, v) { if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v); }; import { AbstractModuleApi, AsDataEndpoint, BadRequestError, CacheNamespace, ConfigStoreFactory, DEFAULT_TENANT_ID, getCacheTenantPathMappingKey, HttpMethod, Namespace, NotFoundError, OCPP1_6_Namespace, OCPP2_0_1_Namespace, } from '@citrineos/base'; import { ChargingStationKeyQuerySchema, ConnectionDeleteQuerySchema, CreateSubscriptionSchema, ModelKeyQuerystringSchema, sequelize, Subscription, TenantQuerySchema, WebsocketDeleteQuerySchema, WebsocketGetQuerySchema, WebsocketMappingQuerySchema, WebsocketRequestSchema, } from '@citrineos/data'; import { Logger } from 'tslog'; /** * Admin API for the OcppRouter. */ export class AdminApi extends AbstractModuleApi { _networkConnection; _subscriptionRepository; _serverNetworkProfileRepository; /** * Constructs a new instance of the class. * * @param {IMessageRouter} ocppRouter - The OcppRouter module. * @param {INetworkConnection} networkConnection - The network connection instance. * @param {FastifyInstance} server - The Fastify server instance. * @param {BootstrapConfig & SystemConfig} config - The configuration instance. * @param {Logger<ILogObj>} [logger] - The logger instance. * @param {ISubscriptionRepository} [subscriptionRepository] - The subscription repository instance. * @param {IServerNetworkProfileRepository} [serverNetworkProfileRepository] - The server network profile repository instance. */ constructor(ocppRouter, networkConnection, server, config, logger, subscriptionRepository, serverNetworkProfileRepository) { super(ocppRouter, server, null, logger); this._networkConnection = networkConnection; this._subscriptionRepository = subscriptionRepository || new sequelize.SequelizeSubscriptionRepository(config, this._logger); this._serverNetworkProfileRepository = serverNetworkProfileRepository || new sequelize.SequelizeServerNetworkProfileRepository(config, this._logger); } // N.B.: When adding subscriptions, chargers may be connected to a different instance of Citrine. // If this is the case, new subscriptions will not take effect until the charger reconnects. /** * Creates a {@link Subscription}. * Will always create a new entity and return its id. * * @param {FastifyRequest<{ Body: Subscription }>} request - The request object, containing the body which is parsed as a {@link Subscription}. * @return {Promise<number>} The id of the created subscription. */ async postSubscription(request) { const tenantId = request.query.tenantId; request.body.tenantId = tenantId; if (!request.body.onClose && !request.body.onConnect && !request.body.onMessage && !request.body.sentMessage) { throw new BadRequestError('Must specify at least one of onConnect, onClose, onMessage, sentMessage to true.'); } return this._subscriptionRepository .create(tenantId, request.body) .then((subscription) => subscription?.id); } async getSubscriptionsByChargingStation(request) { return this._subscriptionRepository.readAllByStationId(request.query.tenantId, request.query.stationId); } async deleteSubscriptionById(request) { const tenantId = request.query.tenantId ?? DEFAULT_TENANT_ID; return this._subscriptionRepository .deleteByKey(tenantId, request.query.id.toString()) .then(() => true); } async getWebsocketConfigurations(request) { if (request.query.id) { const websocketConfig = this._module.config.util.networkConnection.websocketServers.find((ws) => ws.id === request.query.id); if (!websocketConfig) { throw new NotFoundError(`Could not find websocket configuration with id ${request.query.id}`); } else { return websocketConfig; } } else { // TODO when available (coming soon in a separate feature), filter by tenantId if the tenantId query param exists return this._module.config.util.networkConnection.websocketServers; } } async createWebsocketConfiguration(request) { const existingConfig = this._module.config.util.networkConnection.websocketServers.find((ws) => ws.id === request.body.id); if (existingConfig) { throw new BadRequestError(`Websocket configuration with id ${request.body.id} already exists.`); } else { this._module.config.util.networkConnection.websocketServers.push(request.body); await ConfigStoreFactory.getInstance().saveConfig(this._module.config); return request.body; } } /** * Adds or updates a mapping from a path segment to a tenant for a specific websocket server. */ async putWebsocketMapping(request) { const { id: serverId, path, tenantId } = request.query; this._module.config = (await ConfigStoreFactory.getInstance().fetchConfig()); const websocketConfig = this._module.config.util.networkConnection.websocketServers.find((ws) => ws.id === serverId); if (!websocketConfig) { throw new NotFoundError(`Websocket configuration with id ${serverId} not found`); } if (!websocketConfig.tenantPathMapping) { websocketConfig.tenantPathMapping = {}; } if (websocketConfig.tenantPathMapping[path] !== undefined && websocketConfig.tenantPathMapping[path] !== tenantId) { throw new BadRequestError(`Path ${path} is already mapped to tenant ${websocketConfig.tenantPathMapping[path]}`); } websocketConfig.tenantPathMapping[path] = tenantId; websocketConfig.dynamicTenantResolution = true; await ConfigStoreFactory.getInstance().saveConfig(this._module.config); await this._serverNetworkProfileRepository.upsertServerNetworkProfile(websocketConfig, this._module.config.maxCallLengthSeconds); await this._module.cache.set(getCacheTenantPathMappingKey(websocketConfig.id, path), tenantId.toString(), CacheNamespace.TenantPathMapping); return websocketConfig; } /** * Removes a mapping for a specific path OR all mappings for a specific tenant from a websocket server. */ async deleteWebsocketMapping(request) { const { id: serverId, path, tenantId } = request.query; this._module.config = (await ConfigStoreFactory.getInstance().fetchConfig()); const websocketConfig = this._module.config.util.networkConnection.websocketServers.find((ws) => ws.id === serverId); if (!websocketConfig) { throw new NotFoundError(`Websocket configuration with id ${serverId} not found`); } if (websocketConfig.tenantPathMapping) { if (websocketConfig.tenantPathMapping[path] === undefined) { throw new NotFoundError(`Mapping for path ${path} not found in websocket configuration ${serverId}`); } else if (websocketConfig.tenantPathMapping[path] !== tenantId) { throw new BadRequestError(`Mapping for path ${path} is not mapped to tenant ${tenantId}`); } else if (websocketConfig.tenantPathMapping[path] === tenantId) { delete websocketConfig.tenantPathMapping[path]; } await ConfigStoreFactory.getInstance().saveConfig(this._module.config); await this._serverNetworkProfileRepository.upsertServerNetworkProfile(websocketConfig, this._module.config.maxCallLengthSeconds); await this._module.cache.remove(getCacheTenantPathMappingKey(websocketConfig.id, path), CacheNamespace.TenantPathMapping); } return websocketConfig; } async deleteWebsocketConfiguration(request) { const existingConfigIndex = this._module.config.util.networkConnection.websocketServers.findIndex((ws) => ws.id === request.query.id); if (existingConfigIndex) { this._module.config.util.networkConnection.websocketServers.splice(existingConfigIndex, 1); await ConfigStoreFactory.getInstance().saveConfig(this._module.config); } } // Forcibly disconnect a websocket connection by station id and tenant id and mark the station as offline async deleteWebsocketConnection(request) { await this._networkConnection.disconnect(request.query.tenantId, request.query.stationId); } /** * Overrides superclass method to generate the URL path based on the input {@link Namespace} * and the module's endpoint prefix configuration. * * @param {Namespace} input - The input {@link Namespace}. * @return {string} - The generated URL path. */ _toDataPath(input) { const endpointPrefix = '/ocpprouter'; return super._toDataPath(input, endpointPrefix); } } __decorate([ AsDataEndpoint(OCPP2_0_1_Namespace.Subscription, HttpMethod.Post, TenantQuerySchema, CreateSubscriptionSchema), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], AdminApi.prototype, "postSubscription", null); __decorate([ AsDataEndpoint(OCPP2_0_1_Namespace.Subscription, HttpMethod.Get, ChargingStationKeyQuerySchema), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], AdminApi.prototype, "getSubscriptionsByChargingStation", null); __decorate([ AsDataEndpoint(OCPP2_0_1_Namespace.Subscription, HttpMethod.Delete, ModelKeyQuerystringSchema), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], AdminApi.prototype, "deleteSubscriptionById", null); __decorate([ AsDataEndpoint(Namespace.Websocket, HttpMethod.Get, WebsocketGetQuerySchema), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], AdminApi.prototype, "getWebsocketConfigurations", null); __decorate([ AsDataEndpoint(Namespace.Websocket, HttpMethod.Post, undefined, WebsocketRequestSchema), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], AdminApi.prototype, "createWebsocketConfiguration", null); __decorate([ AsDataEndpoint(Namespace.WebsocketMapping, HttpMethod.Put, WebsocketMappingQuerySchema), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], AdminApi.prototype, "putWebsocketMapping", null); __decorate([ AsDataEndpoint(Namespace.WebsocketMapping, HttpMethod.Delete, WebsocketMappingQuerySchema), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], AdminApi.prototype, "deleteWebsocketMapping", null); __decorate([ AsDataEndpoint(Namespace.Websocket, HttpMethod.Delete, WebsocketDeleteQuerySchema), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], AdminApi.prototype, "deleteWebsocketConfiguration", null); __decorate([ AsDataEndpoint(Namespace.Connection, HttpMethod.Delete, ConnectionDeleteQuerySchema), __metadata("design:type", Function), __metadata("design:paramtypes", [Object]), __metadata("design:returntype", Promise) ], AdminApi.prototype, "deleteWebsocketConnection", null); //# sourceMappingURL=DataApi.js.map