Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
735b460
feat: add audit data retrieval endpoint and integrate AuditLogs compo…
samarp-jain Apr 22, 2025
211f5ba
feat: enhance AuditLogs component with improved empty state handling …
samarp-jain Apr 22, 2025
b50694c
refactor: update AuditLogs component styles and improve empty state m…
samarp-jain Apr 23, 2025
1dcd90e
refactor: simplify AuditLogs component by removing unused data fetchi…
samarp-jain Apr 24, 2025
728020c
feat: add filter functionality to audit data retrieval and implement …
samarp-jain Apr 24, 2025
6381d51
feat: implement filter functionality in audit data retrieval and enha…
samarp-jain Apr 28, 2025
3c18feb
feat: enhance audit data retrieval with dynamic filtering and improve…
samarp-jain Apr 30, 2025
89768bb
refactor: streamline AuditLogs component by renaming filter state and…
samarp-jain May 19, 2025
63018bf
Merge branch 'dev' of https://github.com/contentstack/migration-v2 in…
yashin4112 May 20, 2025
4a873cc
feat: introduce auditLogsConstants for improved readability and maint…
samarp-jain May 20, 2025
f126fe4
Merge branch 'feature/audit-log' of https://github.com/contentstack/m…
samarp-jain May 21, 2025
71cba12
style: remove unnecessary margin from select-box in AuditLogs component
samarp-jain May 21, 2025
9afb8fe
Merge branch 'stage' of https://github.com/contentstack/migration-v2 …
samarp-jain May 21, 2025
31b982a
chore: update shelljs dependency to version 0.9.0
samarp-jain May 21, 2025
3e00106
fix: resolve audit log path using path.resolve for improved reliability
samarp-jain May 21, 2025
bb3e96e
refactor: simplify audit data retrieval by directly resolving file pa…
samarp-jain May 21, 2025
efcc858
fix: use fs.promises to read audit log file asynchronously
samarp-jain May 21, 2025
5f8db6f
feat: enhance audit filter modal with new constants and improved styl…
samarp-jain May 23, 2025
db6b964
refactor: streamline modal element access and update filter options r…
samarp-jain May 23, 2025
c22a4d4
fix: update key generation for filter options to ensure unique keys i…
samarp-jain May 23, 2025
200834c
Merge branch 'dev' of https://github.com/contentstack/migration-v2 in…
samarp-jain May 23, 2025
9b176f5
fix: update key generation for filter options to use item label for u…
samarp-jain May 23, 2025
1de2e77
fix: update key generation in AuditFilterModal to use item value for …
samarp-jain May 23, 2025
7bbb2b7
fix: update rendering of filter options to use list items for better …
samarp-jain May 23, 2025
c27aa75
m
samarp-jain May 23, 2025
6553de1
fix: update AuditFilterModal to render filter options as a list for i…
samarp-jain May 23, 2025
33bb119
fix: refactor AuditFilterModal to use a unique key generation functio…
samarp-jain May 23, 2025
b39ac30
fix: update key generation in AuditFilterModal to use a more descript…
samarp-jain May 23, 2025
7027f55
fix: enhance key generation in AuditFilterModal for filter options to…
samarp-jain May 23, 2025
4e0b065
Revert "fix: enhance key generation in AuditFilterModal for filter op…
samarp-jain May 23, 2025
52d7d4a
Revert "Revert "fix: enhance key generation in AuditFilterModal for f…
samarp-jain May 23, 2025
c68dbcd
fix: update migration controller and service for consistent response …
samarp-jain May 26, 2025
e4caa35
updated @contentstack/venus-components": "^3.0.0"
umeshmore45 May 26, 2025
f6320a9
updated .talismanrc
umeshmore45 May 26, 2025
1e25054
fix: correct constant name and improve null safety checks in migratio…
samarp-jain May 27, 2025
7491e66
fix: improve null safety checks in migration service by removing opti…
samarp-jain May 27, 2025
e31be7a
refactor: update styling and structure for AuditLogs and ExecutionLog…
samarp-jain May 29, 2025
bfe94a2
fix: update uniqueKey property in ExecutionLogs component to an empty…
samarp-jain May 29, 2025
cc45917
fix: update uniqueKey property in ExecutionLogs component to 'id' for…
samarp-jain May 29, 2025
b0e886e
fix: enhance log processing in migration service by adding unique ide…
samarp-jain May 29, 2025
5321412
fix: correct constant name from GET_AUIDT_DATA to GET_AUDIT_DATA and …
samarp-jain May 29, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
"p-limit": "^6.2.0",
"path-to-regexp": "^8.2.0",
"router": "^2.0.0",
"shelljs": "^0.8.5",
"shelljs": "^0.9.0",
"socket.io": "^4.7.5",
"uuid": "^9.0.1",
"winston": "^3.11.0"
Expand Down
8 changes: 6 additions & 2 deletions api/src/controllers/migration.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,10 @@ const deleteTestStack = async (req: Request, res: Response): Promise<void> => {
const resp = await migrationService.deleteTestStack(req);
res.status(200).json(resp);
};

const getAuditData = async (req: Request, res: Response): Promise<void> => {
const resp = await migrationService.getAuditData(req);
res.status(200).json(resp);
};
const getLogs = async (req: Request, res: Response): Promise<void> => {
const resp = await migrationService.getLogs(req);
res.status(200).json(resp);
Expand All @@ -72,5 +75,6 @@ export const migrationController = {
startMigration,
getLogs,
saveLocales,
saveMappedLocales
saveMappedLocales,
getAuditData
};
5 changes: 4 additions & 1 deletion api/src/routes/migration.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ router.post(
"/test-stack/:orgId/:projectId",
asyncRouter(migrationController.startTestMigration)
);

router.get(
"/get_audit_data/:orgId/:projectId/:stackId/:moduleName/:skip/:limit/:startIndex/:stopIndex/:searchText/:filter",
asyncRouter(migrationController.getAuditData)
)
/**
* Route for deleting a test stack.
* @route POST /test-stack/:projectId
Expand Down
159 changes: 156 additions & 3 deletions api/src/services/migration.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,8 @@ const createTestStack = async (req: Request): Promise<LoginServiceType> => {
return {
data: {
data: res.data,
url: `${
config.CS_URL[token_payload?.region as keyof typeof config.CS_URL]
}/stack/${res.data.stack.api_key}/dashboard`,
url: `${config.CS_URL[token_payload?.region as keyof typeof config.CS_URL]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add null checks

}/stack/${res.data.stack.api_key}/dashboard`,
},
status: res.status,
};
Expand All @@ -142,6 +141,159 @@ const createTestStack = async (req: Request): Promise<LoginServiceType> => {
}
};

const getAuditData = async (req: Request): Promise<any> => {
const projectId = path.basename(req?.params?.projectId);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

path?.basename?.()

const stackId = path.basename(req?.params?.stackId);
const moduleName = path.basename(req?.params?.moduleName);
const limit = parseInt(req?.params?.limit);
const startIndex = parseInt(req?.params?.startIndex);
const stopIndex = startIndex + limit;
const searchText = req?.params?.searchText;
const filter = req?.params?.filter;
const srcFunc = "getAuditData";

if (projectId.includes('..') || stackId.includes('..') || moduleName.includes('..')) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add optional chaining => projectId?.includes. stackId?.includes, moduleName?.includes

throw new BadRequestError("Invalid projectId, stackId, or moduleName");
}

try {
const mainPath = process.cwd().split("migration-v2")[0];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

process?.cwd?.().split?.("migration-v2")?.[0];

const logsDir = path.join(mainPath, "migration-v2", "api", "migration-data");

const stackFolders = fs.readdirSync(logsDir);

const stackFolder = stackFolders.find(folder => folder.startsWith(stackId));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add optional chaining => tackFolders?.find, folder?.startsWith

if (!stackFolder) {
throw new BadRequestError("Migration data not found for this stack");
}

const auditLogPath = path.resolve(logsDir, stackFolder, "logs", "audit", "audit-report");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add directory in constant

if (!fs.existsSync(auditLogPath)) {
throw new BadRequestError("Audit log path not found");
}


// Read and parse the JSON file for the module
const filePath = path.resolve(auditLogPath, `${moduleName}.json`);
let fileData;
if (fs.existsSync(filePath)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

const fileContent = await fsPromises.readFile(filePath, 'utf8');
fileData = JSON.parse(fileContent);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check type then parse

}

// If no matching module was found
if (!fileData) {
throw new BadRequestError(`No audit data found for module: ${moduleName}`);
}
// Transform and flatten the data with sequential tuid
const filterKey = moduleName === 'Entries_Select_feild' ? 'display_type' : 'data_type';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add this into constant

console.info("🚀 ~ getAuditData ~ filterKey:", filterKey)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove this info


let transformedData = transformAndFlattenData(fileData);
// console.info(transformedData)
if (filter != "all") {
const filters = filter.split("-");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add optional chaining => filter?.split

moduleName === 'Entries_Select_feild' ? transformedData = transformedData.filter((log) => {
return filters.some((filter) => {
//eslint-disable-next-line
console.log("🚀 ~ getAuditData ~ filter:", log)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove log

return (
log?.display_type?.toLowerCase()?.includes(filter?.toLowerCase())
);
});
}) : transformedData = transformedData.filter((log) => {
return filters.some((filter) => {
return (
log?.data_type?.toLowerCase()?.includes(filter?.toLowerCase())
);
});
});

}
// Apply search filter if searchText is provided and not "null"
if (searchText && searchText !== "null") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if (searchText && searchText !== null && searchText !== "null") {

transformedData = transformedData.filter((item: any) => {
// Adjust these fields based on your actual audit data structure
return Object.values(item).some(value =>
value &&
typeof value === 'string' &&
value.toLowerCase().includes(searchText.toLowerCase())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value?.toLowerCase?.().includes(searchText.toLowerCase())

);
});
}

// Apply pagination
const paginatedData = transformedData.slice(startIndex, stopIndex);

return {
data: paginatedData,
totalCount: transformedData.length
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

transformedData?.length

};

} catch (error: any) {
logger.error(
getLogMessage(
srcFunc,
`Error getting audit log data for module: ${moduleName}`,
error
)
);
throw new ExceptionFunction(
error?.message || HTTP_TEXTS.INTERNAL_ERROR,
error?.statusCode || error?.status || HTTP_CODES.SERVER_ERROR
);
}
};
/**
* Transforms and flattens nested data structure into an array of items
* with sequential tuid values
*/
const transformAndFlattenData = (data: any): Array<{ [key: string]: any, id: number }> => {
try {
const flattenedItems: Array<{ [key: string]: any }> = [];

// Handle the data based on its structure
if (Array.isArray(data)) {
// If data is already an array, use it directly
data.forEach((item, index) => {
flattenedItems.push({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...item ?? {}

...item,
uid: item.uid || `item-${index}`
});
});
} else if (typeof data === 'object' && data !== null) {
// Process object data
Object.entries(data).forEach(([key, value]) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Object?.entries?.(data).forEach(([key, value])

if (Array.isArray(value)) {
// If property contains an array, flatten each item
value.forEach((item, index) => {
flattenedItems.push({
...item,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...item ?? {}

parentKey: key,
uid: item.uid || `${key}-${index}`
});
});
} else if (typeof value === 'object' && value !== null) {
// If property contains an object, add it as an item
flattenedItems.push({
...value,
key,
uid: (value as any).uid || key
});
}
});
}

// Add sequential tuid to each item
return flattenedItems.map((item, index) => ({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?

...item,
id: index + 1
}));
} catch (error) {
console.error('Error transforming data:', error);
return [];
}
};
/**
* Deletes a test stack.
* @param req - The request object.
Expand Down Expand Up @@ -971,4 +1123,5 @@ export const migrationService = {
getLogs,
createSourceLocales,
updateLocaleMapper,
getAuditData
};
11 changes: 11 additions & 0 deletions ui/src/components/AuditFilterModal/auditlog.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { FilterOption } from "../AuditLogs/auditLogs.interface";

export type FilterModaleProps = {
isOpen: boolean;
closeModal: () => void;
updateValue: (params: { value: FilterOption; isChecked: boolean }) => void;
onApply: () => void;
selectedLevels: FilterOption[];
setFilterValue: (levels: FilterOption[]) => void;
selectedFileType: string;
};
85 changes: 85 additions & 0 deletions ui/src/components/AuditFilterModal/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
@import '../../scss/variables';

.tableFilterModalStories {
position: absolute;
z-index: 1000;
width: 350px;
background-color: #ffffff;
border-radius: 12px;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
display: flex;
flex-direction: column;
max-height: 350px;
overflow: hidden;
font-family: 'Inter', sans-serif;
}

.tableFilterModalStories__header {
padding: 16px 16px;
color: #3d3f4c;
border-bottom: 1px solid #e5e7eb;
display: flex;
align-items: center;
justify-content: space-between;
}

.tableFilterModalStories__suggestion-item {
padding: 8px 16px;
}

.Checkbox {
display: flex;
align-items: center;
cursor: pointer;
}

.Checkbox .Checkbox__tick svg {
display: block !important;
}

.Checkbox .Checkbox__label {
font-size: $size-font-medium;
line-height: $line-height-reset;
color: #3d3f4c;
text-transform: capitalize;
}

.tableFilterModalStories__footer {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 16px;
padding: 16px 8px;
border-top: 1px solid #e5e7eb;
background: #fff;
font-size: $size-font-medium;
line-height: $line-height-reset;
}

.close-btn {
display: inline-flex;
align-items: center;
justify-content: center;
width: 32px;
height: 32px;
}

.close-btn:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
transform: scale(1.05);
border-radius: 8px;
background-color: #f0f1f3;
cursor: pointer;
transition:
box-shadow 0.2s ease,
transform 0.2s ease;
}

.close-btn:active {
transform: scale(0.95);
}

.text-size {
font-size: $size-font-medium;
line-height: $line-height-reset;
}
Loading