mirror of
https://github.com/coder/coder.git
synced 2026-06-03 13:08:25 +00:00
5a8d0016a5
> Mux updated this PR on behalf of Mike. ## Stack Context This PR is the storage, permissions, API, and SDK layer for experimental personal skills. #25362 has landed on `main`, so this branch is restacked directly on `main`. Stack order: 1. #25363 storage, permissions, API, and SDK 2. #25365 API test coverage 3. #25366 chattool and chatd integration 4. #25066 settings UI and docs 5. #25386 personal skills slash menu ## What? Adds the `user_skills` database table, generated queries, RBAC resources and scopes, audit resource handling, experimental user-scoped CRUD endpoints, SDK types, and generated API/site types. Follow-up review and restack fixes: - Enforce a bounded personal skill description in parser and database constraints. - Return `403 Forbidden` for unauthorized create and update attempts. - Return explicit conflict responses when soft-deleted users are targeted. - Keep user admins out of personal skills, while site owners can read and delete but not create or update. - Document trigger-raised constraint names and keep schema constants covered by tests. - Reuse `UserSkillMetadata` in the full `UserSkill` SDK response type. - Generate user skill IDs in Go instead of relying on a database default. - Rebase on latest `main` and renumber the user skills migration to `000502_user_skills`. ## Why? Personal skills need durable user-owned storage with owner authorization, limited site-owner moderation, and a hidden API surface before chatd can consume them. ## Validation - `make gen` - `go test ./coderd/database -run '^TestUserSkillSchemaConstants$' -count=1` - `go test ./coderd/database/dbauthz -run '^TestMethodTestSuite/TestUserSkills$' -count=1` - `go test ./coderd -run '^TestPatchUserSkill$' -count=1` - `go test ./codersdk ./coderd/database/db2sdk` - `make lint` - pre-commit hook on `97fd58108d`
63 lines
1.7 KiB
Go
63 lines
1.7 KiB
Go
package database_test
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/coder/coder/v2/coderd/database"
|
|
"github.com/coder/coder/v2/coderd/database/dbtestutil"
|
|
"github.com/coder/coder/v2/coderd/x/skills"
|
|
"github.com/coder/coder/v2/testutil"
|
|
)
|
|
|
|
func TestUserSkillSchemaConstants(t *testing.T) {
|
|
t.Parallel()
|
|
if testing.Short() {
|
|
t.SkipNow()
|
|
}
|
|
|
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
|
_, _, sqlDB := dbtestutil.NewDBWithSQLDB(t)
|
|
var triggerDef string
|
|
err := sqlDB.QueryRowContext(ctx,
|
|
`SELECT pg_get_functiondef('enforce_user_skills_per_user_limit'::regproc)`,
|
|
).Scan(&triggerDef)
|
|
require.NoError(t, err)
|
|
require.Contains(t, triggerDef, fmt.Sprintf(
|
|
"skill_limit constant int := %d",
|
|
skills.MaxPersonalSkillsPerUser,
|
|
))
|
|
|
|
constraints := map[database.CheckConstraint]string{
|
|
database.CheckUserSkillsNameSize: fmt.Sprintf(
|
|
"octet_length(name) <= %d",
|
|
skills.MaxPersonalSkillNameBytes,
|
|
),
|
|
database.CheckUserSkillsNameFormat: "name ~ '^[a-z0-9]+(-[a-z0-9]+)*$'::text",
|
|
database.CheckUserSkillsDescriptionSize: fmt.Sprintf(
|
|
"octet_length(description) <= %d",
|
|
skills.MaxPersonalSkillDescriptionBytes,
|
|
),
|
|
database.CheckUserSkillsContentSize: fmt.Sprintf(
|
|
"octet_length(content) <= %d",
|
|
skills.MaxPersonalSkillSizeBytes,
|
|
),
|
|
}
|
|
for constraint, expected := range constraints {
|
|
t.Run(string(constraint), func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := testutil.Context(t, testutil.WaitMedium)
|
|
var constraintDef string
|
|
err := sqlDB.QueryRowContext(ctx,
|
|
`SELECT pg_get_constraintdef(oid) FROM pg_constraint WHERE conname = $1`,
|
|
constraint,
|
|
).Scan(&constraintDef)
|
|
require.NoError(t, err)
|
|
require.Contains(t, constraintDef, expected)
|
|
})
|
|
}
|
|
}
|