Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve/error handling #140

Closed
wants to merge 14 commits into from
Closed

Improve/error handling #140

wants to merge 14 commits into from

Conversation

abdheshnayak
Copy link
Member

No description provided.

@abdheshnayak abdheshnayak deleted the improve/error-handling branch March 13, 2024 12:03
Copy link

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

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

Hey @abdheshnayak - I've reviewed your changes and they look great!

General suggestions:

  • Consider breaking down large components into smaller, more focused ones for improved readability and maintainability.
  • Ensure consistent handling of base64 encoded parameters across client-side and server-side code.
  • Review the use of 'imagePullPolicy' to ensure it aligns with the intended behavior in different environments.
  • Ensure that the new logging and error handling enhancements are well-documented for future maintainers.
Here's what I looked at during the review
  • 🟡 General issues: 6 issues found
  • 🟡 Security: 1 issue found
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 1 issue found
  • 🟢 Docstrings: all looks good

Thanks for using Sourcery. We offer it for free for open source projects and would be very grateful if you could help us grow. If you like it, would you consider sharing Sourcery on your favourite social media? ✨

Share Sourcery

Help me be more useful! Please click 👍 or 👎 on each comment to tell me if it was helpful.

Comment on lines +161 to +254
const ListDetail = (
props: Omit<IResource, 'items'> & {
open: string;
item: BaseType;
setOpen: ISetState<string>;
}
) => {
const { item, open, setOpen } = props;

const { name, id } = parseItem(item);
const keyPrefix = `${RESOURCE_NAME}-${id}`;
const lR = listRender({ keyPrefix, resource: item });

const { account } = useOutletContext<IAccountContext>();

const isLatest = dayjs(item.updateTime).isAfter(dayjs().subtract(3, 'hour'));

const tempStatus = listStatus({
key: keyPrefix,
item,
className: 'basis-full text-center',
});

const { state } = useDataState<{
linesVisible: boolean;
timestampVisible: boolean;
}>('logs');

return (
<div className="w-full flex flex-col">
<div className="flex flex-row items-center">
<div className="w-[220px] min-w-[220px] mr-xl flex flex-row items-center">
<ListTitle
title={name}
subtitle={
<div className="flex flex-row items-center gap-md">{id}</div>
}
avatar={<ConsoleAvatar name={id} />}
/>
</div>

{isLatest && (
<Button
size="sm"
variant="basic"
content={open === item.id ? 'Hide Logs' : 'Show Logs'}
onClick={(e) => {
e.preventDefault();

setOpen((s) => {
if (s === item.id) {
return '';
}
return item.id;
});
}}
/>
)}

<div className="flex items-center w-[20px] mx-xl flex-grow">
{tempStatus.render()}
</div>

<div className="pr-3xl w-[180px] min-w-[180px]">
{lR.authorRender({ className: '' }).render()}
</div>
</div>

<AnimateHide
onClick={(e) => e.preventDefault()}
show={open === item.id}
className="w-full flex pt-4xl justify-center items-center"
>
<LogComp
{...{
hideLineNumber: !state.linesVisible,
hideTimestamp: !state.timestampVisible,
className: 'flex-1',
dark: true,
width: '100%',
height: '40rem',
title: 'Logs',
websocket: {
account: parseName(account),
cluster: parseName(item),
trackingId: item.id,
},
actionComponent: <LogAction />,
}}
/>
</AnimateHide>
</div>
);
};
Copy link

Choose a reason for hiding this comment

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

suggestion (code_refinement): The ListDetail component seems to be quite large and handles multiple responsibilities, such as rendering UI elements, managing state, and handling logic for showing logs. Consider breaking it down into smaller, more focused components. This can improve readability and maintainability, making it easier to understand and modify individual parts of the component.

Suggested change
const ListDetail = (
props: Omit<IResource, 'items'> & {
open: string;
item: BaseType;
setOpen: ISetState<string>;
}
) => {
const { item, open, setOpen } = props;
const { name, id } = parseItem(item);
const keyPrefix = `${RESOURCE_NAME}-${id}`;
const lR = listRender({ keyPrefix, resource: item });
const { account } = useOutletContext<IAccountContext>();
const isLatest = dayjs(item.updateTime).isAfter(dayjs().subtract(3, 'hour'));
const tempStatus = listStatus({
key: keyPrefix,
item,
className: 'basis-full text-center',
});
const { state } = useDataState<{
linesVisible: boolean;
timestampVisible: boolean;
}>('logs');
return (
<div className="w-full flex flex-col">
<div className="flex flex-row items-center">
<div className="w-[220px] min-w-[220px] mr-xl flex flex-row items-center">
<ListTitle
title={name}
subtitle={
<div className="flex flex-row items-center gap-md">{id}</div>
}
avatar={<ConsoleAvatar name={id} />}
/>
</div>
{isLatest && (
<Button
size="sm"
variant="basic"
content={open === item.id ? 'Hide Logs' : 'Show Logs'}
onClick={(e) => {
e.preventDefault();
setOpen((s) => {
if (s === item.id) {
return '';
}
return item.id;
});
}}
/>
)}
<div className="flex items-center w-[20px] mx-xl flex-grow">
{tempStatus.render()}
</div>
<div className="pr-3xl w-[180px] min-w-[180px]">
{lR.authorRender({ className: '' }).render()}
</div>
</div>
<AnimateHide
onClick={(e) => e.preventDefault()}
show={open === item.id}
className="w-full flex pt-4xl justify-center items-center"
>
<LogComp
{...{
hideLineNumber: !state.linesVisible,
hideTimestamp: !state.timestampVisible,
className: 'flex-1',
dark: true,
width: '100%',
height: '40rem',
title: 'Logs',
websocket: {
account: parseName(account),
cluster: parseName(item),
trackingId: item.id,
},
actionComponent: <LogAction />,
}}
/>
</AnimateHide>
</div>
);
};
const ListDetailHeader = ({
name,
id,
isLatest,
open,
setOpen,
tempStatus,
lR,
}: {
name: string;
id: string;
isLatest: boolean;
open: string;
setOpen: ISetState<string>;
tempStatus: ReturnType<typeof listStatus>;
lR: ReturnType<typeof listRender>;
}) => (
<div className="flex flex-row items-center">
<div className="w-[220px] min-w-[220px] mr-xl flex flex-row items-center">
<ListTitle
title={name}
subtitle={<div className="flex flex-row items-center gap-md">{id}</div>}
avatar={<ConsoleAvatar name={id} />}
/>
</div>
{isLatest && (
<Button
size="sm"
variant="basic"
content={open === id ? 'Hide Logs' : 'Show Logs'}
onClick={(e) => {
e.preventDefault();
setOpen((s) => (s === id ? '' : id));
}}
/>
)}
<div className="flex items-center w-[20px] mx-xl flex-grow">
{tempStatus.render()}
</div>
<div className="pr-3xl w-[180px] min-w-[180px]">
{lR.authorRender({ className: '' }).render()}
</div>
</div>
);
const ListDetailLogs = ({
open,
item,
state,
account,
}: {
open: string;
item: BaseType;
state: {
linesVisible: boolean;
timestampVisible: boolean;
};
account: IAccountContext['account'];
}) => (
<AnimateHide
onClick={(e) => e.preventDefault()}
show={open === item.id}
className="w-full flex pt-4xl justify-center items-center"
>
<LogComp
{...{
hideLineNumber: !state.linesVisible,
hideTimestamp: !state.timestampVisible,
className: 'flex-1',
dark: true,
width: '100%',
height: '40rem',
title: 'Logs',
websocket: {
account: parseName(account),
cluster: parseName(item),
trackingId: item.id,
},
actionComponent: <LogAction />,
}}
/>
</AnimateHide>
);
const ListDetail = (props: Omit<IResource, 'items'> & {
open: string;
item: BaseType;
setOpen: ISetState<string>;
}) => {
const { item, open, setOpen } = props;
const { name, id } = parseItem(item);
const keyPrefix = `${RESOURCE_NAME}-${id}`;
const lR = listRender({ keyPrefix, resource: item });
const { account } = useOutletContext<IAccountContext>();
const isLatest = dayjs(item.updateTime).isAfter(dayjs().subtract(3, 'hour'));
const tempStatus = listStatus({
key: keyPrefix,
item,
className: 'basis-full text-center',
});
const { state } = useDataState<{
linesVisible: boolean;
timestampVisible: boolean;
}>('logs');
return (
<div className="w-full flex flex-col">
<ListDetailHeader
name={name}
id={id}
isLatest={isLatest}
open={open}
setOpen={setOpen}
tempStatus={tempStatus}
lR={lR}
/>
<ListDetailLogs open={open} item={item} state={state} account={account} />
</div>
);
};

Comment on lines +104 to +106
const { repo } = ctx.params;

const repoName = atob(repo || '');
Copy link

Choose a reason for hiding this comment

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

suggestion (code_clarification): The use of atob for decoding the repo parameter directly in the loader function implies that the repository name is base64 encoded. This approach is fine for obfuscation or encoding special characters in URLs, but ensure that all client-side and server-side code consistently handles these encoded parameters. Additionally, consider documenting this encoding strategy for future maintainers.

Suggested change
const { repo } = ctx.params;
const repoName = atob(repo || '');
// Extract the 'repo' parameter from the context
const { repo } = ctx.params;
// Decode the base64 'repo' parameter to get the repository name.
// Ensure to handle cases where 'repo' might be undefined or an empty string.
const repoName = repo ? atob(repo) : '';

Comment on lines +73 to +74
toast.error('Repository is required!.');
return;
Copy link

Choose a reason for hiding this comment

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

nitpick (typo): The error message ends with an exclamation mark followed by a period ('!.'). This might be a typo. Consider revising it to end with just an exclamation mark ('!') for proper punctuation.

Suggested change
toast.error('Repository is required!.');
return;
- toast.error('Repository is required!.');
+ toast.error('Repository is required!');

@@ -16,7 +16,7 @@ export const loader = async (ctx: IRemixCtx) => {

const promise = pWrapper(async () => {
const { data, errors } = await GQLServerHandler(ctx.request).listBuildRuns({
repoName: repo,
Copy link

Choose a reason for hiding this comment

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

🚨 issue (security): Using atob for decoding repo directly in the GQLServerHandler's listBuildRuns call might expose the application to security risks if repo is user-controlled. Consider validating or sanitizing the input before decoding.

Suggested change
repoName: repo,
repoName: (() => {
// Ensure repo is a base64 encoded string to mitigate security risks
if (/^[A-Za-z0-9+/]+={0,2}$/.test(repo)) {
return atob(repo);
} else {
// Handle invalid input appropriately
console.error("Invalid repo format");
return ""; // Or handle this case as per your application's error handling policy
}
})(),

@@ -131,6 +131,7 @@ const SettingCompute = () => {
? `${values.repoName}:${values.repoImageTag}`
: `${registryHost}/${values.repoAccountName}/${values.repoName}:${values.repoImageTag}`,
name: 'container-0',
imagePullPolicy: 'Always',
Copy link

Choose a reason for hiding this comment

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

suggestion (code_refinement): Setting imagePullPolicy to 'Always' ensures that the latest version of the image is always used, which is beneficial for development but might lead to unexpected updates in production. Consider making this configurable or documenting the behavior clearly.

Suggested change
imagePullPolicy: 'Always',
imagePullPolicy: process.env.NODE_ENV === 'production' ? 'IfNotPresent' : 'Always',

@@ -35,6 +35,8 @@ import { dayjs } from '~/components/molecule/dayjs';
import LogComp from '~/root/lib/client/components/logger';
import { useWatchReload } from '~/lib/client/helpers/socket/useWatch';
import { IClusterContext } from '~/console/routes/_main+/$account+/infra+/$cluster+/_layout';
import LogAction from '~/console/page-components/log-action';
import { useDataState } from '~/console/page-components/common-state';
Copy link

Choose a reason for hiding this comment

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

issue (complexity): The recent changes introduce additional complexity to the LogComp component by adding global state management for features that seem to be specific to the component's instances (visibility of lines and timestamps). This not only increases the component's dependency on the global state but also makes its behavior less predictable due to potential side effects from state changes elsewhere in the application.

Additionally, the introduction of more props (hideLineNumber and hideTimestamp) and conditional logic increases the component's API surface and internal complexity. This could make the component harder to understand and maintain over time.

To simplify, consider the following:

  • If the visibility settings are specific to each instance of LogComp, using local state within the component to manage these features could reduce complexity and improve encapsulation.
  • If global state management is necessary, simplifying how state is passed to the component could help. For example, passing a single configuration object instead of multiple boolean flags might make the component's API cleaner and its implementation more straightforward.

By focusing on these aspects, we can maintain the component's flexibility and configurability while also keeping its implementation more maintainable and easier to understand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants