Skip to content

Commit

Permalink
Merge branch 'console' into console-v0.3
Browse files Browse the repository at this point in the history
  • Loading branch information
jagankumar-egov committed Dec 3, 2024
2 parents f327392 + c587a40 commit 4458113
Show file tree
Hide file tree
Showing 12 changed files with 166 additions and 47 deletions.
6 changes: 6 additions & 0 deletions health-services/project-factory/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,9 @@ All notable changes to this module will be documented in this file.
1. Timeline integration for workflow of campaign.
2. Call user, facility and boundary generate when boundaries changed in campaign update flow
3. Generate target template based on delivery conditions changed to anything from default.

## 0.3.0 - 2024-12-03
1. Campaign Details Table Updates -> added new columns: parentId and active,removed unique constraint on campaignName.

Check notice

Code scanning / Remark-lint (reported by Codacy)

Warn when list item bullets are indented. Note

[list-item-bullet-indent] Incorrect indentation before bullet: remove 2 spaces
2. Update Ongoing Campaign (can add new boundaries , edit facilities , user and target sheet).

Check notice

Code scanning / Remark-lint (reported by Codacy)

Warn when list item bullets are indented. Note

[list-item-bullet-indent] Incorrect indentation before bullet: remove 2 spaces
3. Boundary Management Apis added.

Check notice

Code scanning / Remark-lint (reported by Codacy)

Warn when list item bullets are indented. Note

[list-item-bullet-indent] Incorrect indentation before bullet: remove 2 spaces
4. Microplan Integration api (fetch-from-microplan api) added.

Check notice

Code scanning / Remark-lint (reported by Codacy)

Warn when list item bullets are indented. Note

[list-item-bullet-indent] Incorrect indentation before bullet: remove 2 spaces
4 changes: 3 additions & 1 deletion health-services/project-factory/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ ARG COMMIT_ID
ENV BRANCH_NAME=$BRANCH_NAME
ENV ACTION_NUMBER=$ACTION_NUMBER
ENV COMMIT_ID=$COMMIT_ID
ENV NODE_OPTIONS="--max-old-space-size=2048"

# Copy package.json and yarn.lock (if exists)
COPY package.json ./
Expand All @@ -30,5 +31,6 @@ COPY . .
EXPOSE 3000

CMD ["yarn", "prod"]
# Replace "app.js" with your entry point file
# Replaced by CMD ["yarn", "prod"]


Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,12 @@ function getRawCellValue(cell: any) {
else if ('formula' in cell.value) {
// Get the result of the formula
return cell.value.result;
} else if ('error' in cell.value) {
}
else if('sharedFormula' in cell.value){
// Get the result of the shared formula
return cell.value.result;
}
else if ('error' in cell.value) {
// Get the error value
return cell.value.error;
} else if (cell.value instanceof Date) {
Expand Down
28 changes: 28 additions & 0 deletions health-services/project-factory/src/server/api/healthApis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { httpRequest } from "../utils/request";
import { defaultRequestInfo } from "./coreApis";
import config from "../config";
import { throwError } from "../utils/genericUtils";
import { logger } from "../utils/logger";

export async function fetchProductVariants(pvarIds: string[]) {
const CHUNK_SIZE = 100;
const allProductVariants: any[] = [];
const params: any = { limit: CHUNK_SIZE, offset: 0, tenantId: defaultRequestInfo?.RequestInfo?.userInfo?.tenantId };

for (let i = 0; i < pvarIds.length; i += CHUNK_SIZE) {
const chunk = pvarIds.slice(i, i + CHUNK_SIZE);
try {
const response = await httpRequest(
config.host.productHost + config.paths.productVariantSearch,
{ ProductVariant: { id: chunk }, ...defaultRequestInfo },
params
);
allProductVariants.push(...response?.ProductVariant || []);
} catch (error: any) {
logger.error("Error during product variant fetch");
throwError("COMMON", 500, "INTERNAL_SERVER_ERROR", `Some error occurred while fetching product variants. ${error?.message}`);
}
}

return allProductVariants; // Return the fetched product variants
}
21 changes: 19 additions & 2 deletions health-services/project-factory/src/server/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import { requestMiddleware } from './utils/middlewares';
import { errorLogger, errorResponder, invalidPathHandler } from './utils/genericUtils';
import { tracingMiddleware } from './tracing';
import { createProxyMiddleware } from 'http-proxy-middleware';
import * as v8 from 'v8';

const printMemoryInMB=(memoryInBytes:number)=> {
const memoryInMB = memoryInBytes / (1024 * 1024); // Convert bytes to MB
return `${memoryInMB.toFixed(2)} MB`;
}

class App {
public app: express.Application;
Expand All @@ -17,11 +23,21 @@ class App {
this.initializeMiddlewares();
this.initializeControllers(controllers);
this.app.use(invalidPathHandler);

// Global error handling for uncaught exceptions
process.on('uncaughtException', (err) => {
console.error('Unhandled Exception:', err);
});

// Global error handling for unhandled promise rejections
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});
}

private initializeMiddlewares() {
this.app.use(bodyParser.json({ limit: '2mb' }));
this.app.use(bodyParser.urlencoded({ limit: '2mb', extended: true }));
this.app.use(bodyParser.json({ limit: config.app.incomingRequestPayloadLimit }));
this.app.use(bodyParser.urlencoded({ limit: config.app.incomingRequestPayloadLimit, extended: true }));
this.app.use(bodyParser.json());
this.app.use(tracingMiddleware);
this.app.use(requestMiddleware);
Expand All @@ -45,6 +61,7 @@ class App {
public listen() {
this.app.listen(this.port, () => {
console.log(`App listening on the port ${this.port}`);
console.log("Current App's Heap Configuration Total Available :", printMemoryInMB(v8.getHeapStatistics()?.total_available_size) ," max limit set to : ",printMemoryInMB(v8.getHeapStatistics()?.heap_size_limit) );
});
}
}
Expand Down
4 changes: 3 additions & 1 deletion health-services/project-factory/src/server/config/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ const config = {
contextPath: process.env.CONTEXT_PATH || "/project-factory",
logLevel: process.env.APP_LOG_LEVEL || "debug",
debugLogCharLimit: process.env.APP_MAX_DEBUG_CHAR ? Number(process.env.APP_MAX_DEBUG_CHAR) : 1000,
defaultTenantId: process.env.DEFAULT_TENANT_ID || "mz"
defaultTenantId: process.env.DEFAULT_TENANT_ID || "mz",
// incomingRequestPayloadLimit : process.env.REQUEST_PAYLOAD_LIMIT || "2mb" @ashish add this key and config helm chart values
incomingRequestPayloadLimit: "2mb"
},
localisation: {
defaultLocale: process.env.LOCALE || "en_MZ",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ async function createBoundaryWithProjectMapping(projects: any, boundaryWithProje
}
}

function getPvarIds(messageObject: any) {
export function getPvarIds(messageObject: any) {
//update to set now
logger.info("campaign product resource mapping started");
const deliveryRules = messageObject?.CampaignDetails?.deliveryRules;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1041,7 +1041,7 @@ async function generateUserAndBoundarySheet(request: any, localizationMap?: { [k
const tenantId = request?.query?.tenantId;
const isSourceMicroplan = await isMicroplanRequest(request);
if (isSourceMicroplan) {
const rolesForMicroplan = await getRolesForMicroplan(tenantId);
const rolesForMicroplan = await getRolesForMicroplan(tenantId, localizationMap);
await generateUserSheetForMicroPlan(request, rolesForMicroplan, userData, localizationMap, fileUrl);
}
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -495,7 +495,20 @@ function findAndChangeFacilityData(
for (const [facilityCode, facilityDetails] of Object.entries(mappingData)) {
if (!mappedData[facilityCode]) {
missingFacilities.push(facilityCode);
const newRow = worksheet.addRow([]);

// Find the first empty row in the sheet
let emptyRowIndex = worksheet.rowCount + 1; // Default to the next available row
for (let i = 1; i <= worksheet.rowCount; i++) {
const row = worksheet.getRow(i);
if (!row.getCell(1).value) { // Assuming column 1 is used to determine emptiness
emptyRowIndex = i;
break;
}
}

const newRow = worksheet.getRow(emptyRowIndex);

// Assign values to the identified empty row
newRow.getCell(facilityCodeIndex).value = facilityCode;
newRow.getCell(headerValues.indexOf(headersMap["Facility Name"])).value =
facilityDetails?.additionalDetails?.facilityName;
Expand All @@ -509,8 +522,11 @@ function findAndChangeFacilityData(
newRow.getCell(boundaryCodeIndex).value =
facilityDetails.serviceBoundaries.join(",") || "";
newRow.getCell(facilityUsageIndex).value = "Active";

newRow.commit(); // Save the changes to the row
}
}

logger.info(
`Updated the boundary & active/inactive status information in facility received from the microplan`
);
Expand All @@ -529,7 +545,7 @@ function getHeaderIndex(
localizationMap: any
) {
return headers.indexOf(
getLocalizedName(config?.boundary?.boundaryCode, localizationMap)
getLocalizedName(headerName, localizationMap)
);
}

Expand All @@ -544,23 +560,18 @@ function findAndChangeTargetData(
Object.keys(mappingData)?.length
}`
);

if (headers == null || headers.length == 0) {
throwError("Error", 500, "Mapping not found in MDMS for Campaign");
}
logger.info(
`Received for Target mapping, headers count : ${
headers?.length
}`
);
logger.info(
`Received for Target mapping, headers: ${
headers?.length
}`
);
logger.debug(
`headers: ${getFormattedStringForDebug(headers)}`
);

if (headers == null || headers.length == 0) {
throwError("Error", 500, "Mapping not found in MDMS for Campaign");
}
let headersInSheet = worksheet.getRow(1).values;
const mappedData: any = {};
// Iterate through rows in Sheet1 (starting from row 2 to skip the header)
Expand All @@ -578,17 +589,19 @@ function findAndChangeTargetData(
);
if (mappingData?.[column1Value] && headers != null && headers.length > 0) {
// Update columns 5 and 6 if column 1 value matches
headers[0]?.from.forEach((fromValue: any) => {
row.getCell(
getHeaderIndex(headersInSheet, fromValue, localizationMap)
).value =
mappingData?.[column1Value]?.additionalDetails?.[
getLocalizedName(headers[0]?.to, localizationMap)
];
logger.debug(
`headers to: ${getFormattedStringForDebug(getLocalizedName(headers[0]?.to, localizationMap))}`
);
});
headers.forEach((header: any) => {
header.from.forEach((fromValue: any) => {
row.getCell(
getHeaderIndex(headersInSheet, header?.to, localizationMap)
).value =
mappingData?.[column1Value]?.additionalDetails?.[
fromValue
];
logger.debug(
`headers to: ${getFormattedStringForDebug(getLocalizedName(header?.to, localizationMap))}`
);
});
})
mappedData[column1Value] = rowIndex;
} else {
logger.info(`not doing anything if taregt cel not found`);
Expand Down Expand Up @@ -943,24 +956,27 @@ export async function fetchUserRoleMappingFromMDMS(tenantId: any) {
};
const data = await searchMDMSDataViaV1Api(MdmsCriteria);
const result: Record<string, any[]> = {};

if (
data?.MdmsRes?.["HCM-ADMIN-CONSOLE"]?.microplanIntegration &&
Array.isArray(data.MdmsRes["HCM-ADMIN-CONSOLE"].microplanIntegration)
) {
const integrations = data.MdmsRes["HCM-ADMIN-CONSOLE"].microplanIntegration;

const integrations =
data.MdmsRes["HCM-ADMIN-CONSOLE"].microplanIntegration;

integrations.forEach((integration: any) => {
const type = integration.type;

if (!result[type]) {
result[type] = [];
}

result[type].push({
to: integration.to,
from: integration.from,
type: integration.type,
filter: integration.filter,

integration.mappings.forEach((mapping: any) => {
result[type].push({
to: mapping.to,
from: mapping.from,
filter: mapping.filter,
});
});
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export async function getUserDataFromMicroplanSheet(request: any, fileStoreId: a
if (!fileResponse?.fileStoreIds?.[0]?.url) {
throwError("FILE", 500, "DOWNLOAD_URL_NOT_FOUND");
}
const rolesForMicroplanWithCode = await getRolesForMicroplan(tenantId, true);
const rolesForMicroplanWithCode = await getRolesForMicroplan(tenantId, localizationMap, true);
const rolesCodeMapping = rolesForMicroplanWithCode.reduce((acc: any, role: any) => {
acc[role.role] = role.code;
return acc;
Expand Down Expand Up @@ -436,7 +436,7 @@ export function modifyBoundaryIfSourceMicroplan(boundaryData: any[], request: an
return boundaryData;
}

export async function getRolesForMicroplan(tenantId: string, fetchWithRoleCodes: boolean = false) {
export async function getRolesForMicroplan(tenantId: string, localizationMap: any, fetchWithRoleCodes: boolean = false) {
const MdmsCriteria: any = {
tenantId: tenantId,
schemaCode: "hcm-microplanning.rolesForMicroplan",
Expand All @@ -446,12 +446,12 @@ export async function getRolesForMicroplan(tenantId: string, fetchWithRoleCodes:
if (fetchWithRoleCodes) {
// return array { role : "role", code : "roleCode" }
return mdmsResponse?.mdms?.filter((role: any) => role?.isActive)
?.map((role: any) => ({ role: role?.data?.role, code: role?.data?.roleCode }));
?.map((role: any) => ({ role: getLocalizedName(role?.data?.roleCode, localizationMap), code: role?.data?.roleCode }));
}
else {
return mdmsResponse?.mdms
?.filter((role: any) => role?.isActive) // Filter roles with isActive true
?.map((role: any) => role?.data?.role); // Map to extract the role
?.map((role: any) => getLocalizedName(role?.data?.roleCode, localizationMap)); // Map to extract the role
}
}
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import { getBoundariesFromCampaignSearchResponse, validateBoundariesIfParentPres
import { validateFacilityBoundaryForLowestLevel, validateLatLongForMicroplanCampaigns, validatePhoneNumberSheetWise, validateTargetsForMicroplanCampaigns, validateUniqueSheetWise, validateUserForMicroplan } from "./microplanValidators";
import { produceModifiedMessages } from "../kafka/Producer";
import { planConfigSearch, planFacilitySearch } from "../utils/microplanUtils";
import { getPvarIds } from "../utils/campaignMappingUtils";
import { fetchProductVariants } from "../api/healthApis";



Expand Down Expand Up @@ -1037,6 +1039,7 @@ async function validateCampaignBody(request: any, CampaignDetails: any, actionIn
await validateProjectType(request, projectType, tenantId);
await validateProjectCampaignBoundaries(request?.body?.boundariesCombined, hierarchyType, tenantId, request);
await validateProjectCampaignResources(resources, request);
await validateProductVariant(request);
}
else {
validateDraftProjectCampaignMissingFields(CampaignDetails);
Expand Down Expand Up @@ -1154,12 +1157,52 @@ async function validateForRetry(request: any) {
}
}

async function validateProductVariant(request: any) {
const deliveryRules = request?.body?.CampaignDetails?.deliveryRules;

if (!Array.isArray(deliveryRules)) {
throwError("COMMON", 400, "VALIDATION_ERROR", "deliveryRules must be an array");
}

deliveryRules.forEach((rule: any, index: number) => {
const productVariants = rule?.resources;
if (!Array.isArray(productVariants) || productVariants.length === 0) {
throwError("COMMON", 400, "VALIDATION_ERROR", `deliveryRules[${index}].resources must be a non-empty array`);
}
});
const pvarIds= getPvarIds(request?.body);
await validatePvarIds(pvarIds as string[]);
logger.info("Validated product variants successfully");
}

async function validatePvarIds(pvarIds: string[]) {
// Validate that pvarIds is not null, undefined, or empty, and that no element is null or undefined
if (!pvarIds?.length || pvarIds.some((id:any) => !id)) {
throwError("COMMON", 400, "VALIDATION_ERROR", "productVariantId is required in every delivery rule's resources");
}

// Fetch product variants using the fetchProductVariants function
const allProductVariants = await fetchProductVariants(pvarIds);

// Extract the ids of the fetched product variants
const fetchedIds = new Set(allProductVariants.map((pvar: any) => pvar?.id));

// Identify missing or invalid product variants
const missingPvarIds = pvarIds.filter((id: any) => !fetchedIds.has(id));

if (missingPvarIds.length) {
throwError("COMMON", 400, "VALIDATION_ERROR", `Invalid product variant ${missingPvarIds.length === 1 ? 'id' : 'ids'}: ${missingPvarIds.join(", ")}`);
}
}


async function validateIsActive(request: any) {
if (!request?.body?.CampaignDetails.isActive) {
throwError("COMMON", 400, "VALIDATION_ERROR", "Can't update isActive")
}
}


async function validateSearchProjectCampaignRequest(request: any) {
const CampaignDetails = request.body.CampaignDetails;
if (!CampaignDetails) {
Expand Down
Loading

0 comments on commit 4458113

Please sign in to comment.