mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add user secrets client utilities (#25370)
Add frontend API methods, mocks, and form helpers for user secrets CRUD. The new client methods cover list, get, create, update, and delete requests, including URL encoding for secret names used in route paths. Add user secret form utilities for create and update payload construction, required create field checks, and structured API validation error mapping back to form fields. User secret name validation now lives in codersdk with tests, and coderd returns field-level validation errors for create, update, and uniqueness conflicts so the frontend can show backend-owned validation results consistently.
This commit is contained in:
@@ -132,6 +132,24 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
// UserSecretNameValid validates a user secret name. Names are used in
|
||||
// API route path segments, so they must not include route separators.
|
||||
func UserSecretNameValid(s string) error {
|
||||
if strings.TrimSpace(s) == "" {
|
||||
return xerrors.New("Name is required.")
|
||||
}
|
||||
|
||||
if strings.TrimSpace(s) != s {
|
||||
return xerrors.New("Name must not have leading or trailing whitespace.")
|
||||
}
|
||||
|
||||
if strings.ContainsAny(s, "/?#") {
|
||||
return xerrors.New("Name must not contain /, ?, or #.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UserSecretEnvNameValid validates an environment variable name for
|
||||
// a user secret. Empty string is allowed (means no env injection).
|
||||
func UserSecretEnvNameValid(s string) error {
|
||||
|
||||
@@ -9,6 +9,43 @@ import (
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
)
|
||||
|
||||
func TestUserSecretNameValid(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
wantErr bool
|
||||
errMsg string
|
||||
}{
|
||||
{name: "Simple", input: "github-token"},
|
||||
{name: "WithUnderscore", input: "github_token"},
|
||||
{name: "WithDot", input: "github.token"},
|
||||
{name: "Empty", input: "", wantErr: true, errMsg: "required"},
|
||||
{name: "WhitespaceOnly", input: " ", wantErr: true, errMsg: "required"},
|
||||
{name: "LeadingWhitespace", input: " github", wantErr: true, errMsg: "whitespace"},
|
||||
{name: "TrailingWhitespace", input: "github ", wantErr: true, errMsg: "whitespace"},
|
||||
{name: "Slash", input: "foo/bar", wantErr: true, errMsg: "must not contain"},
|
||||
{name: "Question", input: "foo?bar", wantErr: true, errMsg: "must not contain"},
|
||||
{name: "Fragment", input: "foo#bar", wantErr: true, errMsg: "must not contain"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
err := codersdk.UserSecretNameValid(tt.input)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
if tt.errMsg != "" {
|
||||
assert.Contains(t, err.Error(), tt.errMsg)
|
||||
}
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUserSecretEnvNameValid(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user