Services/ConfigManager.js

/**Copyright (c) 2009-2023, NVIDIA CORPORATION & AFFILIATES. All rights reserved.**/
"use strict";const BadRequestError=require("../Errors/BadRequestError"),InternalServerError=require("../Errors/InternalServerError"),InvalidInputError=require("../Errors/InvalidInputError"),ResourceNotFoundError=require("../Errors/ResourceNotFoundError"),Validator=require("../Utils/Validator"),NotificationManager=require("./NotificationManager"),Elasticsearch=require("../Utils/Elasticsearch"),mtmcAnalyticsConfigSchema=require("../ajv-schemas/mtmcAnalyticsConfig.json"),winston=require("winston"),logger=winston.createLogger({transports:[new winston.transports.Console({timestamp:!0})],exitOnError:!1});
/** 
 * Class which defines ConfigManager
 * @memberof mdxWebApiCore.Services
 * */
class ConfigManager{static#e=new Set(["mdx-mtmc-analytics"]);
/**
     * Returns valid docTypes for ConfigManager.
     * @public
     * @static
     * @returns {Set<string>} A set containing valid docTypes is returned.
     * @example
     * const mdx = require("@nvidia-mdx/web-api-core");
     * let result = mdx.Services.ConfigManager.getValidDocTypes();
     */
static getValidDocTypes(){return this.#e}async#i(e,i){const t=`${e.getConfigs().get("indexPrefix")}${Elasticsearch.getIndex("configs")}`,r=e.getClient();let n={index:t,body:{query:{ids:{values:[i]}}},size:1},a=await Elasticsearch.getSearchResults(r,n,!1),o=null;return a.indexAbsent||(a=Elasticsearch.searchResultFormatter(a.body),a.length>0&&(o=a[0])),o}
/** 
     * returns an object containing config and the timestamp associated with it.
     * @public
     * @async
     * @param {Database} documentDb - Database Object
     * @param {("mdx-mtmc-analytics")} docType
     * @returns {Promise<Object>} Config Object along with timestamp is returned
     * @example
     * const mdx = require("@nvidia-mdx/web-api-core");
     * const elastic = new mdx.Utils.Elasticsearch({node: "elasticsearch-url"},databaseConfigMap);
     * let docType = "mdx-mtmc-analytics";
     * let configManagerObject = new mdx.Services.ConfigManager();
     * let result = await configManagerObject.getConfig(elastic,docType);
     */async getConfig(e,i){if(!ConfigManager.#e.has(i))throw new BadRequestError(`Invalid docType: ${i}.`);let t=null;const r=(new Date).toISOString();if("Elasticsearch"===e.getName())return t=await this.#i(e,i),null!=t?t:{config:null,timestamp:r};throw new InternalServerError(`Invalid database: ${e.getName()}.`)}async#t(e,{docType:i,config:t}){const r=`${e.getConfigs().get("indexPrefix")}${Elasticsearch.getIndex("configs")}`,n=e.getClient(),a="timestamp",o="addInsertTimestamp-field-timestamp";(await n.ingest.getPipeline()).body.hasOwnProperty(o)||await Elasticsearch.initTimestampIngestPipeline(n,a);let s={index:r,body:{config:t},pipeline:o,id:i},c=await n.index(s);return await n.indices.refresh({index:r}),c}async#r(e,{docType:i,config:t,timestamp:r,eventType:n}){const a=`${e.getConfigs().get("indexPrefix")}${Elasticsearch.getIndex("configsAudit")}`,o=e.getClient();let s={index:a,body:{docType:i,config:t,timestamp:r,eventType:n}},c=await o.index(s);return await o.indices.refresh({index:a}),c}
/** 
     * Inserts the initial config into the database.
     * @public
     * @async
     * @param {Database} documentDb - Database Object
     * @param {("mdx-mtmc-analytics")} docType
     * @param {string} config - A json stringified config object
     * @example
     * const mdx = require("@nvidia-mdx/web-api-core");
     * const elastic = new mdx.Utils.Elasticsearch({node: "elasticsearch-url"},databaseConfigMap);
     * let docType = "mdx-mtmc-analytics";
     * let configManagerObject = new mdx.Services.ConfigManager();
     * await configManagerObject.initConfig(elastic,docType,config);
     */async initConfig(e,i,t){if(!ConfigManager.#e.has(i))throw new BadRequestError(`Invalid docType: ${i}.`);if("mdx-mtmc-analytics"===i){if(!Validator.validateJsonSchema(JSON.parse(t),mtmcAnalyticsConfigSchema,!1).valid)throw new BadRequestError("MTMC Analytics Configuration doesn't follow schema.")}switch(e.getName()){case"Elasticsearch":{await this.#t(e,{docType:i,config:t});let r=await this.#i(e,i);if(null!=r){let{timestamp:n}=r,a={docType:i,config:t,timestamp:n,eventType:"upsert-all"};await this.#r(e,a),logger.info(`[DATA] Config init of docType: ${i} was successful.`);break}throw new InternalServerError(`Insertion of ${i} config has failed. Couldn't find config before audit index insertion.`)}default:throw new InternalServerError(`Invalid database: ${e.getName()}.`)}}
/** 
     * returns a success message once the config has been updated and kafka message is sent.
     * @public
     * @async
     * @param {Database} documentDb - Database Object
     * @param {MessageBroker} messageBroker - MessageBroker Object
     * @param {("mdx-mtmc-analytics")} docType
     * @param {string} inputConfig - A json stringified config object
     * @returns {Promise<Object>} A success message is returned
     * @example
     * const mdx = require("@nvidia-mdx/web-api-core");
     * const elastic = new mdx.Utils.Elasticsearch({node: "elasticsearch-url"},databaseConfigMap);
     * const kafka = new mdx.Utils.Kafka({brokers: ["kafka-broker-url"]}, kafkaConfigMap);
     * let docType = "mdx-mtmc-analytics";
     * let configManagerObject = new mdx.Services.ConfigManager();
     * let result = await configManagerObject.update(elastic,kafka,docType,inputConfig);
     */async update(e,i,t,r){if(null==i)throw new InvalidInputError("A message broker like 'kafka' is required to dynamically update a microservice's config.");if(!ConfigManager.#e.has(t))throw new BadRequestError(`Invalid docType: ${t}.`);if("mdx-mtmc-analytics"===t){if(!Validator.validateJsonSchema(r,mtmcAnalyticsConfigSchema,!1).valid)throw new BadRequestError("MTMC Analytics Configuration doesn't follow schema.")}let n=await this.getConfig(e,t),a=new NotificationManager;if(null==n.config)throw await a.produceConfigNotification(i,{docType:t,timestamp:n.timestamp,eventType:"upsert-all",config:n.config}),new ResourceNotFoundError(`Initial config of ${t} doesn't exist. Fetching it from the microservice. Please try again after some time.`);if("Elasticsearch"===e.getName()){let o=JSON.parse(n.config);if("mdx-mtmc-analytics"===t)for(let e in r)for(let i in r[e])o[e][i]=r[e][i];await this.#t(e,{docType:t,config:JSON.stringify(o)});let s=await this.#i(e,t);if(null!=s){let{timestamp:n}=s,o={docType:t,config:JSON.stringify(r),timestamp:n,eventType:"upsert"};return await Promise.all([this.#r(e,o),a.produceConfigNotification(i,o)]),{success:!0}}throw new InternalServerError(`Insertion of ${t} config has failed. Couldn't find config before audit index insertion.`)}throw new InternalServerError(`Invalid database: ${e.getName()}.`)}}module.exports=ConfigManager;