-
Notifications
You must be signed in to change notification settings - Fork 1
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
Conversation
* 🐛 Fixed issue with repoName
* trigger build integration implemented * minor change
There was a problem hiding this 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? ✨
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> | ||
); | ||
}; |
There was a problem hiding this comment.
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.
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> | |
); | |
}; |
const { repo } = ctx.params; | ||
|
||
const repoName = atob(repo || ''); |
There was a problem hiding this comment.
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.
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) : ''; |
toast.error('Repository is required!.'); | ||
return; |
There was a problem hiding this comment.
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.
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, |
There was a problem hiding this comment.
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.
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', |
There was a problem hiding this comment.
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.
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'; |
There was a problem hiding this comment.
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.
No description provided.