Compare commits

..

2 Commits

Author SHA1 Message Date
Stephen Kirby d1cd784866 chore: patch 2.14.2 (#14371)
* chore: sign the windows installer (#14353)

(cherry picked from commit 6f9b3c1592)

* chore: handle merge conflicts

* make fmt patch

---------

Co-authored-by: Kyle Carberry <kyle@coder.com>
Co-authored-by: Jaayden Halko <jaayden.halko@gmail.com>
2024-08-20 15:46:19 -05:00
Stephen Kirby db94b30b1c fix: patch known issues in 2.14.1 (#14207)
* chore: revert status code change for delete users endpoint (#14168)

Revert from https://github.com/coder/coder/pull/13870

(cherry picked from commit b80d99550a)

* chore: delete user codersdk to support status code regression (#14173)

* chore: delete user codersdk to support status code regression
* Update codersdk/users.go

Co-authored-by: Kayla Washburn-Love <mckayla@hey.com>

---------

Co-authored-by: Kayla Washburn-Love <mckayla@hey.com>
(cherry picked from commit b55a7a8b78)

* fix: add template editor to /templates/:templateName route group (#14206)

(cherry picked from commit d0feb70811)

---------

Co-authored-by: Steven Masley <Emyrk@users.noreply.github.com>
Co-authored-by: Kayla Washburn-Love <mckayla@hey.com>
2024-08-07 11:56:07 -05:00
11 changed files with 136 additions and 33 deletions
+2 -2
View File
@@ -4845,8 +4845,8 @@ const docTemplate = `{
}
],
"responses": {
"204": {
"description": "No Content"
"200": {
"description": "OK"
}
}
}
+2 -2
View File
@@ -4273,8 +4273,8 @@
}
],
"responses": {
"204": {
"description": "No Content"
"200": {
"description": "OK"
}
}
}
+4 -2
View File
@@ -513,7 +513,7 @@ func (api *API) postUser(rw http.ResponseWriter, r *http.Request) {
// @Security CoderSessionToken
// @Tags Users
// @Param user path string true "User ID, name, or me"
// @Success 204
// @Success 200
// @Router /users/{user} [delete]
func (api *API) deleteUser(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
@@ -588,7 +588,9 @@ func (api *API) deleteUser(rw http.ResponseWriter, r *http.Request) {
}
}
rw.WriteHeader(http.StatusNoContent)
httpapi.Write(ctx, rw, http.StatusOK, codersdk.Response{
Message: "User has been deleted!",
})
}
// Returns the parameterized user requested. All validation
+3 -1
View File
@@ -309,7 +309,9 @@ func (c *Client) DeleteUser(ctx context.Context, id uuid.UUID) error {
return err
}
defer res.Body.Close()
if res.StatusCode != http.StatusNoContent {
// Check for a 200 or a 204 response. 2.14.0 accidentally included a 204 response,
// which was a breaking change, and reverted in 2.14.1.
if res.StatusCode != http.StatusOK && res.StatusCode != http.StatusNoContent {
return ReadBodyAsError(res)
}
return nil
+3 -3
View File
@@ -426,9 +426,9 @@ curl -X DELETE http://coder-server:8080/api/v2/users/{user} \
### Responses
| Status | Meaning | Description | Schema |
| ------ | --------------------------------------------------------------- | ----------- | ------ |
| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | |
| Status | Meaning | Description | Schema |
| ------ | ------------------------------------------------------- | ----------- | ------ |
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | |
To perform this operation, you must be authenticated. [Learn more](authentication.md).
+10
View File
@@ -19,6 +19,7 @@ source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
agpl="${CODER_BUILD_AGPL:-0}"
output_path=""
version=""
sign_windows="${CODER_SIGN_WINDOWS:-0}"
args="$(getopt -o "" -l agpl,output:,version: -- "$@")"
eval set -- "$args"
@@ -51,6 +52,11 @@ if [[ "$output_path" == "" ]]; then
error "--output is a required parameter"
fi
if [[ "$sign_windows" == 1 ]]; then
dependencies java
requiredenvs JSIGN_PATH EV_KEYSTORE EV_KEY EV_CERTIFICATE_PATH EV_TSA_URL GCLOUD_ACCESS_TOKEN
fi
if [[ "$#" != 1 ]]; then
error "Exactly one argument must be provided to this script, $# were supplied"
fi
@@ -125,3 +131,7 @@ popd
cp "$temp_dir/installer.exe" "$output_path"
rm -rf "$temp_dir"
if [[ "$sign_windows" == 1 ]]; then
execrelative ./sign_windows.sh "$output_path" 1>&2
fi
@@ -0,0 +1,22 @@
import type { Meta, StoryObj } from "@storybook/react";
import { screen, userEvent } from "@storybook/test";
import { CreateTemplateButton } from "./CreateTemplateButton";
const meta: Meta<typeof CreateTemplateButton> = {
title: "pages/TemplatesPage/CreateTemplateButton",
component: CreateTemplateButton,
};
export default meta;
type Story = StoryObj<typeof CreateTemplateButton>;
export const Close: Story = {};
export const Open: Story = {
play: async ({ step }) => {
const user = userEvent.setup();
await step("click on trigger", async () => {
await user.click(screen.getByRole("button"));
});
},
};
@@ -0,0 +1,56 @@
import AddIcon from "@mui/icons-material/AddOutlined";
import Inventory2 from "@mui/icons-material/Inventory2";
import NoteAddOutlined from "@mui/icons-material/NoteAddOutlined";
import UploadOutlined from "@mui/icons-material/UploadOutlined";
import Button from "@mui/material/Button";
import type { FC } from "react";
import {
MoreMenu,
MoreMenuContent,
MoreMenuItem,
MoreMenuTrigger,
} from "components/MoreMenu/MoreMenu";
type CreateTemplateButtonProps = {
onNavigate: (path: string) => void;
};
export const CreateTemplateButton: FC<CreateTemplateButtonProps> = ({
onNavigate,
}) => {
return (
<MoreMenu>
<MoreMenuTrigger>
<Button startIcon={<AddIcon />} variant="contained">
Create Template
</Button>
</MoreMenuTrigger>
<MoreMenuContent>
<MoreMenuItem
onClick={() => {
onNavigate("/templates/new?exampleId=scratch");
}}
>
<NoteAddOutlined />
From scratch
</MoreMenuItem>
<MoreMenuItem
onClick={() => {
onNavigate("/templates/new");
}}
>
<UploadOutlined />
Upload template
</MoreMenuItem>
<MoreMenuItem
onClick={() => {
onNavigate("/starter-templates");
}}
>
<Inventory2 />
Choose a starter template
</MoreMenuItem>
</MoreMenuContent>
</MoreMenu>
);
};
@@ -17,7 +17,7 @@ test("create template from scratch", async () => {
element: <TemplatesPage />,
},
{
path: "/starter-templates",
path: "/templates/new",
element: <div data-testid="new-template-page" />,
},
],
@@ -34,6 +34,9 @@ test("create template from scratch", async () => {
name: "Create Template",
});
await user.click(createTemplateButton);
const fromScratchMenuItem = await screen.findByText("From scratch");
await user.click(fromScratchMenuItem);
await screen.findByTestId("new-template-page");
expect(router.state.location.pathname).toBe("/starter-templates");
expect(router.state.location.pathname).toBe("/templates/new");
expect(router.state.location.search).toBe("?exampleId=scratch");
});
@@ -38,6 +38,7 @@ import {
TableRowSkeleton,
} from "components/TableLoader/TableLoader";
import { useClickableTableRow } from "hooks/useClickableTableRow";
import { useDashboard } from "modules/dashboard/useDashboard";
import { linkToTemplate, useLinks } from "modules/navigation";
import { createDayString } from "utils/createDayString";
import { docs } from "utils/docs";
@@ -45,6 +46,7 @@ import {
formatTemplateBuildTime,
formatTemplateActiveDevelopers,
} from "utils/templates";
import { CreateTemplateButton } from "./CreateTemplateButton";
import { EmptyTemplates } from "./EmptyTemplates";
export const Language = {
@@ -167,38 +169,40 @@ export const TemplatesPageView: FC<TemplatesPageViewProps> = ({
examples,
canCreateTemplates,
}) => {
const { experiments } = useDashboard();
const isLoading = !templates;
const isEmpty = templates && templates.length === 0;
const navigate = useNavigate();
const multiOrgExperimentEnabled = experiments.includes("multi-organization");
const createTemplateAction = () => {
return multiOrgExperimentEnabled ? (
<Button
startIcon={<AddIcon />}
variant="contained"
onClick={() => {
navigate("/starter-templates");
}}
>
Create Template
</Button>
) : (
<CreateTemplateButton onNavigate={navigate} />
);
};
return (
<Margins>
<PageHeader
actions={
canCreateTemplates && (
<Button
startIcon={<AddIcon />}
variant="contained"
onClick={() => {
navigate("/starter-templates");
}}
>
Create Template
</Button>
)
}
>
<PageHeader actions={canCreateTemplates && createTemplateAction()}>
<PageHeaderTitle>
<Stack spacing={1} direction="row" alignItems="center">
Templates
<TemplateHelpTooltip />
</Stack>
</PageHeaderTitle>
{templates && templates.length > 0 && (
<PageHeaderSubtitle>
Select a template to create a workspace.
</PageHeaderSubtitle>
)}
<PageHeaderSubtitle>
Select a template to create a workspace.
</PageHeaderSubtitle>
</PageHeader>
{error ? (
@@ -212,7 +216,7 @@ export const TemplatesPageView: FC<TemplatesPageViewProps> = ({
<TableCell width="15%">{Language.usedByLabel}</TableCell>
<TableCell width="10%">{Language.buildTimeLabel}</TableCell>
<TableCell width="15%">{Language.lastUpdatedLabel}</TableCell>
<TableCell width="1%"></TableCell>
<TableCell width="1%" />
</TableRow>
</TableHead>
<TableBody>
+4
View File
@@ -472,6 +472,10 @@ export const router = createBrowserRouter(
{/* Pages that don't have the dashboard layout */}
<Route path="/:username/:workspace" element={<WorkspacePage />} />
<Route
path="/templates/:template/versions/:version/edit"
element={<TemplateVersionEditorPage />}
/>
<Route
path="/templates/:organization/:template/versions/:version/edit"
element={<TemplateVersionEditorPage />}