Compare commits

...

1 Commits

Author SHA1 Message Date
Mathias Fredriksson fc6d004989 fix: hide Create Workspace button for deleted templates
Fixes #17417

The Create Workspace button was visible on deleted templates, causing
404 errors when clicked. This change exposes the `deleted` field in the
template API response and updates the frontend to hide the button for
deleted templates, following the same pattern as deprecated templates.

Changes:
- Add `Deleted` field to codersdk.Template struct
- Update convertTemplate() to populate the Deleted field
- Update frontend button condition to check !template.deleted
- Add backend test to verify deleted field in API response

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 12:40:35 +00:00
10 changed files with 37 additions and 0 deletions
+3
View File
@@ -18073,6 +18073,9 @@ const docTemplate = `{
"default_ttl_ms": {
"type": "integer"
},
"deleted": {
"type": "boolean"
},
"deprecated": {
"type": "boolean"
},
+3
View File
@@ -16550,6 +16550,9 @@
"default_ttl_ms": {
"type": "integer"
},
"deleted": {
"type": "boolean"
},
"deprecated": {
"type": "boolean"
},
+1
View File
@@ -1129,6 +1129,7 @@ func (api *API) convertTemplate(
RequireActiveVersion: templateAccessControl.RequireActiveVersion,
Deprecated: templateAccessControl.IsDeprecated(),
DeprecationMessage: templateAccessControl.Deprecated,
Deleted: template.Deleted,
MaxPortShareLevel: maxPortShareLevel,
UseClassicParameterFlow: template.UseClassicParameterFlow,
UseTerraformWorkspaceCache: template.UseTerraformWorkspaceCache,
+16
View File
@@ -1753,6 +1753,22 @@ func TestDeleteTemplate(t *testing.T) {
require.ErrorAs(t, err, &apiErr)
require.Equal(t, http.StatusBadRequest, apiErr.StatusCode())
})
t.Run("DeletedIsSet", func(t *testing.T) {
t.Parallel()
client := coderdtest.New(t, &coderdtest.Options{IncludeProvisionerDaemon: true})
user := coderdtest.CreateFirstUser(t, client)
version := coderdtest.CreateTemplateVersion(t, client, user.OrganizationID, nil)
template := coderdtest.CreateTemplate(t, client, user.OrganizationID, version.ID)
coderdtest.AwaitTemplateVersionJobCompleted(t, client, version.ID)
ctx := testutil.Context(t, testutil.WaitLong)
// Verify the deleted field is exposed in the SDK and set to false for active templates
got, err := client.Template(ctx, template.ID)
require.NoError(t, err)
require.False(t, got.Deleted)
})
}
func TestTemplateMetrics(t *testing.T) {
+1
View File
@@ -32,6 +32,7 @@ type Template struct {
Description string `json:"description"`
Deprecated bool `json:"deprecated"`
DeprecationMessage string `json:"deprecation_message"`
Deleted bool `json:"deleted"`
Icon string `json:"icon"`
DefaultTTLMillis int64 `json:"default_ttl_ms"`
ActivityBumpMillis int64 `json:"activity_bump_ms"`
+2
View File
@@ -8101,6 +8101,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit|
"created_by_id": "9377d689-01fb-4abf-8450-3368d2c1924f",
"created_by_name": "string",
"default_ttl_ms": 0,
"deleted": true,
"deprecated": true,
"deprecation_message": "string",
"description": "string",
@@ -8142,6 +8143,7 @@ Only certain features set these fields: - FeatureManagedAgentLimit|
| `created_by_id` | string | false | | |
| `created_by_name` | string | false | | |
| `default_ttl_ms` | integer | false | | |
| `deleted` | boolean | false | | |
| `deprecated` | boolean | false | | |
| `deprecation_message` | string | false | | |
| `description` | string | false | | |
+8
View File
@@ -62,6 +62,7 @@ To include deprecated templates, specify `deprecated:true` in the search query.
"created_by_id": "9377d689-01fb-4abf-8450-3368d2c1924f",
"created_by_name": "string",
"default_ttl_ms": 0,
"deleted": true,
"deprecated": true,
"deprecation_message": "string",
"description": "string",
@@ -120,6 +121,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W
|`» created_by_id`|string(uuid)|false|||
|`» created_by_name`|string|false|||
|`» default_ttl_ms`|integer|false|||
|`» deleted`|boolean|false|||
|`» deprecated`|boolean|false|||
|`» deprecation_message`|string|false|||
|`» description`|string|false|||
@@ -250,6 +252,7 @@ curl -X POST http://coder-server:8080/api/v2/organizations/{organization}/templa
"created_by_id": "9377d689-01fb-4abf-8450-3368d2c1924f",
"created_by_name": "string",
"default_ttl_ms": 0,
"deleted": true,
"deprecated": true,
"deprecation_message": "string",
"description": "string",
@@ -401,6 +404,7 @@ curl -X GET http://coder-server:8080/api/v2/organizations/{organization}/templat
"created_by_id": "9377d689-01fb-4abf-8450-3368d2c1924f",
"created_by_name": "string",
"default_ttl_ms": 0,
"deleted": true,
"deprecated": true,
"deprecation_message": "string",
"description": "string",
@@ -818,6 +822,7 @@ To include deprecated templates, specify `deprecated:true` in the search query.
"created_by_id": "9377d689-01fb-4abf-8450-3368d2c1924f",
"created_by_name": "string",
"default_ttl_ms": 0,
"deleted": true,
"deprecated": true,
"deprecation_message": "string",
"description": "string",
@@ -876,6 +881,7 @@ Restarts will only happen on weekdays in this list on weeks which line up with W
|`» created_by_id`|string(uuid)|false|||
|`» created_by_name`|string|false|||
|`» default_ttl_ms`|integer|false|||
|`» deleted`|boolean|false|||
|`» deprecated`|boolean|false|||
|`» deprecation_message`|string|false|||
|`» description`|string|false|||
@@ -1024,6 +1030,7 @@ curl -X GET http://coder-server:8080/api/v2/templates/{template} \
"created_by_id": "9377d689-01fb-4abf-8450-3368d2c1924f",
"created_by_name": "string",
"default_ttl_ms": 0,
"deleted": true,
"deprecated": true,
"deprecation_message": "string",
"description": "string",
@@ -1197,6 +1204,7 @@ curl -X PATCH http://coder-server:8080/api/v2/templates/{template} \
"created_by_id": "9377d689-01fb-4abf-8450-3368d2c1924f",
"created_by_name": "string",
"default_ttl_ms": 0,
"deleted": true,
"deprecated": true,
"deprecation_message": "string",
"description": "string",
+1
View File
@@ -4886,6 +4886,7 @@ export interface Template {
readonly description: string;
readonly deprecated: boolean;
readonly deprecation_message: string;
readonly deleted: boolean;
readonly icon: string;
readonly default_ttl_ms: number;
readonly activity_bump_ms: number;
@@ -220,6 +220,7 @@ export const TemplatePageHeader: FC<TemplatePageHeaderProps> = ({
actions={
<>
{!template.deprecated &&
!template.deleted &&
workspacePermissions.createWorkspaceForUserID && (
<Button asChild>
<RouterLink to={`${templateLink}/workspace`}>
+1
View File
@@ -852,6 +852,7 @@ export const MockTemplate: TypesGen.Template = {
require_active_version: false,
deprecated: false,
deprecation_message: "",
deleted: false,
max_port_share_level: "public",
use_classic_parameter_flow: false,
cors_behavior: "simple",