Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions apps/workspace-engine/pkg/db/models.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions apps/workspace-engine/pkg/db/queries/schema.sql
Original file line number Diff line number Diff line change
Expand Up @@ -221,4 +221,14 @@ CREATE TABLE policy_rule_version_selector (
description TEXT,
selector TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE user_approval_record (
version_id UUID NOT NULL,
user_id UUID NOT NULL,
environment_id UUID NOT NULL,
status TEXT NOT NULL,
reason TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (version_id, user_id, environment_id)
);
Comment on lines +226 to 234
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add a lookup index for approver queries.

Line 233 defines PK order (version_id, user_id, environment_id), which does not efficiently support reads by (version_id, environment_id[, status]). This will degrade as approval rows grow.

💡 Proposed schema addition
 CREATE TABLE user_approval_record (
     version_id UUID NOT NULL,
     user_id UUID NOT NULL,
     environment_id UUID NOT NULL,
     status TEXT NOT NULL,
     reason TEXT,
     created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
     PRIMARY KEY (version_id, user_id, environment_id)
 );
+
+CREATE INDEX user_approval_record_version_env_status_idx
+    ON user_approval_record (version_id, environment_id, status);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
CREATE TABLE user_approval_record (
version_id UUID NOT NULL,
user_id UUID NOT NULL,
environment_id UUID NOT NULL,
status TEXT NOT NULL,
reason TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (version_id, user_id, environment_id)
);
CREATE TABLE user_approval_record (
version_id UUID NOT NULL,
user_id UUID NOT NULL,
environment_id UUID NOT NULL,
status TEXT NOT NULL,
reason TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
PRIMARY KEY (version_id, user_id, environment_id)
);
CREATE INDEX user_approval_record_version_env_status_idx
ON user_approval_record (version_id, environment_id, status);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/workspace-engine/pkg/db/queries/schema.sql` around lines 226 - 234, The
primary key on user_approval_record is (version_id, user_id, environment_id)
which doesn't efficiently support reads by (version_id, environment_id[,
status]); add a dedicated lookup index on user_approval_record to cover those
queries (at minimum on (version_id, environment_id) and preferably including
status for filtered lookups) so reads by version+environment (and optional
status) are index-backed; create the index using a non-blocking approach (e.g.,
CONCURRENTLY) and name it clearly (e.g.,
idx_user_approval_record_by_version_env[_status]) to locate it later.

25 changes: 25 additions & 0 deletions apps/workspace-engine/pkg/db/queries/user_approval_records.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
-- name: GetUserApprovalRecord :one
SELECT version_id, user_id, environment_id, status, reason, created_at
FROM user_approval_record
WHERE version_id = $1 AND user_id = $2 AND environment_id = $3;

-- name: UpsertUserApprovalRecord :exec
INSERT INTO user_approval_record (version_id, user_id, environment_id, status, reason, created_at)
VALUES ($1, $2, $3, $4, $5, COALESCE(sqlc.narg('created_at')::timestamptz, NOW()))
ON CONFLICT (version_id, user_id, environment_id) DO UPDATE
SET status = EXCLUDED.status, reason = EXCLUDED.reason;

-- name: DeleteUserApprovalRecord :exec
DELETE FROM user_approval_record
WHERE version_id = $1 AND user_id = $2 AND environment_id = $3;

-- name: ListApprovedRecordsByVersionAndEnvironment :many
SELECT version_id, user_id, environment_id, status, reason, created_at
FROM user_approval_record
WHERE version_id = $1 AND environment_id = $2 AND status = 'approved'
ORDER BY created_at ASC;

-- name: ListUserApprovalRecordsByVersionID :many
SELECT version_id, user_id, environment_id, status, reason, created_at
FROM user_approval_record
WHERE version_id = $1;
1 change: 1 addition & 0 deletions apps/workspace-engine/pkg/db/sqlc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ sql:
- queries/releases.sql
- queries/changelog.sql
- queries/policies.sql
- queries/user_approval_records.sql
database:
uri: "postgresql://ctrlplane:ctrlplane@127.0.0.1:5432/ctrlplane?sslmode=disable"
gen:
Expand Down
155 changes: 155 additions & 0 deletions apps/workspace-engine/pkg/db/user_approval_records.sql.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions apps/workspace-engine/pkg/persistence/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ func TestPersistence_AllEntityTypes(t *testing.T) {
_, ok = testStore.Repo().GithubEntities.Get(githubEntityKey)
assert.True(t, ok, "GithubEntity should be restored")

_, ok = testStore.Repo().UserApprovalRecords.Get(userApprovalRecord.Key())
_, ok = testStore.Repo().UserApprovalRecords().Get(userApprovalRecord.Key())
assert.True(t, ok, "UserApprovalRecord should be restored")
}

Expand Down Expand Up @@ -757,13 +757,13 @@ func TestUserApprovalRecordKeyIncludesEnvironment(t *testing.T) {

assert.NotEqual(t, recordOne.Key(), recordTwo.Key())

_, ok := testStore.Repo().UserApprovalRecords.Get(recordOne.Key())
_, ok := testStore.Repo().UserApprovalRecords().Get(recordOne.Key())
assert.True(t, ok, "first user approval record should be stored")

_, ok = testStore.Repo().UserApprovalRecords.Get(recordTwo.Key())
_, ok = testStore.Repo().UserApprovalRecords().Get(recordTwo.Key())
assert.True(t, ok, "second user approval record should be stored")

assert.Equal(t, 2, len(testStore.Repo().UserApprovalRecords.Items()))
assert.Equal(t, 2, len(testStore.Repo().UserApprovalRecords().Items()))
}

// Helper function to create string pointers
Expand Down
51 changes: 29 additions & 22 deletions apps/workspace-engine/pkg/workspace/store/repository/db/repo.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,20 +14,22 @@ import (
"workspace-engine/pkg/workspace/store/repository/db/systemdeployments"
"workspace-engine/pkg/workspace/store/repository/db/systemenvironments"
"workspace-engine/pkg/workspace/store/repository/db/systems"
"workspace-engine/pkg/workspace/store/repository/db/userapprovalrecords"
)

type DBRepo struct {
deploymentVersions repository.DeploymentVersionRepo
deployments repository.DeploymentRepo
environments repository.EnvironmentRepo
resources repository.ResourceRepo
systems repository.SystemRepo
jobAgents repository.JobAgentRepo
resourceProviders repository.ResourceProviderRepo
releases repository.ReleaseRepo
systemDeployments repository.SystemDeploymentRepo
systemEnvironments repository.SystemEnvironmentRepo
policies repository.PolicyRepo
deploymentVersions repository.DeploymentVersionRepo
deployments repository.DeploymentRepo
environments repository.EnvironmentRepo
resources repository.ResourceRepo
systems repository.SystemRepo
jobAgents repository.JobAgentRepo
resourceProviders repository.ResourceProviderRepo
releases repository.ReleaseRepo
systemDeployments repository.SystemDeploymentRepo
systemEnvironments repository.SystemEnvironmentRepo
policies repository.PolicyRepo
userApprovalRecords repository.UserApprovalRecordRepo
}

func (d *DBRepo) DeploymentVersions() repository.DeploymentVersionRepo {
Expand Down Expand Up @@ -74,18 +76,23 @@ func (d *DBRepo) Policies() repository.PolicyRepo {
return d.policies
}

func (d *DBRepo) UserApprovalRecords() repository.UserApprovalRecordRepo {
return d.userApprovalRecords
}

func NewDBRepo(ctx context.Context, workspaceID string) *DBRepo {
return &DBRepo{
deploymentVersions: deploymentversions.NewRepo(ctx, workspaceID),
deployments: deployments.NewRepo(ctx, workspaceID),
environments: environments.NewRepo(ctx, workspaceID),
resources: resources.NewRepo(ctx, workspaceID),
systems: systems.NewRepo(ctx, workspaceID),
jobAgents: jobagents.NewRepo(ctx, workspaceID),
resourceProviders: resourceproviders.NewRepo(ctx, workspaceID),
releases: releases.NewRepo(ctx, workspaceID),
systemDeployments: systemdeployments.NewRepo(ctx),
systemEnvironments: systemenvironments.NewRepo(ctx),
policies: policies.NewRepo(ctx, workspaceID),
deploymentVersions: deploymentversions.NewRepo(ctx, workspaceID),
deployments: deployments.NewRepo(ctx, workspaceID),
environments: environments.NewRepo(ctx, workspaceID),
resources: resources.NewRepo(ctx, workspaceID),
systems: systems.NewRepo(ctx, workspaceID),
jobAgents: jobagents.NewRepo(ctx, workspaceID),
resourceProviders: resourceproviders.NewRepo(ctx, workspaceID),
releases: releases.NewRepo(ctx, workspaceID),
systemDeployments: systemdeployments.NewRepo(ctx),
systemEnvironments: systemenvironments.NewRepo(ctx),
policies: policies.NewRepo(ctx, workspaceID),
userApprovalRecords: userapprovalrecords.NewRepo(ctx),
}
}
Loading
Loading