diff --git a/utilities/boundary-bulk-bff/src/server/config/schemas/boundaryTemplateGenerateBody.ts b/utilities/boundary-bulk-bff/src/server/config/schemas/boundaryTemplateGenerateBody.ts new file mode 100644 index 00000000000..dc97359664f --- /dev/null +++ b/utilities/boundary-bulk-bff/src/server/config/schemas/boundaryTemplateGenerateBody.ts @@ -0,0 +1,17 @@ +export const boundaryTemplateGenerateBodySchema = { + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "hierarchyType": { + "type": "string", + "maxLength": 128, + "minLength": 1 + }, + "tenantId": { + "type": "string", + "maxLength": 64, + "minLength": 1 + } + }, + "required": ["tenantId", "hierarchyType"] +}; diff --git a/utilities/boundary-bulk-bff/src/server/controllers/boundaryManage/boundaryManage.controller.ts b/utilities/boundary-bulk-bff/src/server/controllers/boundaryManage/boundaryManage.controller.ts index cb29e13bd4d..8b3cca0f265 100644 --- a/utilities/boundary-bulk-bff/src/server/controllers/boundaryManage/boundaryManage.controller.ts +++ b/utilities/boundary-bulk-bff/src/server/controllers/boundaryManage/boundaryManage.controller.ts @@ -1,5 +1,5 @@ import * as express from "express"; -import { searchBoundaryDetailService } from "../../service/boundaryManageService"; +import { generateBoundaryTemplateService, searchBoundaryDetailService } from "../../service/boundaryManageService"; import { logger } from "../../utils/logger"; import { errorResponder, sendResponse } from "../../utils/genericUtils"; import { createBoundariesService } from "../../service/boundaryManageService"; @@ -22,6 +22,7 @@ class boundaryManageController { public intializeRoutes() { this.router.post(`${this.path}/create`, this.createBoundaries); this.router.post(`${this.path}/search`, this.searchBoundaryDetails); + this.router.post(`${this.path}/generate`, this.generateBoundaryTemplate); } /** * Handles the creation of a project type campaign. @@ -60,6 +61,22 @@ class boundaryManageController { } }; + generateBoundaryTemplate = async ( + request: express.Request, + response: express.Response + ) => { + try { + logger.info("RECEIVED A BOUNDARY TEMPLATE GENERATE REQUEST"); + await generateBoundaryTemplateService(request); + return sendResponse(response, { boundaryDetails: request.body.boundaryDetails }, request); + } catch (e: any) { + console.log(e) + logger.error(String(e)) + // Handle errors and send error response + return errorResponder({ message: String(e), code: e?.code, description: e?.description }, request, response, e?.status || 500); + } + }; + }; export default boundaryManageController; diff --git a/utilities/boundary-bulk-bff/src/server/service/boundaryManageService.ts b/utilities/boundary-bulk-bff/src/server/service/boundaryManageService.ts index 6d866bd1cea..db7abc74b27 100644 --- a/utilities/boundary-bulk-bff/src/server/service/boundaryManageService.ts +++ b/utilities/boundary-bulk-bff/src/server/service/boundaryManageService.ts @@ -1,8 +1,8 @@ import express from "express"; import { logger } from "../utils/logger"; -import { validateCreateBoundariesRequest, validateSearchBoundaryDetailRequest } from "../validators/boundaryValidator"; +import { validateBoundaryTemplateGenerateRequest, validateCreateBoundariesRequest, validateSearchBoundaryDetailRequest } from "../validators/boundaryValidator"; import { getLocalizedMessagesHandler } from "../utils/localisationUtils"; -import { boundaryBulkUpload, getBoundaryDetails } from "../utils/boundaryUtils"; +import { boundaryBulkUpload, generateBoundaryFile, getBoundaryDetails } from "../utils/boundaryUtils"; import { enrichAndPersistBoundaryDetails } from "../utils/persistUtils"; export async function createBoundariesService(request: express.Request, response: express.Response) { @@ -20,3 +20,11 @@ export async function searchBoundaryDetailService(request: express.Request) { validateSearchBoundaryDetailRequest(request); await getBoundaryDetails(request); } + + +export async function generateBoundaryTemplateService(request: express.Request) { + const localizationMap = await getLocalizedMessagesHandler(request, request?.query?.tenantId); + request.body.localizationMap = localizationMap; + await validateBoundaryTemplateGenerateRequest(request); + await generateBoundaryFile(request); +} diff --git a/utilities/boundary-bulk-bff/src/server/utils/boundaryUtils.ts b/utilities/boundary-bulk-bff/src/server/utils/boundaryUtils.ts index bc5612a5425..2c06fff40b5 100644 --- a/utilities/boundary-bulk-bff/src/server/utils/boundaryUtils.ts +++ b/utilities/boundary-bulk-bff/src/server/utils/boundaryUtils.ts @@ -2,7 +2,7 @@ import config from "../config"; import { executeQuery } from "./db"; import { throwError } from "./errorUtils"; import { addDataToSheet, createAndUploadFile, createExcelSheet, getExcelWorkbookFromFileURL, getNewExcelWorkbook, getSheetData, prepareDataForExcel } from "./excelUtils"; -import { getLocalizedHeaders, getLocalizedName, transformAndCreateLocalisation } from "./localisationUtils"; +import { getLocalizedHeaders, getLocalizedName, localiseAllDatas, transformAndCreateLocalisation } from "./localisationUtils"; import { getFormattedStringForDebug, logger } from "./logger"; import { persistEntityCreate, persistError, persistRelationship, processAndPersist } from "./persistUtils"; import { httpRequest } from "./request"; @@ -696,3 +696,93 @@ function formatRows(rows: any) { delete row.lastmodifiedby; }); } + +export async function generateBoundaryFile(request: any) { + const allBoundaries = await getBoundariesOfHierarchy(request) + const workbook = getNewExcelWorkbook(); + const localizedBoundaryTab = getLocalizedName(config?.boundary?.boundaryTab, request?.body?.localizationMap); + const boundarySheet = workbook.addWorksheet(localizedBoundaryTab); + addDataToSheet(boundarySheet, allBoundaries, undefined, undefined, true, true); + const boundaryFileDetails: any = await createAndUploadFile(workbook, request); + enrichRequestWithfFileId(request, boundaryFileDetails?.[0]?.fileStoreId); +} + +function enrichRequestWithfFileId(request: any, fileStoreId: string) { + request.body.boundaryDetails = { + tenantId: request?.query?.tenantId, + hierarchyType: request?.query?.hierarchyType, + fileStoreId: fileStoreId + } +} + +async function getBoundariesOfHierarchy(request: any) { + const { hierarchyType } = request?.query || {}; + const hierarchy = await getHierarchy(request, request?.query?.tenantId, request?.query?.hierarchyType) + const modifiedHierarchy = hierarchy.map(ele => `${hierarchyType}_${ele}`.toUpperCase()) + logger.info("modifiedHierarchy during generateBoundaryFile " + JSON.stringify(modifiedHierarchy)) + const headers = [...modifiedHierarchy, config?.boundary?.boundaryCode]; + const localizedHeaders = getLocalizedHeaders(headers, request?.body?.localizationMap); + logger.info("localizedHeaders during generateBoundaryFile " + JSON.stringify(localizedHeaders)) + const allBoundaries = await getAllBoundaries(request) + const allDatas = getAllDatas(allBoundaries) + var localisedData = await localiseAllDatas(request, allDatas) + formatBoundaryData(allDatas, localisedData, localizedHeaders) + return [localizedHeaders, ...localisedData] +} + +function getAllDatas(allBoundaries: any) { + let allDatas: string[][] = [] + for (const data of allBoundaries) { + generatePaths(data, [], allDatas) + } + return allDatas +} + + +function formatBoundaryData(allDatas: any, localisedData: any, localizedHeaders: any) { + const lengthOfHeaders = localizedHeaders.length + for (let i = 0; i < localisedData.length; i++) { + const localisedDatas = localisedData[i] + const unlocalisedDatas = allDatas[i] + const dataLength = unlocalisedDatas.length + const lastElement = unlocalisedDatas[dataLength - 1]; + const toAdd = lengthOfHeaders - dataLength - 1; + if (toAdd >= 0) { + for (let i = 0; i < toAdd; i++) { + localisedDatas.push('') + } + localisedDatas.push(lastElement) + } + } +} + +function generatePaths(data: any, path: string[] = [], allDatas: string[][]) { + const newPath = [...path, data.code]; + allDatas.push(newPath); + if (data.children && data.children.length > 0) { + for (let child of data.children) { + generatePaths(child, newPath, allDatas); + } + } +} + +async function getAllBoundaries(request: any) { + const hierarchyType = request?.query?.hierarchyType; + const requestBody = { + RequestInfo: request?.body?.RequestInfo + } + const requestParams = { + tenantId: request?.query?.tenantId, + hierarchyType: hierarchyType, + "includeChildren": true + } + const url = `${config.host.boundaryHost}${config.paths.boundaryRelationship}`; + const boundaryRelationshipResponse = await httpRequest(url, requestBody, requestParams, undefined, undefined); + const boundaryData = boundaryRelationshipResponse?.TenantBoundary?.[0]?.boundary; + if (boundaryData) { + return boundaryData; + } + else { + return []; + } +} diff --git a/utilities/boundary-bulk-bff/src/server/utils/localisationUtils.ts b/utilities/boundary-bulk-bff/src/server/utils/localisationUtils.ts index 57cb308d35f..25f38f4f3cc 100644 --- a/utilities/boundary-bulk-bff/src/server/utils/localisationUtils.ts +++ b/utilities/boundary-bulk-bff/src/server/utils/localisationUtils.ts @@ -133,3 +133,13 @@ export async function transformAndCreateLocalisation( // Call method to create localisation entries await createLocalisation(localisationMessages, tenantId, request); }; + +export async function localiseAllDatas(request: any, arrayDatas: any) { + const newLocaisationMap: any = await getLocalizedMessagesHandler(request, request?.query?.tenantId, getLocalisationModuleName(request?.query?.hierarchyType));; + let localisedArray: any[] = []; + arrayDatas.map((data: any) => { + const localisedElement = getLocalizedHeaders(data, newLocaisationMap); + localisedArray.push(localisedElement); + }) + return localisedArray; +} diff --git a/utilities/boundary-bulk-bff/src/server/validators/boundaryValidator.ts b/utilities/boundary-bulk-bff/src/server/validators/boundaryValidator.ts index 6547270e8ba..0272af43b1d 100644 --- a/utilities/boundary-bulk-bff/src/server/validators/boundaryValidator.ts +++ b/utilities/boundary-bulk-bff/src/server/validators/boundaryValidator.ts @@ -7,6 +7,7 @@ import { getLocalizedHeaders, getLocalizedName } from "../utils/localisationUtil import { getHeadersOfBoundarySheet, getHierarchy } from "../utils/boundaryUtils"; import { getSheetData } from "../utils/excelUtils"; import { boundarySearchBodySchema } from "../config/schemas/boundarySearchBody"; +import { boundaryTemplateGenerateBodySchema } from "../config/schemas/boundaryTemplateGenerateBody"; const _ = require('lodash'); @@ -83,4 +84,9 @@ export async function validateCreateBoundariesRequest(request: any, localization export function validateSearchBoundaryDetailRequest(request: any) { validateBodyViaSchema(boundarySearchBodySchema, request.query); +} + +export async function validateBoundaryTemplateGenerateRequest(request: any) { + validateBodyViaSchema(boundaryTemplateGenerateBodySchema, request.query); + validateHierarchyType(request, request?.query?.hierarchyType, request?.query?.tenantId); } \ No newline at end of file diff --git a/utilities/boundary-bulk-bff/src/server/validators/genericValidator.ts b/utilities/boundary-bulk-bff/src/server/validators/genericValidator.ts index 551dd16c29a..5ef3cec458e 100644 --- a/utilities/boundary-bulk-bff/src/server/validators/genericValidator.ts +++ b/utilities/boundary-bulk-bff/src/server/validators/genericValidator.ts @@ -17,6 +17,11 @@ export function validateBodyViaSchema(schema: any, objectData: any) { const dataPath = error.dataPath.replace(/\//g, '.').replace(/^\./, ''); formattedErrorMessage = `${dataPath} ${error.message}`; } + else if (error?.instancePath) { + // Replace slash with dot and remove leading dot if present + const dataPath = error.instancePath.replace(/\//g, '.').replace(/^\./, ''); + formattedErrorMessage = `${dataPath} ${error.message}`; + } else { formattedErrorMessage = `${error.message}` }