Skip to content

feat(site): Ask for parameter values when update a workspace #6586

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

Merged
merged 10 commits into from
Mar 14, 2023
Merged
Show file tree
Hide file tree
Changes from 8 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
80 changes: 80 additions & 0 deletions site/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -897,3 +897,83 @@ export const getWorkspaceBuildParameters = async (
)
return response.data
}

export class MissingBuildParameters extends Error {
parameters: TypesGen.TemplateVersionParameter[] = []

constructor(parameters: TypesGen.TemplateVersionParameter[]) {
super("Missing build parameters.")
this.parameters = parameters
}
}

/** Steps to update the workspace
* - Get the latest template to access the latest active version
* - Get the current build parameters
* - Get the template parameters
* - Update the build parameters and check if there are missed parameters for the newest version
* - If there are missing parameters raise an error
* - Create a build with the latest version and updated build parameters
*/
export const updateWorkspace = async (
workspace: TypesGen.Workspace,
newBuildParameters: TypesGen.WorkspaceBuildParameter[] = [],
): Promise<TypesGen.WorkspaceBuild> => {
const [template, oldBuildParameters] = await Promise.all([
getTemplate(workspace.template_id),
getWorkspaceBuildParameters(workspace.latest_build.id),
])
const activeVersionId = template.active_version_id
const templateParameters = await getTemplateVersionRichParameters(
activeVersionId,
)
const [updatedBuildParameters, missingParameters] = updateBuildParameters(
oldBuildParameters,
newBuildParameters,
templateParameters,
)

if (missingParameters.length > 0) {
throw new MissingBuildParameters(missingParameters)
}

return postWorkspaceBuild(workspace.id, {
transition: "start",
template_version_id: activeVersionId,
rich_parameter_values: updatedBuildParameters,
})
}

const updateBuildParameters = (
oldBuildParameters: TypesGen.WorkspaceBuildParameter[],
newBuildParameters: TypesGen.WorkspaceBuildParameter[],
templateParameters: TypesGen.TemplateVersionParameter[],
) => {
const missingParameters: TypesGen.TemplateVersionParameter[] = []
const updatedBuildParameters: TypesGen.WorkspaceBuildParameter[] = []

for (const parameter of templateParameters) {
// Check if there is a new value
let buildParameter = newBuildParameters.find(
(p) => p.name === parameter.name,
)

// If not, get the old one
if (!buildParameter) {
buildParameter = oldBuildParameters.find((p) => p.name === parameter.name)
}

// If there is a value from the new or old one, add it to the list
if (buildParameter) {
updatedBuildParameters.push(buildParameter)
continue
}

// If there is no value and it is required, add it to the list of missing parameters
if (parameter.required) {
missingParameters.push(parameter)
}
}

return [updatedBuildParameters, missingParameters] as const
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,84 @@ import {
FormFooter as BaseFormFooter,
} from "components/FormFooter/FormFooter"
import { Stack } from "components/Stack/Stack"
import { FC, HTMLProps, PropsWithChildren } from "react"
import {
createContext,
FC,
HTMLProps,
PropsWithChildren,
useContext,
} from "react"
import { combineClasses } from "util/combineClasses"

export const HorizontalForm: FC<
PropsWithChildren & HTMLProps<HTMLFormElement>
> = ({ children, ...formProps }) => {
type FormContextValue = { direction?: "horizontal" | "vertical" }

const FormContext = createContext<FormContextValue>({
direction: "horizontal",
})

type FormProps = HTMLProps<HTMLFormElement> & {
direction?: FormContextValue["direction"]
}

export const Form: FC<FormProps> = ({ direction, className, ...formProps }) => {
const styles = useStyles()

return (
<form {...formProps}>
<Stack direction="column" spacing={10} className={styles.formSections}>
{children}
</Stack>
</form>
<FormContext.Provider value={{ direction }}>
<form
{...formProps}
className={combineClasses([styles.form, className])}
/>
</FormContext.Provider>
)
}

export const HorizontalForm: FC<HTMLProps<HTMLFormElement>> = ({
children,
...formProps
}) => {
return (
<Form direction="horizontal" {...formProps}>
{children}
</Form>
)
}

export const VerticalForm: FC<HTMLProps<HTMLFormElement>> = ({
children,
...formProps
}) => {
return (
<Form direction="vertical" {...formProps}>
{children}
</Form>
)
}

export const FormSection: FC<
PropsWithChildren & {
title: string
title: string | JSX.Element
description: string | JSX.Element
className?: string
classes?: {
root?: string
infoTitle?: string
}
}
> = ({ children, title, description, className }) => {
const styles = useStyles()
> = ({ children, title, description, classes = {} }) => {
const formContext = useContext(FormContext)
const styles = useStyles(formContext)

return (
<div className={combineClasses([styles.formSection, className])}>
<div className={combineClasses([styles.formSection, classes.root])}>
<div className={styles.formSectionInfo}>
<h2 className={styles.formSectionInfoTitle}>{title}</h2>
<h2
className={combineClasses([
styles.formSectionInfoTitle,
classes.infoTitle,
])}
>
{title}
</h2>
<div className={styles.formSectionInfoDescription}>{description}</div>
</div>

Expand Down Expand Up @@ -62,7 +110,12 @@ export const FormFooter: FC<BaseFormFooterProps> = (props) => {
}

const useStyles = makeStyles((theme) => ({
formSections: {
form: {
display: "flex",
flexDirection: "column",
gap: ({ direction }: FormContextValue = {}) =>
direction === "horizontal" ? theme.spacing(10) : theme.spacing(5),

[theme.breakpoints.down("sm")]: {
gap: theme.spacing(8),
},
Expand All @@ -71,7 +124,10 @@ const useStyles = makeStyles((theme) => ({
formSection: {
display: "flex",
alignItems: "flex-start",
gap: theme.spacing(15),
gap: ({ direction }: FormContextValue = {}) =>
direction === "horizontal" ? theme.spacing(15) : theme.spacing(3),
flexDirection: ({ direction }: FormContextValue = {}) =>
direction === "horizontal" ? "row" : "column",

[theme.breakpoints.down("sm")]: {
flexDirection: "column",
Expand All @@ -80,9 +136,11 @@ const useStyles = makeStyles((theme) => ({
},

formSectionInfo: {
width: 312,
maxWidth: ({ direction }: FormContextValue = {}) =>
direction === "horizontal" ? 312 : undefined,
flexShrink: 0,
position: "sticky",
position: ({ direction }: FormContextValue = {}) =>
direction === "horizontal" ? "sticky" : undefined,
top: theme.spacing(3),

[theme.breakpoints.down("sm")]: {
Expand Down
4 changes: 2 additions & 2 deletions site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,12 @@ import { LazyIconField } from "components/IconField/LazyIconField"
import { Maybe } from "components/Conditionals/Maybe"
import i18next from "i18next"
import Link from "@material-ui/core/Link"
import { FormFooter } from "components/FormFooter/FormFooter"
import {
HorizontalForm,
FormSection,
FormFields,
} from "components/HorizontalForm/HorizontalForm"
FormFooter,
} from "components/Form/Form"
import camelCase from "lodash/camelCase"
import capitalize from "lodash/capitalize"
import { VariableInput } from "./VariableInput"
Expand Down
Loading