diff --git a/coderd/apidoc/docs.go b/coderd/apidoc/docs.go index 2a00a4616c..f766a836a0 100644 --- a/coderd/apidoc/docs.go +++ b/coderd/apidoc/docs.go @@ -5468,6 +5468,9 @@ const docTemplate = `{ "schema": { "$ref": "#/definitions/codersdk.TemplateVersion" } + }, + "204": { + "description": "No Content" } }, "security": [ diff --git a/coderd/apidoc/swagger.json b/coderd/apidoc/swagger.json index f723051ce2..3f09eafba6 100644 --- a/coderd/apidoc/swagger.json +++ b/coderd/apidoc/swagger.json @@ -4853,6 +4853,9 @@ "schema": { "$ref": "#/definitions/codersdk.TemplateVersion" } + }, + "204": { + "description": "No Content" } }, "security": [ diff --git a/coderd/templateversions.go b/coderd/templateversions.go index 55e2838d08..6490165782 100644 --- a/coderd/templateversions.go +++ b/coderd/templateversions.go @@ -1074,6 +1074,7 @@ func (api *API) templateVersionByOrganizationTemplateAndName(rw http.ResponseWri // @Param templatename path string true "Template name" // @Param templateversionname path string true "Template version name" // @Success 200 {object} codersdk.TemplateVersion +// @Success 204 // @Router /organizations/{organization}/templates/{templatename}/versions/{templateversionname}/previous [get] func (api *API) previousTemplateVersionByOrganizationTemplateAndName(rw http.ResponseWriter, r *http.Request) { ctx := r.Context() @@ -1126,9 +1127,7 @@ func (api *API) previousTemplateVersionByOrganizationTemplateAndName(rw http.Res }) if err != nil { if httpapi.Is404Error(err) { - httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{ - Message: fmt.Sprintf("No previous template version found for %q.", templateVersionName), - }) + rw.WriteHeader(http.StatusNoContent) return } diff --git a/coderd/templateversions_test.go b/coderd/templateversions_test.go index 4520d37a10..c3d2153f34 100644 --- a/coderd/templateversions_test.go +++ b/coderd/templateversions_test.go @@ -1980,8 +1980,8 @@ func TestPreviousTemplateVersion(t *testing.T) { templateAVersion1 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) coderdtest.CreateTemplate(t, client, user.OrganizationID, templateAVersion1.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, templateAVersion1.ID) - // Create two versions for the template B to be sure if we try to get the - // previous version of the first version it will returns a 404 + // Create two versions for template B so we can verify that requesting + // the previous version of the first version returns nil. templateBVersion1 := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil) templateB := coderdtest.CreateTemplate(t, client, user.OrganizationID, templateBVersion1.ID) coderdtest.AwaitTemplateVersionJobCompleted(t, client, templateBVersion1.ID) @@ -1992,9 +1992,7 @@ func TestPreviousTemplateVersion(t *testing.T) { defer cancel() _, err := client.PreviousTemplateVersion(ctx, user.OrganizationID, templateB.Name, templateBVersion1.Name) - var apiErr *codersdk.Error - require.ErrorAs(t, err, &apiErr) - require.Equal(t, http.StatusNotFound, apiErr.StatusCode()) + require.ErrorIs(t, err, codersdk.ErrNoPreviousVersion) }) t.Run("Previous version found", func(t *testing.T) { diff --git a/codersdk/templateversions.go b/codersdk/templateversions.go index 9927975786..01cd233707 100644 --- a/codersdk/templateversions.go +++ b/codersdk/templateversions.go @@ -9,6 +9,7 @@ import ( "time" "github.com/google/uuid" + "golang.org/x/xerrors" ) type TemplateVersionWarning string @@ -280,12 +281,19 @@ func (c *Client) CancelTemplateVersionDryRun(ctx context.Context, version, job u return nil } +// ErrNoPreviousVersion is returned when no previous template version +// exists (the server responds with 204 No Content). +var ErrNoPreviousVersion = xerrors.New("no previous template version") + func (c *Client) PreviousTemplateVersion(ctx context.Context, organization uuid.UUID, templateName, versionName string) (TemplateVersion, error) { res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/organizations/%s/templates/%s/versions/%s/previous", organization, templateName, versionName), nil) if err != nil { return TemplateVersion{}, err } defer res.Body.Close() + if res.StatusCode == http.StatusNoContent { + return TemplateVersion{}, ErrNoPreviousVersion + } if res.StatusCode != http.StatusOK { return TemplateVersion{}, ReadBodyAsError(res) } diff --git a/docs/reference/api/templates.md b/docs/reference/api/templates.md index f23a670fdf..4d0b89b77a 100644 --- a/docs/reference/api/templates.md +++ b/docs/reference/api/templates.md @@ -632,9 +632,10 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat ### Responses -| Status | Meaning | Description | Schema | -|--------|---------------------------------------------------------|-------------|----------------------------------------------------------------| -| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.TemplateVersion](schemas.md#codersdktemplateversion) | +| Status | Meaning | Description | Schema | +|--------|-----------------------------------------------------------------|-------------|----------------------------------------------------------------| +| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | [codersdk.TemplateVersion](schemas.md#codersdktemplateversion) | +| 204 | [No Content](https://tools.ietf.org/html/rfc7231#section-6.3.5) | No Content | | To perform this operation, you must be authenticated. [Learn more](authentication.md). diff --git a/site/src/api/api.ts b/site/src/api/api.ts index dc847f4eba..f47d4972a3 100644 --- a/site/src/api/api.ts +++ b/site/src/api/api.ts @@ -1097,25 +1097,17 @@ class ApiMethods { templateName: string, versionName: string, ) => { - try { - const response = await this.axios.get( - `/api/v2/organizations/${organization}/templates/${templateName}/versions/${versionName}/previous`, - ); + const response = await this.axios.get( + `/api/v2/organizations/${organization}/templates/${templateName}/versions/${versionName}/previous`, + ); - return response.data; - } catch (error) { - // When there is no previous version, like the first version of a - // template, the API returns 404 so in this case we can safely return - // undefined - const is404 = - isAxiosError(error) && error.response && error.response.status === 404; - - if (is404) { - return undefined; - } - - throw error; + // The API returns 204 No Content when there is no previous version + // (e.g. the first version of a template). + if (response.status === 204) { + return undefined; } + + return response.data; }; /** diff --git a/site/src/modules/dashboard/DeploymentBanner/DeploymentBanner.tsx b/site/src/modules/dashboard/DeploymentBanner/DeploymentBanner.tsx index d153d6add1..d82e7f1669 100644 --- a/site/src/modules/dashboard/DeploymentBanner/DeploymentBanner.tsx +++ b/site/src/modules/dashboard/DeploymentBanner/DeploymentBanner.tsx @@ -18,7 +18,10 @@ const HIDE_DEPLOYMENT_BANNER_PATHS = [ export const DeploymentBanner: FC = () => { const { permissions } = useAuthenticated(); - const deploymentStatsQuery = useQuery(deploymentStats()); + const deploymentStatsQuery = useQuery({ + ...deploymentStats(), + enabled: permissions.viewDeploymentStats, + }); const healthQuery = useQuery({ ...health(), enabled: permissions.viewDeploymentConfig, diff --git a/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx b/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx index 0b0741aca5..31247e0761 100644 --- a/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx +++ b/site/src/pages/CreateTemplatePage/CreateTemplateForm.tsx @@ -259,7 +259,7 @@ export const CreateTemplateForm: FC = (props) => { const { data: provisioners } = useQuery({ ...provisionerDaemons(selectedOrg?.id ?? ""), - enabled: showOrganizationPicker && Boolean(selectedOrg), + enabled: Boolean(showOrganizationPicker) && Boolean(selectedOrg), }); // TODO: Ideally, we would have a backend endpoint that could notify the diff --git a/site/src/pages/OrganizationSettingsPage/OrganizationProvisionerKeysPage/OrganizationProvisionerKeysPage.tsx b/site/src/pages/OrganizationSettingsPage/OrganizationProvisionerKeysPage/OrganizationProvisionerKeysPage.tsx index a0c01a2f0b..2b0bd12237 100644 --- a/site/src/pages/OrganizationSettingsPage/OrganizationProvisionerKeysPage/OrganizationProvisionerKeysPage.tsx +++ b/site/src/pages/OrganizationSettingsPage/OrganizationProvisionerKeysPage/OrganizationProvisionerKeysPage.tsx @@ -17,6 +17,7 @@ const OrganizationProvisionerKeysPage: FC = () => { const { entitlements } = useDashboard(); const provisionerKeyDaemonsQuery = useQuery({ ...provisionerDaemonGroups(organizationName), + enabled: !!organization, select: (data) => [...data].sort((a, b) => b.daemons.length - a.daemons.length), }); diff --git a/site/src/pages/OrganizationSettingsPage/OrganizationProvisionersPage/OrganizationProvisionersPage.tsx b/site/src/pages/OrganizationSettingsPage/OrganizationProvisionersPage/OrganizationProvisionersPage.tsx index f164008797..c87dbfd6fd 100644 --- a/site/src/pages/OrganizationSettingsPage/OrganizationProvisionersPage/OrganizationProvisionersPage.tsx +++ b/site/src/pages/OrganizationSettingsPage/OrganizationProvisionersPage/OrganizationProvisionersPage.tsx @@ -30,6 +30,7 @@ const OrganizationProvisionersPage: FC = () => { ...queryParams, limit: 100, }), + enabled: !!organization, }); if (!organization) { diff --git a/site/src/pages/TasksPage/TasksTable.tsx b/site/src/pages/TasksPage/TasksTable.tsx index cbabb96a57..ef7b4eee6e 100644 --- a/site/src/pages/TasksPage/TasksTable.tsx +++ b/site/src/pages/TasksPage/TasksTable.tsx @@ -96,9 +96,7 @@ export const TasksTable: FC = ({ 0 && - checkedTaskIds.size === tasks.length + Boolean(tasks) && checkedTaskIds.size === tasks?.length } onCheckedChange={(checked) => { if (!tasks || !onCheckChange) {