Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
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/src/routes/migration.routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ router.post(
);

router.get(
"/get_migration_logs/:orgId/:projectId/:stackId",
"/get_migration_logs/:orgId/:projectId/:stackId/:skip/:limit/:startIndex/:stopIndex/:searchText/:filter",
asyncRouter(migrationController.getLogs)

)
Expand Down
74 changes: 58 additions & 16 deletions api/src/services/migration.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -634,36 +634,78 @@ const startMigration = async (req: Request): Promise<any> => {
};

const getLogs = async (req: Request): Promise<any> => {
const projectId = path.basename(req?.params?.projectId);
const stackId = path.basename(req?.params?.stackId);
const srcFunc = 'getLogs';

if (projectId.includes('..') || stackId.includes('..')) {
throw new BadRequestError('Invalid projectId or stackId');
const projectId = req?.params?.projectId ? path.basename(req.params.projectId) : '';
const stackId = req?.params?.stackId ? path.basename(req.params.stackId) : '';
const limit = req?.params?.limit ? parseInt(req.params.limit) : 10;
const startIndex = req?.params?.startIndex ? parseInt(req.params.startIndex) : 0;
const stopIndex = startIndex + limit;
const searchText = req?.params?.searchText ?? null;
const filter = req?.params?.filter ?? 'all';

const srcFunc = "getLogs";

if (!projectId || !stackId || projectId.includes("..") || stackId.includes("..")) {
throw new BadRequestError("Invalid projectId or stackId");
}

try {
const logsDir = path.join(process.cwd(), 'logs');
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.

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

if (!mainPath) {
throw new BadRequestError("Invalid application path");
}

const logsDir = path.join(mainPath, "migration-v2", "api", "logs");
const loggerPath = path.join(logsDir, projectId, `${stackId}.log`);
const absolutePath = path.resolve(loggerPath); // Resolve the absolute path
const absolutePath = path.resolve(loggerPath);

if (!absolutePath.startsWith(logsDir)) {
throw new BadRequestError('Access to this file is not allowed.');
throw new BadRequestError("Access to this file is not allowed.");
}

if (fs.existsSync(absolutePath)) {
const logs = await fs.promises.readFile(absolutePath, 'utf8');
const logEntries = logs
.split('\n')
.map((line) => {
const logs = await fs.promises.readFile(absolutePath, "utf8");
let logEntries = logs
?.split("\n")
?.map((line) => {
try {
return JSON.parse(line);
return line ? JSON.parse(line) : null;
} catch (error) {
return null;
}
})
.filter((entry) => entry !== null);
return logEntries;
?.filter((entry) => entry !== null);
Copy link
Contributor

Choose a reason for hiding this comment

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

filter?.(


if (!logEntries?.length) {
return { logs: [], total: 0 };
}

logEntries = logEntries?.slice(1, logEntries.length - 2);
Copy link
Contributor

Choose a reason for hiding this comment

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

?.(


if (filter !== "all") {
const filters = filter?.split("-") ?? [];
logEntries = logEntries?.filter((log) => {
return filters?.some((filter) => {
return log?.level?.toLowerCase()?.includes(filter?.toLowerCase() ?? '');
});
});
}

if (searchText !== "null") {
logEntries = logEntries?.filter((log) => {
return (
log?.level?.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.

lets create genric func for this

log?.message?.toLowerCase()?.includes(searchText?.toLowerCase() ?? '') ||
log?.methodName?.toLowerCase()?.includes(searchText?.toLowerCase() ?? '') ||
log?.timestamp?.toLowerCase()?.includes(searchText?.toLowerCase() ?? '')
);
});
}

const paginatedLogs = logEntries?.slice(startIndex, stopIndex) ?? [];
return {
logs: paginatedLogs,
total: logEntries?.length ?? 0,
};
} else {
logger.error(getLogMessage(srcFunc, HTTP_TEXTS.LOGS_NOT_FOUND));
throw new BadRequestError(HTTP_TEXTS.LOGS_NOT_FOUND);
Expand Down
5 changes: 5 additions & 0 deletions ui/src/components/Common/Settings/Settings.scss
Original file line number Diff line number Diff line change
Expand Up @@ -103,3 +103,8 @@
color: #6c5ce7 !important;
font-weight: 600;
}

.back-button{
cursor: pointer;
margin-bottom: 20px ;
}
111 changes: 74 additions & 37 deletions ui/src/components/Common/Settings/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
// Libraries
import React, { useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { Params, useNavigate, useParams } from 'react-router';
Expand All @@ -12,7 +11,8 @@ import {
Textarea,
PageLayout,
Notification,
cbModal
cbModal,
ClipBoard
} from '@contentstack/venus-components';

// Redux
Expand All @@ -35,6 +35,7 @@ import './Settings.scss';
import { useDispatch } from 'react-redux';
import { updateNewMigrationData } from '../../../store/slice/migrationDataSlice';
import { DEFAULT_NEW_MIGRATION } from '../../../context/app/app.interface';
import ExecutionLog from '../../../components/ExecutionLogs';

/**
* Renders the Settings component.
Expand All @@ -47,14 +48,19 @@ const Settings = () => {
const [active, setActive] = useState<string>();
const [currentHeader, setCurrentHeader] = useState<string>();
const [projectName, setProjectName] = useState('');
const [projectId, setProjectId] = useState('');
const [projectDescription, setProjectDescription] = useState('');

const selectedOrganisation = useSelector(
(state: RootState) => state?.authentication?.selectedOrganisation
);

const currentStep = useSelector(
(state: RootState) => state?.migration?.newMigrationData?.project_current_step
);

const navigate = useNavigate();
const dispatch = useDispatch()
const dispatch = useDispatch();

useEffect(() => {
const fetchData = async () => {
Expand All @@ -78,6 +84,7 @@ const Settings = () => {
if (status === 200) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Let’s create constant for 200

setProjectName(data?.name);
setProjectDescription(data?.description);
setProjectId(params?.projectId ?? '');
}
};

Expand Down Expand Up @@ -124,29 +131,34 @@ const Settings = () => {
});
}
};
const handleDeleteProject = async (closeModal: ()=> void): Promise<void> => {
//setIsLoading(true);
const response = await deleteProject(selectedOrganisation?.value, params?.projectId ?? '');

if (response?.status === 200) {
//setIsLoading(false);
closeModal();
dispatch(updateNewMigrationData(DEFAULT_NEW_MIGRATION));
setTimeout(() => {
navigate('/projects');
}, 800);
setTimeout(() => {
Notification({
notificationContent: { text: response?.data?.data?.message },
notificationProps: {
position: 'bottom-center',
hideProgressBar: true
},
type: 'success'
});
}, 1200);
}
};

const handleDeleteProject = async (closeModal: () => void): Promise<void> => {
//setIsLoading(true);
const response = await deleteProject(selectedOrganisation?.value, params?.projectId ?? '');

if (response?.status === 200) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Let’s create constant for 200

//setIsLoading(false);
closeModal();
dispatch(updateNewMigrationData(DEFAULT_NEW_MIGRATION));
setTimeout(() => {
navigate('/projects');
}, 800);
setTimeout(() => {
Notification({
notificationContent: { text: response?.data?.data?.message },
notificationProps: {
position: 'bottom-center',
hideProgressBar: true
},
type: 'success'
});
}, 1200);
}
};

const handleBack = () => {
navigate(`/projects/${params?.projectId}/migration/steps/${currentStep}`);
}

const handleClick = () => {
cbModal({
Expand Down Expand Up @@ -178,15 +190,13 @@ const Settings = () => {
class="Button Button--secondary Button--size-large Button--icon-alignment-left Button--v2"
aria-label="Delete Project for deleting project"
type="button"
onClick={handleClick}
>
onClick={handleClick}>
<div className="flex-center">
<div className="flex-v-center Button__mt-regular Button__visible">
<Icon
icon="Delete"
version="v2"
data={cmsData?.project?.delete_project?.title}
></Icon>
data={cmsData?.project?.delete_project?.title}></Icon>
</div>
</div>
</Button>
Expand Down Expand Up @@ -214,8 +224,7 @@ const Settings = () => {
aria-label="projectname"
version="v2"
value={projectName}
onChange={handleProjectNameChange}
></TextInput>
onChange={handleProjectNameChange}></TextInput>
</div>
</div>
</div>
Expand Down Expand Up @@ -244,16 +253,19 @@ const Settings = () => {
icon={'v2-Save'}
Copy link
Contributor

Choose a reason for hiding this comment

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

cmsData?.project?.save_project?.icon somthing like this for icon

autoClose={5000}
label={'Success'}
onClick={handleUpdateProject}
>
onClick={handleUpdateProject}>
{cmsData?.project?.save_project?.title}
</Button>
</div>
</form>
</div>
</div>
)}
{active === cmsData?.execution_logs?.title && <div></div>}
{active === cmsData?.execution_logs?.title && (
<div>
Copy link
Contributor

Choose a reason for hiding this comment

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

can we use fragment here <></>

<ExecutionLog projectId={projectId} />
</div>
)}
</div>
)
};
Expand All @@ -265,8 +277,21 @@ const Settings = () => {
data-testid="cs-section-header"
className="SectionHeader SectionHeader--extra-bold SectionHeader--medium SectionHeader--black SectionHeader--v2"
aria-label={cmsData?.title}
aria-level={1}
>
aria-level={1}>
<div>
<Icon
version="v2"
icon={'LeftArrow'}
Copy link
Contributor

Choose a reason for hiding this comment

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

same for this icon

size="medium"
onClick={() => {
handleBack();
}}
withTooltip={true}
tooltipContent={'Back'}
tooltipPosition="right"
className='back-button'
/>
</div>
{cmsData?.title}
</div>

Expand All @@ -281,6 +306,18 @@ const Settings = () => {
}}
version="v2"
/>

<ListRow
rightArrow={true}
active={active === cmsData?.execution_logs?.title}
content={cmsData?.execution_logs?.title}
Copy link
Contributor

Choose a reason for hiding this comment

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

can we also this v2 to at constant so we don’t need to change every time in each component
?

leftIcon={<Icon icon="ExecutionLog" version="v2" />}
onClick={() => {
setActive(cmsData?.execution_logs?.title);
setCurrentHeader(cmsData?.execution_logs?.title);
}}
version="v2"
/>
</div>
)
};
Expand Down
23 changes: 23 additions & 0 deletions ui/src/components/ExecutionLogs/executionlog.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
export type LogEntry = {
level: string;
message: string;
methodName: string;
timestamp: string;
};

export type StackIds = {
stackUid?: string;
stackName?: string;
isMigrated?: boolean;
};


export type DropdownOption = {
label: string ;
value: string;
};

export type FilterOption = {
label: string;
value: string;
};
49 changes: 49 additions & 0 deletions ui/src/components/ExecutionLogs/index.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.Search-input-show {
margin-bottom: 8px;
width: 300px;
}
.Search .Search__search-icon {
top: calc(50% - 10px);
width: 20px;
}

.Search .Search__input.regular-corners {
margin-left: 8px;
}

.dropdown-wrapper {
margin-bottom: 8px;
}

.PageLayout--primary .PageLayout__leftSidebar + .PageLayout__content .PageLayout__body {
width: calc(100% - 15.4rem);
}

.Table__head__column {
align-items: center;
display: flex;
justify-content: space-between;
}

.Table__head{
height: auto;
}

.Table:has(.custom-empty-state) {
height: 40.5rem;
}


.custom-empty-state {
.Icon--original {
width: 207px !important;
height: auto !important;
max-width: 100%;
display: block;
margin: 0 auto;
}
}

.select-container {
margin-bottom: 8px;
}
Loading