-
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
KLO-128 : user should be able to select nodespool while creating application and project managed services #118
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,13 +7,15 @@ import Yup from '~/root/lib/server/helpers/yup'; | |
import { FadeIn, parseValue } from '~/console/page-components/util'; | ||
import Select from '~/components/atoms/select'; | ||
import ExtendedFilledTab from '~/console/components/extended-filled-tab'; | ||
import { parseNodes } from '~/console/server/r-utils/common'; | ||
import {parseName, parseNodes} from '~/console/server/r-utils/common'; | ||
import useCustomSwr from '~/lib/client/hooks/use-custom-swr'; | ||
import { useConsoleApi } from '~/console/server/gql/api-provider'; | ||
import { useMapper } from '~/components/utils'; | ||
import { registryHost } from '~/lib/configs/base-url.cjs'; | ||
import { BottomNavigation } from '~/console/components/commons'; | ||
import { plans } from './datas'; | ||
import {useOutletContext } from '@remix-run/react'; | ||
import {IAppContext} from "~/console/routes/_main+/$account+/$project+/$environment+/app+/$app+/_layout"; | ||
|
||
const valueRender = ({ | ||
label, | ||
|
@@ -47,6 +49,9 @@ const AppCompute = () => { | |
getImageTag, | ||
} = useAppState(); | ||
const api = useConsoleApi(); | ||
const {cluster} = useOutletContext<IAppContext>() | ||
|
||
console.log("cluster", parseName(cluster)) | ||
|
||
const { | ||
data, | ||
|
@@ -56,6 +61,15 @@ const AppCompute = () => { | |
return api.listRepo({}); | ||
}); | ||
|
||
const { | ||
data: nodepoolData, | ||
isLoading: nodepoolLoading, | ||
error: nodepoolLoadingError, | ||
} = useCustomSwr('/nodepools', async () => { | ||
return api.listNodePools({clusterName: parseName(cluster)}) | ||
}) | ||
|
||
console.log("np", nodepoolData) | ||
const { values, errors, handleChange, isLoading, submit } = useForm({ | ||
initialValues: { | ||
imageUrl: app.spec.containers[activeContIndex]?.image || '', | ||
|
@@ -97,6 +111,8 @@ const AppCompute = () => { | |
app.spec.containers[activeContIndex].resourceMemory?.max, | ||
0 | ||
), | ||
|
||
nodepoolName: app.spec.nodeSelector?.[keyconstants.nodepoolName] || '' | ||
}, | ||
validationSchema: Yup.object({ | ||
pullSecret: Yup.string(), | ||
|
@@ -121,6 +137,10 @@ const AppCompute = () => { | |
}, | ||
spec: { | ||
...s.spec, | ||
nodeSelector: { | ||
...(s.spec.nodeSelector || {}), | ||
[keyconstants.nodepoolName]: val.nodepoolName | ||
}, | ||
containers: [ | ||
{ | ||
...(s.spec.containers?.[0] || {}), | ||
|
@@ -166,6 +186,11 @@ const AppCompute = () => { | |
accName: val.accountName, | ||
})); | ||
|
||
const nodepools = useMapper(parseNodes(nodepoolData), (val) => ({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (llm): The addition of nodepool selection in the app compute configuration is a significant feature. To ensure its robustness, unit tests should be added to verify that the nodepool selection is correctly passed to the app's spec. This includes testing the handling of the selected nodepool name and ensuring it's correctly applied. |
||
label: val.metadata?.name || '', | ||
value: val.metadata?.name || '', | ||
})) | ||
|
||
const { | ||
data: digestData, | ||
isLoading: digestLoading, | ||
|
@@ -196,6 +221,24 @@ const AppCompute = () => { | |
manipulation and calculations in a system. | ||
</div> | ||
<div className="flex flex-col gap-3xl"> | ||
|
||
<Select | ||
label="Nodepool Name" | ||
size="lg" | ||
placeholder="Select Nodepool" | ||
value={values.nodepoolName} | ||
creatable | ||
onChange={(val) => { | ||
handleChange('nodepoolName')(dummyEvent(val.value)); | ||
}} | ||
options={async () => [...nodepools]} | ||
error={!!errors.repos || !!nodepoolLoadingError} | ||
message={ | ||
nodepoolLoadingError ? 'Error fetching nodepools.' : errors.app | ||
} | ||
loading={nodepoolLoading} | ||
/> | ||
|
||
<Select | ||
label="Repo Name" | ||
size="lg" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,6 +13,7 @@ import { | |
BottomNavigation, | ||
ReviewComponent, | ||
} from '~/console/components/commons'; | ||
import {keyconstants} from "~/console/server/r-utils/key-constants"; | ||
|
||
const AppReview = () => { | ||
const { app, setPage, resetState } = useAppState(); | ||
|
@@ -114,12 +115,19 @@ const AppReview = () => { | |
</div> | ||
</div> | ||
</ReviewComponent> | ||
<ReviewComponent | ||
title="Environment" | ||
<ReviewComponent title="Nodepool Details" onEdit={() => {}}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (llm): The addition of the "Nodepool Details" section via a new There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (llm): The inclusion of nodepool details in the app review step is a good addition. It would be beneficial to have end-to-end tests that cover the scenario of selecting a nodepool and verifying that this selection is accurately reflected in the review step. This ensures that the user's selection is correctly captured and displayed. |
||
<div className="flex flex-col p-xl gap-md rounded border border-border-default"> | ||
<div className="bodyMd-semibold text-text-default"> | ||
Nodepool Selector | ||
</div> | ||
<div className="bodySm text-text-soft">{app.spec.nodeSelector[keyconstants.nodepoolName]}</div> | ||
</div> | ||
</ReviewComponent> | ||
<ReviewComponent | ||
title="Environment" | ||
onEdit={() => { | ||
setPage(3); | ||
}} | ||
> | ||
}}> | ||
<div className="flex flex-col gap-xl p-xl rounded border border-border-default"> | ||
<div className="flex flex-row items-center gap-lg pb-xl border-b border-border-default"> | ||
<div className="flex-1 bodyMd-medium text-text-default"> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,7 +14,7 @@ import { | |
import { Switch } from '~/components/atoms/switch'; | ||
import { NumberInput, TextInput } from '~/components/atoms/input'; | ||
import { handleError } from '~/root/lib/utils/common'; | ||
import { titleCase } from '~/components/utils'; | ||
import {titleCase, useMapper} from '~/components/utils'; | ||
import { flatMapValidations, flatM } from '~/console/utils/commons'; | ||
import MultiStepProgress, { | ||
useMultiStepProgress, | ||
|
@@ -25,6 +25,10 @@ import { | |
ReviewComponent, | ||
} from '~/console/components/commons'; | ||
import { IProjectContext } from '../_layout'; | ||
import {parseName, parseNodes} from "~/console/server/r-utils/common"; | ||
import useCustomSwr from "~/lib/client/hooks/use-custom-swr"; | ||
import {INodepools} from "~/console/server/gql/queries/nodepool-queries"; | ||
import {keyconstants} from "~/console/server/r-utils/key-constants"; | ||
|
||
const valueRender = ({ label, icon }: { label: string; icon: string }) => { | ||
return ( | ||
|
@@ -239,6 +243,7 @@ const TemplateView = ({ | |
|
||
const FieldView = ({ | ||
selectedTemplate, | ||
nodepools, | ||
values, | ||
handleSubmit, | ||
handleChange, | ||
|
@@ -249,6 +254,7 @@ const FieldView = ({ | |
values: Record<string, any>; | ||
errors: Record<string, any>; | ||
selectedTemplate: ISelectedTemplate | null; | ||
nodepools: {label: string, value: string}[] | ||
}) => { | ||
const nameRef = useRef<HTMLInputElement>(null); | ||
useEffect(() => { | ||
|
@@ -276,6 +282,21 @@ const FieldView = ({ | |
handleChange={handleChange} | ||
nameErrorLabel="isNameError" | ||
/> | ||
|
||
<Select | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (llm): The implementation of the 'Select' component for 'Nodepool Name' is well-integrated with the form's state management and validation schema. The use of async options loading and the 'creatable' prop provides a flexible user experience. However, consider handling the scenario where the async loading fails more gracefully, ensuring the user is informed and can retry or proceed with manual input. |
||
label="Nodepool Name" | ||
size="lg" | ||
placeholder="Select Nodepool" | ||
value={values.nodepoolName} | ||
creatable | ||
onChange={(val) => { | ||
handleChange('nodepoolName')(dummyEvent(val.value)); | ||
}} | ||
options={async () => [...nodepools]} | ||
error={!!errors.nodepoolName} | ||
message={errors.nodepoolName} | ||
/> | ||
|
||
{selectedTemplate?.template.fields?.map((field) => { | ||
const k = field.name; | ||
const x = k.split('.').reduce((acc, curr) => { | ||
|
@@ -368,21 +389,32 @@ const ReviewView = ({ | |
</div> | ||
</div> | ||
</ReviewComponent> | ||
|
||
<ReviewComponent | ||
title="Service detail" | ||
onEdit={() => { | ||
onEdit(1); | ||
}} | ||
> | ||
<div className="flex flex-col p-xl gap-md rounded border border-border-default"> | ||
<div className="bodyMd-semibold text-text-default"> | ||
{values?.selectedTemplate?.categoryDisplayName} | ||
title="Service details" | ||
onEdit={() => { | ||
onEdit(1); | ||
}}> | ||
<div className="flex flex-col gap-xl p-xl rounded border border-border-default"> | ||
<div className="flex flex-col gap-lg pb-xl border-b border-border-default"> | ||
<div className="flex-1 bodyMd-medium text-text-default"> | ||
{values?.selectedTemplate?.categoryDisplayName} | ||
</div> | ||
<div className="text-text-soft bodyMd"> | ||
{values?.selectedTemplate?.template?.displayName} | ||
</div> | ||
</div> | ||
<div className="bodySm text-text-soft"> | ||
{values?.selectedTemplate?.template?.displayName} | ||
<div className="flex flex-col gap-lg"> | ||
<div className="flex-1 bodyMd-medium text-text-default"> | ||
Node Selector | ||
</div> | ||
<div className="text-text-soft bodyMd"> | ||
{values.nodepoolName} | ||
</div> | ||
</div> | ||
</div> | ||
</ReviewComponent> | ||
|
||
{renderFieldView()} | ||
{values?.res?.resources && ( | ||
<ReviewComponent | ||
|
@@ -436,6 +468,17 @@ const ManagedServiceLayout = () => { | |
const { project, account } = useParams(); | ||
const rootUrl = `/${account}/${project}/managed-services`; | ||
|
||
const { cluster } = useOutletContext<IProjectContext>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. issue (llm): The addition of new logic for fetching and managing node pools significantly increases the complexity of this component. This includes more state variables, asynchronous data fetching, and additional UI logic for handling the selection of node pools. While these changes are necessary for the new functionality, there are opportunities to simplify the code and improve maintainability. Consider abstracting the complex logic related to fetching and transforming node pool data into a custom hook. This would encapsulate the asynchronous operations and data processing, making the main component more focused on rendering and interaction logic. For example, a Simplifying state management by reducing the number of state variables or centralizing their management can also help make the component easier to understand and reduce the potential for bugs. Additionally, consider lazy loading components or data that are not critical for the initial render to improve performance and reduce the initial load complexity. By addressing these points, the component can be made more readable and easier to maintain, while still fulfilling its required functionality. |
||
console.log("cluster", parseName(cluster)) | ||
|
||
const { | ||
data: nodepoolData, | ||
isLoading: nodepoolLoading, | ||
error: nodepoolLoadingError, | ||
} = useCustomSwr('/nodepools', async () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (llm): It's great to see the use of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (llm): Ensure to handle loading and error states gracefully in the UI for a better user experience. This could include displaying loading indicators while the nodepools are being fetched and showing error messages if the fetch fails. Testing these scenarios to ensure they're handled correctly would be beneficial. |
||
return api.listNodePools({clusterName: parseName(cluster)}) | ||
}) | ||
|
||
const { currentStep, jumpStep, nextStep } = useMultiStepProgress({ | ||
defaultStep: 1, | ||
totalSteps: 3, | ||
|
@@ -449,6 +492,7 @@ const ManagedServiceLayout = () => { | |
res: {}, | ||
selectedTemplate: null, | ||
isNameError: false, | ||
nodepoolName: '' | ||
}, | ||
validationSchema: Yup.object().shape({ | ||
name: Yup.string().test('required', 'Name is required', (v) => { | ||
|
@@ -513,6 +557,9 @@ const ManagedServiceLayout = () => { | |
|
||
spec: { | ||
msvcSpec: { | ||
nodeSelector: { | ||
[keyconstants.nodepoolName]: val.nodepoolName | ||
}, | ||
serviceTemplate: { | ||
apiVersion: selectedTemplate.template.apiVersion, | ||
kind: selectedTemplate.template.kind, | ||
|
@@ -550,6 +597,14 @@ const ManagedServiceLayout = () => { | |
}, | ||
}); | ||
|
||
const nodepools = useMapper(parseNodes(nodepoolData), (val) => ({ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion (llm): I noticed that you're filtering nodepools to only include those with a 'stateful' nodepoolStateType. It would be beneficial to include integration tests that verify the filtering logic works as expected, especially in scenarios with a mix of stateful and other types of nodepools. |
||
label: val.metadata?.name || '', | ||
value: val.metadata?.name || '', | ||
nodepoolStateType: val.spec.nodeLabels[keyconstants.nodepoolStateType] | ||
})) | ||
|
||
const statefulNodepools = nodepools.filter((np) => np.nodepoolStateType == 'stateful') | ||
|
||
useEffect(() => { | ||
const selectedTemplate = | ||
values.selectedTemplate as unknown as ISelectedTemplate; | ||
|
@@ -594,6 +649,7 @@ const ManagedServiceLayout = () => { | |
errors={errors} | ||
handleChange={handleChange} | ||
handleSubmit={handleSubmit} | ||
nodepools={statefulNodepools} | ||
/> | ||
</MultiStepProgress.Step> | ||
<MultiStepProgress.Step label="Review" step={3}> | ||
|
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 (llm): The refactored code introduces additional complexity compared to the original version, primarily due to increased dependencies, more intricate state management, and the addition of new UI elements. Here are a few suggestions to streamline the implementation:
Abstract Data Fetching: Consider encapsulating the data fetching logic into custom hooks or services. This not only reduces the complexity within the component but also makes the data fetching logic reusable and easier to maintain.
Simplify State Management: There's an opportunity to simplify state management by consolidating related states or employing a state management library that offers a cleaner way to manage dependencies between states.
Remove Debugging Code: It's generally advisable to remove
console.log
statements from production code. If logging is necessary, consider implementing a structured logging approach that can be toggled based on the environment.UI Components Abstraction: For UI patterns that recur, such as form fields with validation, creating abstracted components can reduce duplication and simplify the main component. This encapsulates common logic and styling, making the overall codebase more manageable.
By addressing these points, the code can be made more maintainable and easier to understand, enhancing its long-term scalability.