diff --git a/apps/workspace-engine/pkg/db/models.go b/apps/workspace-engine/pkg/db/models.go index 64c890a0b..e8daedcde 100644 --- a/apps/workspace-engine/pkg/db/models.go +++ b/apps/workspace-engine/pkg/db/models.go @@ -248,6 +248,12 @@ type ResourceProvider struct { Metadata map[string]string } +type ResourceVariable struct { + ResourceID uuid.UUID + Key string + Value []byte +} + type System struct { ID uuid.UUID Name string diff --git a/apps/workspace-engine/pkg/db/queries/resource_variables.sql b/apps/workspace-engine/pkg/db/queries/resource_variables.sql new file mode 100644 index 000000000..e8d49ff8c --- /dev/null +++ b/apps/workspace-engine/pkg/db/queries/resource_variables.sql @@ -0,0 +1,29 @@ +-- name: GetResourceVariable :one +SELECT resource_id, key, value +FROM resource_variable +WHERE resource_id = $1 AND key = $2; + +-- name: UpsertResourceVariable :exec +INSERT INTO resource_variable (resource_id, key, value) +VALUES ($1, $2, $3) +ON CONFLICT (resource_id, key) DO UPDATE +SET value = EXCLUDED.value; + +-- name: DeleteResourceVariable :exec +DELETE FROM resource_variable +WHERE resource_id = $1 AND key = $2; + +-- name: ListResourceVariablesByResourceID :many +SELECT resource_id, key, value +FROM resource_variable +WHERE resource_id = $1; + +-- name: DeleteResourceVariablesByResourceID :exec +DELETE FROM resource_variable +WHERE resource_id = $1; + +-- name: ListResourceVariablesByWorkspaceID :many +SELECT rv.resource_id, rv.key, rv.value +FROM resource_variable rv +INNER JOIN resource r ON r.id = rv.resource_id +WHERE r.workspace_id = $1; diff --git a/apps/workspace-engine/pkg/db/queries/schema.sql b/apps/workspace-engine/pkg/db/queries/schema.sql index 2e430ae45..200fbddc6 100644 --- a/apps/workspace-engine/pkg/db/queries/schema.sql +++ b/apps/workspace-engine/pkg/db/queries/schema.sql @@ -231,4 +231,11 @@ CREATE TABLE user_approval_record ( reason TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), PRIMARY KEY (version_id, user_id, environment_id) +); + +CREATE TABLE resource_variable ( + resource_id UUID NOT NULL REFERENCES resource(id) ON DELETE CASCADE, + key TEXT NOT NULL, + value JSONB NOT NULL, + PRIMARY KEY (resource_id, key) ); \ No newline at end of file diff --git a/apps/workspace-engine/pkg/db/resource_variables.sql.go b/apps/workspace-engine/pkg/db/resource_variables.sql.go new file mode 100644 index 000000000..3dc9b7b41 --- /dev/null +++ b/apps/workspace-engine/pkg/db/resource_variables.sql.go @@ -0,0 +1,126 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: resource_variables.sql + +package db + +import ( + "context" + + "github.com/google/uuid" +) + +const deleteResourceVariable = `-- name: DeleteResourceVariable :exec +DELETE FROM resource_variable +WHERE resource_id = $1 AND key = $2 +` + +type DeleteResourceVariableParams struct { + ResourceID uuid.UUID + Key string +} + +func (q *Queries) DeleteResourceVariable(ctx context.Context, arg DeleteResourceVariableParams) error { + _, err := q.db.Exec(ctx, deleteResourceVariable, arg.ResourceID, arg.Key) + return err +} + +const deleteResourceVariablesByResourceID = `-- name: DeleteResourceVariablesByResourceID :exec +DELETE FROM resource_variable +WHERE resource_id = $1 +` + +func (q *Queries) DeleteResourceVariablesByResourceID(ctx context.Context, resourceID uuid.UUID) error { + _, err := q.db.Exec(ctx, deleteResourceVariablesByResourceID, resourceID) + return err +} + +const getResourceVariable = `-- name: GetResourceVariable :one +SELECT resource_id, key, value +FROM resource_variable +WHERE resource_id = $1 AND key = $2 +` + +type GetResourceVariableParams struct { + ResourceID uuid.UUID + Key string +} + +func (q *Queries) GetResourceVariable(ctx context.Context, arg GetResourceVariableParams) (ResourceVariable, error) { + row := q.db.QueryRow(ctx, getResourceVariable, arg.ResourceID, arg.Key) + var i ResourceVariable + err := row.Scan(&i.ResourceID, &i.Key, &i.Value) + return i, err +} + +const listResourceVariablesByResourceID = `-- name: ListResourceVariablesByResourceID :many +SELECT resource_id, key, value +FROM resource_variable +WHERE resource_id = $1 +` + +func (q *Queries) ListResourceVariablesByResourceID(ctx context.Context, resourceID uuid.UUID) ([]ResourceVariable, error) { + rows, err := q.db.Query(ctx, listResourceVariablesByResourceID, resourceID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ResourceVariable + for rows.Next() { + var i ResourceVariable + if err := rows.Scan(&i.ResourceID, &i.Key, &i.Value); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const listResourceVariablesByWorkspaceID = `-- name: ListResourceVariablesByWorkspaceID :many +SELECT rv.resource_id, rv.key, rv.value +FROM resource_variable rv +INNER JOIN resource r ON r.id = rv.resource_id +WHERE r.workspace_id = $1 +` + +func (q *Queries) ListResourceVariablesByWorkspaceID(ctx context.Context, workspaceID uuid.UUID) ([]ResourceVariable, error) { + rows, err := q.db.Query(ctx, listResourceVariablesByWorkspaceID, workspaceID) + if err != nil { + return nil, err + } + defer rows.Close() + var items []ResourceVariable + for rows.Next() { + var i ResourceVariable + if err := rows.Scan(&i.ResourceID, &i.Key, &i.Value); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const upsertResourceVariable = `-- name: UpsertResourceVariable :exec +INSERT INTO resource_variable (resource_id, key, value) +VALUES ($1, $2, $3) +ON CONFLICT (resource_id, key) DO UPDATE +SET value = EXCLUDED.value +` + +type UpsertResourceVariableParams struct { + ResourceID uuid.UUID + Key string + Value []byte +} + +func (q *Queries) UpsertResourceVariable(ctx context.Context, arg UpsertResourceVariableParams) error { + _, err := q.db.Exec(ctx, upsertResourceVariable, arg.ResourceID, arg.Key, arg.Value) + return err +} diff --git a/apps/workspace-engine/pkg/db/sqlc.yaml b/apps/workspace-engine/pkg/db/sqlc.yaml index 36fed78c5..de6ffe761 100644 --- a/apps/workspace-engine/pkg/db/sqlc.yaml +++ b/apps/workspace-engine/pkg/db/sqlc.yaml @@ -20,6 +20,7 @@ sql: - queries/changelog.sql - queries/policies.sql - queries/user_approval_records.sql + - queries/resource_variables.sql database: uri: "postgresql://ctrlplane:ctrlplane@127.0.0.1:5432/ctrlplane?sslmode=disable" gen: @@ -98,3 +99,8 @@ sql: - column: "policy_rule_verification.metrics" go_type: type: "[]byte" + + # ResourceVariable + - column: "resource_variable.value" + go_type: + type: "[]byte" diff --git a/apps/workspace-engine/pkg/persistence/integration_test.go b/apps/workspace-engine/pkg/persistence/integration_test.go index ded0367f3..461a415e9 100644 --- a/apps/workspace-engine/pkg/persistence/integration_test.go +++ b/apps/workspace-engine/pkg/persistence/integration_test.go @@ -469,7 +469,7 @@ func TestPersistence_AllEntityTypes(t *testing.T) { _, ok = testStore.Repo().ResourceProviders().Get(resourceProvider.Id) assert.True(t, ok, "ResourceProvider should be restored") - _, ok = testStore.Repo().ResourceVariables.Get(resourceVariable.ID()) + _, ok = testStore.Repo().ResourceVariables().Get(resourceVariable.ID()) assert.True(t, ok, "ResourceVariable should be restored") _, ok = testStore.Repo().Deployments().Get(deploymentId) diff --git a/apps/workspace-engine/pkg/workspace/store/repository/db/repo.go b/apps/workspace-engine/pkg/workspace/store/repository/db/repo.go index 4ab8c7684..fbabaca01 100644 --- a/apps/workspace-engine/pkg/workspace/store/repository/db/repo.go +++ b/apps/workspace-engine/pkg/workspace/store/repository/db/repo.go @@ -11,6 +11,7 @@ import ( "workspace-engine/pkg/workspace/store/repository/db/releases" "workspace-engine/pkg/workspace/store/repository/db/resourceproviders" "workspace-engine/pkg/workspace/store/repository/db/resources" + "workspace-engine/pkg/workspace/store/repository/db/resourcevariables" "workspace-engine/pkg/workspace/store/repository/db/systemdeployments" "workspace-engine/pkg/workspace/store/repository/db/systemenvironments" "workspace-engine/pkg/workspace/store/repository/db/systems" @@ -30,6 +31,7 @@ type DBRepo struct { systemEnvironments repository.SystemEnvironmentRepo policies repository.PolicyRepo userApprovalRecords repository.UserApprovalRecordRepo + resourceVariables repository.ResourceVariableRepo } func (d *DBRepo) DeploymentVersions() repository.DeploymentVersionRepo { @@ -80,6 +82,10 @@ func (d *DBRepo) UserApprovalRecords() repository.UserApprovalRecordRepo { return d.userApprovalRecords } +func (d *DBRepo) ResourceVariables() repository.ResourceVariableRepo { + return d.resourceVariables +} + func NewDBRepo(ctx context.Context, workspaceID string) *DBRepo { return &DBRepo{ deploymentVersions: deploymentversions.NewRepo(ctx, workspaceID), @@ -94,5 +100,6 @@ func NewDBRepo(ctx context.Context, workspaceID string) *DBRepo { systemEnvironments: systemenvironments.NewRepo(ctx), policies: policies.NewRepo(ctx, workspaceID), userApprovalRecords: userapprovalrecords.NewRepo(ctx), + resourceVariables: resourcevariables.NewRepo(ctx, workspaceID), } } diff --git a/apps/workspace-engine/pkg/workspace/store/repository/db/resourcevariables/mapper.go b/apps/workspace-engine/pkg/workspace/store/repository/db/resourcevariables/mapper.go new file mode 100644 index 000000000..afbe9f245 --- /dev/null +++ b/apps/workspace-engine/pkg/workspace/store/repository/db/resourcevariables/mapper.go @@ -0,0 +1,49 @@ +package resourcevariables + +import ( + "fmt" + "workspace-engine/pkg/db" + "workspace-engine/pkg/oapi" + + "github.com/google/uuid" +) + +func ToOapi(row db.ResourceVariable) *oapi.ResourceVariable { + value := oapi.Value{} + _ = value.UnmarshalJSON(row.Value) + + return &oapi.ResourceVariable{ + ResourceId: row.ResourceID.String(), + Key: row.Key, + Value: value, + } +} + +func ToUpsertParams(e *oapi.ResourceVariable) (db.UpsertResourceVariableParams, error) { + rid, err := uuid.Parse(e.ResourceId) + if err != nil { + return db.UpsertResourceVariableParams{}, fmt.Errorf("parse resource_id: %w", err) + } + + valueBytes, err := e.Value.MarshalJSON() + if err != nil { + return db.UpsertResourceVariableParams{}, fmt.Errorf("marshal value: %w", err) + } + + return db.UpsertResourceVariableParams{ + ResourceID: rid, + Key: e.Key, + Value: valueBytes, + }, nil +} + +func parseKey(key string) (uuid.UUID, string, error) { + if len(key) < 38 || key[36] != '-' { + return uuid.Nil, "", fmt.Errorf("invalid resource variable key: %q", key) + } + rid, err := uuid.Parse(key[:36]) + if err != nil { + return uuid.Nil, "", fmt.Errorf("parse resource_id from key: %w", err) + } + return rid, key[37:], nil +} diff --git a/apps/workspace-engine/pkg/workspace/store/repository/db/resourcevariables/repo.go b/apps/workspace-engine/pkg/workspace/store/repository/db/resourcevariables/repo.go new file mode 100644 index 000000000..0cdafe08e --- /dev/null +++ b/apps/workspace-engine/pkg/workspace/store/repository/db/resourcevariables/repo.go @@ -0,0 +1,133 @@ +package resourcevariables + +import ( + "context" + "fmt" + "workspace-engine/pkg/db" + "workspace-engine/pkg/oapi" + + "github.com/charmbracelet/log" + "github.com/google/uuid" +) + +type Repo struct { + ctx context.Context + workspaceID string +} + +func NewRepo(ctx context.Context, workspaceID string) *Repo { + return &Repo{ctx: ctx, workspaceID: workspaceID} +} + +func (r *Repo) Get(key string) (*oapi.ResourceVariable, bool) { + resourceID, varKey, err := parseKey(key) + if err != nil { + log.Warn("Failed to parse resource variable key", "key", key, "error", err) + return nil, false + } + + row, err := db.GetQueries(r.ctx).GetResourceVariable(r.ctx, db.GetResourceVariableParams{ + ResourceID: resourceID, + Key: varKey, + }) + if err != nil { + return nil, false + } + + return ToOapi(row), true +} + +func (r *Repo) Set(entity *oapi.ResourceVariable) error { + params, err := ToUpsertParams(entity) + if err != nil { + return fmt.Errorf("convert to upsert params: %w", err) + } + + return db.GetQueries(r.ctx).UpsertResourceVariable(r.ctx, params) +} + +func (r *Repo) Remove(key string) error { + resourceID, varKey, err := parseKey(key) + if err != nil { + return fmt.Errorf("parse key: %w", err) + } + + return db.GetQueries(r.ctx).DeleteResourceVariable(r.ctx, db.DeleteResourceVariableParams{ + ResourceID: resourceID, + Key: varKey, + }) +} + +func (r *Repo) Items() map[string]*oapi.ResourceVariable { + uid, err := uuid.Parse(r.workspaceID) + if err != nil { + log.Warn("Failed to parse workspace id for Items()", "id", r.workspaceID, "error", err) + return make(map[string]*oapi.ResourceVariable) + } + + rows, err := db.GetQueries(r.ctx).ListResourceVariablesByWorkspaceID(r.ctx, uid) + if err != nil { + log.Warn("Failed to list resource variables by workspace", "workspaceId", r.workspaceID, "error", err) + return make(map[string]*oapi.ResourceVariable) + } + + result := make(map[string]*oapi.ResourceVariable, len(rows)) + for _, row := range rows { + rv := ToOapi(row) + result[rv.ID()] = rv + } + return result +} + +func (r *Repo) BulkUpdate(toUpsert []*oapi.ResourceVariable, toRemove []*oapi.ResourceVariable) error { + tx, err := db.GetPool(r.ctx).Begin(r.ctx) + if err != nil { + return fmt.Errorf("begin transaction: %w", err) + } + defer tx.Rollback(r.ctx) + + q := db.New(tx) + + for _, rv := range toRemove { + rid, err := uuid.Parse(rv.ResourceId) + if err != nil { + return fmt.Errorf("parse resource_id for remove: %w", err) + } + if err := q.DeleteResourceVariable(r.ctx, db.DeleteResourceVariableParams{ + ResourceID: rid, + Key: rv.Key, + }); err != nil { + return fmt.Errorf("delete resource variable %s-%s: %w", rv.ResourceId, rv.Key, err) + } + } + + for _, rv := range toUpsert { + params, err := ToUpsertParams(rv) + if err != nil { + return fmt.Errorf("convert to upsert params: %w", err) + } + if err := q.UpsertResourceVariable(r.ctx, params); err != nil { + return fmt.Errorf("upsert resource variable %s-%s: %w", rv.ResourceId, rv.Key, err) + } + } + + return tx.Commit(r.ctx) +} + +func (r *Repo) GetByResourceID(resourceID string) ([]*oapi.ResourceVariable, error) { + rid, err := uuid.Parse(resourceID) + if err != nil { + return nil, fmt.Errorf("parse resource_id: %w", err) + } + + rows, err := db.GetQueries(r.ctx).ListResourceVariablesByResourceID(r.ctx, rid) + if err != nil { + return nil, fmt.Errorf("list resource variables: %w", err) + } + + result := make([]*oapi.ResourceVariable, len(rows)) + for i, row := range rows { + result[i] = ToOapi(row) + } + return result, nil +} diff --git a/apps/workspace-engine/pkg/workspace/store/repository/interfaces.go b/apps/workspace-engine/pkg/workspace/store/repository/interfaces.go index ae4da04a1..21f2f3c7e 100644 --- a/apps/workspace-engine/pkg/workspace/store/repository/interfaces.go +++ b/apps/workspace-engine/pkg/workspace/store/repository/interfaces.go @@ -117,6 +117,16 @@ type PolicyRepo interface { Items() map[string]*oapi.Policy } +// ResourceVariableRepo defines the contract for resource variable storage. +type ResourceVariableRepo interface { + Get(key string) (*oapi.ResourceVariable, bool) + Set(entity *oapi.ResourceVariable) error + Remove(key string) error + Items() map[string]*oapi.ResourceVariable + GetByResourceID(resourceID string) ([]*oapi.ResourceVariable, error) + BulkUpdate(toUpsert []*oapi.ResourceVariable, toRemove []*oapi.ResourceVariable) error +} + // UserApprovalRecordRepo defines the contract for user approval record storage. type UserApprovalRecordRepo interface { Get(key string) (*oapi.UserApprovalRecord, bool) diff --git a/apps/workspace-engine/pkg/workspace/store/repository/memory/repo.go b/apps/workspace-engine/pkg/workspace/store/repository/memory/repo.go index 937236f67..a2a9f2184 100644 --- a/apps/workspace-engine/pkg/workspace/store/repository/memory/repo.go +++ b/apps/workspace-engine/pkg/workspace/store/repository/memory/repo.go @@ -50,7 +50,7 @@ func New(wsId string) *InMemory { JobVerifications: createTypedStore[*oapi.JobVerification](router, "job_verification"), resources: createTypedStore[*oapi.Resource](router, "resource"), resourceProviders: createTypedStore[*oapi.ResourceProvider](router, "resource_provider"), - ResourceVariables: createTypedStore[*oapi.ResourceVariable](router, "resource_variable"), + resourceVariables: createTypedStore[*oapi.ResourceVariable](router, "resource_variable"), deployments: createTypedStore[*oapi.Deployment](router, "deployment"), deploymentVersions: createMemDBStore[*oapi.DeploymentVersion](router, "deployment_version", memdb), DeploymentVariables: createTypedStore[*oapi.DeploymentVariable](router, "deployment_variable"), @@ -85,7 +85,7 @@ type InMemory struct { db *memdb.MemDB resources cmap.ConcurrentMap[string, *oapi.Resource] - ResourceVariables cmap.ConcurrentMap[string, *oapi.ResourceVariable] + resourceVariables cmap.ConcurrentMap[string, *oapi.ResourceVariable] resourceProviders cmap.ConcurrentMap[string, *oapi.ResourceProvider] deployments cmap.ConcurrentMap[string, *oapi.Deployment] @@ -473,6 +473,53 @@ func (s *InMemory) UserApprovalRecords() repository.UserApprovalRecordRepo { return &userApprovalRecordRepoAdapter{store: &s.userApprovalRecords} } +type resourceVariableRepoAdapter struct { + store *cmap.ConcurrentMap[string, *oapi.ResourceVariable] +} + +func (a *resourceVariableRepoAdapter) Get(key string) (*oapi.ResourceVariable, bool) { + return a.store.Get(key) +} + +func (a *resourceVariableRepoAdapter) Set(entity *oapi.ResourceVariable) error { + a.store.Set(entity.ID(), entity) + return nil +} + +func (a *resourceVariableRepoAdapter) Remove(key string) error { + a.store.Remove(key) + return nil +} + +func (a *resourceVariableRepoAdapter) Items() map[string]*oapi.ResourceVariable { + return a.store.Items() +} + +func (a *resourceVariableRepoAdapter) BulkUpdate(toUpsert []*oapi.ResourceVariable, toRemove []*oapi.ResourceVariable) error { + for _, rv := range toRemove { + a.store.Remove(rv.ID()) + } + for _, rv := range toUpsert { + a.store.Set(rv.ID(), rv) + } + return nil +} + +func (a *resourceVariableRepoAdapter) GetByResourceID(resourceID string) ([]*oapi.ResourceVariable, error) { + var result []*oapi.ResourceVariable + for item := range a.store.IterBuffered() { + if item.Val.ResourceId == resourceID { + result = append(result, item.Val) + } + } + return result, nil +} + +// ResourceVariables implements repository.Repo. +func (s *InMemory) ResourceVariables() repository.ResourceVariableRepo { + return &resourceVariableRepoAdapter{store: &s.resourceVariables} +} + // SystemEnvironments implements repository.Repo. func (s *InMemory) SystemEnvironments() repository.SystemEnvironmentRepo { return &systemEnvironmentRepoAdapter{ diff --git a/apps/workspace-engine/pkg/workspace/store/repository/repo.go b/apps/workspace-engine/pkg/workspace/store/repository/repo.go index 59287aa8c..13629ebe2 100644 --- a/apps/workspace-engine/pkg/workspace/store/repository/repo.go +++ b/apps/workspace-engine/pkg/workspace/store/repository/repo.go @@ -13,4 +13,5 @@ type Repo interface { SystemEnvironments() SystemEnvironmentRepo Policies() PolicyRepo UserApprovalRecords() UserApprovalRecordRepo + ResourceVariables() ResourceVariableRepo } diff --git a/apps/workspace-engine/pkg/workspace/store/resource_variables.go b/apps/workspace-engine/pkg/workspace/store/resource_variables.go index 5312db984..32c20a457 100644 --- a/apps/workspace-engine/pkg/workspace/store/resource_variables.go +++ b/apps/workspace-engine/pkg/workspace/store/resource_variables.go @@ -5,88 +5,118 @@ import ( "context" "workspace-engine/pkg/oapi" - "workspace-engine/pkg/workspace/store/repository/memory" + "workspace-engine/pkg/workspace/store/repository" + + "github.com/charmbracelet/log" ) func NewResourceVariables(store *Store) *ResourceVariables { return &ResourceVariables{ - repo: store.repo, + repo: store.repo.ResourceVariables(), store: store, } } type ResourceVariables struct { - repo *memory.InMemory + repo repository.ResourceVariableRepo store *Store } +func (r *ResourceVariables) SetRepo(repo repository.ResourceVariableRepo) { + r.repo = repo +} + func (r *ResourceVariables) Upsert(ctx context.Context, resourceVariable *oapi.ResourceVariable) { - r.repo.ResourceVariables.Set(resourceVariable.ID(), resourceVariable) + if err := r.repo.Set(resourceVariable); err != nil { + log.Error("Failed to upsert resource variable", "error", err) + return + } r.store.changeset.RecordUpsert(resourceVariable) } func (r *ResourceVariables) Get(resourceId string, key string) (*oapi.ResourceVariable, bool) { - return r.repo.ResourceVariables.Get(resourceId + "-" + key) + return r.repo.Get(resourceId + "-" + key) } func (r *ResourceVariables) Remove(ctx context.Context, resourceId string, key string) { - resourceVariable, ok := r.repo.ResourceVariables.Get(resourceId + "-" + key) + resourceVariable, ok := r.repo.Get(resourceId + "-" + key) if !ok || resourceVariable == nil { return } - r.repo.ResourceVariables.Remove(resourceId + "-" + key) + if err := r.repo.Remove(resourceId + "-" + key); err != nil { + log.Error("Failed to remove resource variable", "error", err) + return + } r.store.changeset.RecordDelete(resourceVariable) } func (r *ResourceVariables) BulkUpdate(ctx context.Context, resourceId string, variables map[string]any) (bool, error) { - currentVariables := make(map[string]*oapi.ResourceVariable) - hasChanges := false + currentVars, err := r.repo.GetByResourceID(resourceId) + if err != nil { + return false, err + } - for _, variable := range r.repo.ResourceVariables.Items() { - if variable.ResourceId == resourceId { - currentVariables[variable.Key] = variable - } + currentVariables := make(map[string]*oapi.ResourceVariable, len(currentVars)) + for _, v := range currentVars { + currentVariables[v.Key] = v } + var toUpsert []*oapi.ResourceVariable + var toRemove []*oapi.ResourceVariable + for key, currentVar := range currentVariables { if _, ok := variables[key]; !ok { - hasChanges = true - r.repo.ResourceVariables.Remove(resourceId + "-" + key) - r.store.changeset.RecordDelete(currentVar) + toRemove = append(toRemove, currentVar) } } for key, value := range variables { newValue := oapi.NewValueFromLiteral(oapi.NewLiteralValue(value)) - if currentVar, exists := currentVariables[key]; !exists { - hasChanges = true - r.Upsert(ctx, &oapi.ResourceVariable{ + currentVar, exists := currentVariables[key] + + if !exists { + toUpsert = append(toUpsert, &oapi.ResourceVariable{ ResourceId: resourceId, Key: key, Value: *newValue, }) - } else { - oldBytes, err := currentVar.Value.MarshalJSON() - if err != nil { - return false, err - } - newBytes, err := newValue.MarshalJSON() - if err != nil { - return false, err - } - - if !bytes.Equal(oldBytes, newBytes) { - hasChanges = true - currentVar.Value = *newValue - r.Upsert(ctx, currentVar) - } + continue } + + oldBytes, err := currentVar.Value.MarshalJSON() + if err != nil { + return false, err + } + newBytes, err := newValue.MarshalJSON() + if err != nil { + return false, err + } + if !bytes.Equal(oldBytes, newBytes) { + currentVar.Value = *newValue + toUpsert = append(toUpsert, currentVar) + } + } + + hasChanges := len(toUpsert) > 0 || len(toRemove) > 0 + if !hasChanges { + return false, nil + } + + if err := r.repo.BulkUpdate(toUpsert, toRemove); err != nil { + return false, err + } + + for _, rv := range toRemove { + r.store.changeset.RecordDelete(rv) + } + for _, rv := range toUpsert { + r.store.changeset.RecordUpsert(rv) } - return hasChanges, nil + return true, nil } func (r *ResourceVariables) Items() map[string]*oapi.ResourceVariable { - return r.repo.ResourceVariables.Items() + return r.repo.Items() } diff --git a/apps/workspace-engine/pkg/workspace/store/resources.go b/apps/workspace-engine/pkg/workspace/store/resources.go index 4da2a462a..ffaadc2ab 100644 --- a/apps/workspace-engine/pkg/workspace/store/resources.go +++ b/apps/workspace-engine/pkg/workspace/store/resources.go @@ -143,12 +143,13 @@ func (r *Resources) ListByProviderID(providerID string) []*oapi.Resource { } func (r *Resources) Variables(resourceId string) map[string]*oapi.ResourceVariable { - variables := make(map[string]*oapi.ResourceVariable, 25) - for _, variable := range r.store.repo.ResourceVariables.Items() { - if variable.ResourceId != resourceId { - continue - } - variables[variable.Key] = variable + vars, err := r.store.ResourceVariables.repo.GetByResourceID(resourceId) + if err != nil { + return make(map[string]*oapi.ResourceVariable) + } + variables := make(map[string]*oapi.ResourceVariable, len(vars)) + for _, v := range vars { + variables[v.Key] = v } return variables } diff --git a/apps/workspace-engine/pkg/workspace/store/store.go b/apps/workspace-engine/pkg/workspace/store/store.go index 89d7247c3..1e5116878 100644 --- a/apps/workspace-engine/pkg/workspace/store/store.go +++ b/apps/workspace-engine/pkg/workspace/store/store.go @@ -124,6 +124,15 @@ func WithDBUserApprovalRecords(ctx context.Context) StoreOption { } } +// WithDBResourceVariables replaces the default in-memory ResourceVariableRepo +// with a DB-backed implementation. +func WithDBResourceVariables(ctx context.Context) StoreOption { + return func(s *Store) { + dbRepo := dbrepo.NewDBRepo(ctx, s.id) + s.ResourceVariables.SetRepo(dbRepo.ResourceVariables()) + } +} + func New(wsId string, changeset *statechange.ChangeSet[any], opts ...StoreOption) *Store { repo := memory.New(wsId) store := &Store{id: wsId, repo: repo, changeset: changeset} @@ -315,6 +324,16 @@ func (s *Store) Restore(ctx context.Context, changes persistence.Changes, setSta } } + if setStatus != nil { + setStatus("Migrating legacy resource variables") + } + for _, rv := range s.repo.ResourceVariables().Items() { + if err := s.ResourceVariables.repo.Set(rv); err != nil { + log.Warn("Failed to migrate legacy resource variable", + "key", rv.ID(), "error", err) + } + } + if setStatus != nil { setStatus("Computing release targets") } diff --git a/apps/workspace-engine/svc/workspaceconsumer/consumer.go b/apps/workspace-engine/svc/workspaceconsumer/consumer.go index a65a207bf..8528a9008 100644 --- a/apps/workspace-engine/svc/workspaceconsumer/consumer.go +++ b/apps/workspace-engine/svc/workspaceconsumer/consumer.go @@ -96,6 +96,7 @@ func (s *Service) configureManager() error { wsstore.WithDBJobAgents(bgCtx), wsstore.WithDBPolicies(bgCtx), wsstore.WithDBUserApprovalRecords(bgCtx), + wsstore.WithDBResourceVariables(bgCtx), ), ), ) diff --git a/apps/workspace-engine/test/integration/dbtest.go b/apps/workspace-engine/test/integration/dbtest.go index 1b271e96b..d76218381 100644 --- a/apps/workspace-engine/test/integration/dbtest.go +++ b/apps/workspace-engine/test/integration/dbtest.go @@ -80,6 +80,7 @@ func newDBTestWorkspace(t *testing.T, options ...WorkspaceOption) *TestWorkspace store.WithDBResources(ctx), store.WithDBPolicies(ctx), store.WithDBUserApprovalRecords(ctx), + store.WithDBResourceVariables(ctx), // store.WithDBReleases(ctx), ), ) diff --git a/packages/db/drizzle/0150_abandoned_eddie_brock.sql b/packages/db/drizzle/0150_abandoned_eddie_brock.sql new file mode 100644 index 000000000..f0da42854 --- /dev/null +++ b/packages/db/drizzle/0150_abandoned_eddie_brock.sql @@ -0,0 +1,8 @@ +CREATE TABLE "resource_variable" ( + "resource_id" uuid NOT NULL, + "key" text NOT NULL, + "value" jsonb NOT NULL, + CONSTRAINT "resource_variable_resource_id_key_pk" PRIMARY KEY("resource_id","key") +); +--> statement-breakpoint +ALTER TABLE "resource_variable" ADD CONSTRAINT "resource_variable_resource_id_resource_id_fk" FOREIGN KEY ("resource_id") REFERENCES "public"."resource"("id") ON DELETE cascade ON UPDATE no action; \ No newline at end of file diff --git a/packages/db/drizzle/meta/0150_snapshot.json b/packages/db/drizzle/meta/0150_snapshot.json new file mode 100644 index 000000000..53755cf62 --- /dev/null +++ b/packages/db/drizzle/meta/0150_snapshot.json @@ -0,0 +1,4166 @@ +{ + "id": "985e51d1-83d4-4a45-ab79-46f2a4f4eb68", + "prevId": "b64e903a-273c-4574-b965-e91e509ce092", + "version": "7", + "dialect": "postgresql", + "tables": { + "public.account": { + "name": "account", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "account_id": { + "name": "account_id", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "refresh_token": { + "name": "refresh_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token": { + "name": "access_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "access_token_expires_at": { + "name": "access_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "refresh_token_expires_at": { + "name": "refresh_token_expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "scope": { + "name": "scope", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "id_token": { + "name": "id_token", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "password": { + "name": "password", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "account_userId_idx": { + "name": "account_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "account_user_id_user_id_fk": { + "name": "account_user_id_user_id_fk", + "tableFrom": "account", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.session": { + "name": "session", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "session_token": { + "name": "session_token", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "expires": { + "name": "expires", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "ip_address": { + "name": "ip_address", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "user_agent": { + "name": "user_agent", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "session_userId_idx": { + "name": "session_userId_idx", + "columns": [ + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "session_user_id_user_id_fk": { + "name": "session_user_id_user_id_fk", + "tableFrom": "session", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "session_session_token_unique": { + "name": "session_session_token_unique", + "nullsNotDistinct": false, + "columns": ["session_token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user": { + "name": "user", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "email": { + "name": "email", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "email_verified": { + "name": "email_verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "image": { + "name": "image", + "type": "varchar(255)", + "primaryKey": false, + "notNull": false + }, + "active_workspace_id": { + "name": "active_workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false, + "default": "null" + }, + "password_hash": { + "name": "password_hash", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "null" + }, + "system_role": { + "name": "system_role", + "type": "system_role", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'user'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "user_active_workspace_id_workspace_id_fk": { + "name": "user_active_workspace_id_workspace_id_fk", + "tableFrom": "user", + "tableTo": "workspace", + "columnsFrom": ["active_workspace_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_api_key": { + "name": "user_api_key", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "varchar(255)", + "primaryKey": false, + "notNull": true + }, + "key_preview": { + "name": "key_preview", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key_hash": { + "name": "key_hash", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "key_prefix": { + "name": "key_prefix", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "user_api_key_key_prefix_key_hash_index": { + "name": "user_api_key_key_prefix_key_hash_index", + "columns": [ + { + "expression": "key_prefix", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key_hash", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "user_api_key_user_id_user_id_fk": { + "name": "user_api_key_user_id_user_id_fk", + "tableFrom": "user_api_key", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.verification": { + "name": "verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "text", + "primaryKey": true, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "verification_identifier_idx": { + "name": "verification_identifier_idx", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.changelog_entry": { + "name": "changelog_entry", + "schema": "", + "columns": { + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "entity_data": { + "name": "entity_data", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "changelog_entry_workspace_id_workspace_id_fk": { + "name": "changelog_entry_workspace_id_workspace_id_fk", + "tableFrom": "changelog_entry", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "changelog_entry_workspace_id_entity_type_entity_id_pk": { + "name": "changelog_entry_workspace_id_entity_type_entity_id_pk", + "columns": ["workspace_id", "entity_type", "entity_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.dashboard": { + "name": "dashboard", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "dashboard_workspace_id_workspace_id_fk": { + "name": "dashboard_workspace_id_workspace_id_fk", + "tableFrom": "dashboard", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.dashboard_widget": { + "name": "dashboard_widget", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "dashboard_id": { + "name": "dashboard_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "widget": { + "name": "widget", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "x": { + "name": "x", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "y": { + "name": "y", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "w": { + "name": "w", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "h": { + "name": "h", + "type": "integer", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "dashboard_widget_dashboard_id_dashboard_id_fk": { + "name": "dashboard_widget_dashboard_id_dashboard_id_fk", + "tableFrom": "dashboard_widget", + "tableTo": "dashboard", + "columnsFrom": ["dashboard_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment_trace_span": { + "name": "deployment_trace_span", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "trace_id": { + "name": "trace_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "span_id": { + "name": "span_id", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "parent_span_id": { + "name": "parent_span_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "start_time": { + "name": "start_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true + }, + "end_time": { + "name": "end_time", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "release_target_key": { + "name": "release_target_key", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "release_id": { + "name": "release_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "job_id": { + "name": "job_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "parent_trace_id": { + "name": "parent_trace_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "phase": { + "name": "phase", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "node_type": { + "name": "node_type", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "depth": { + "name": "depth", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "sequence": { + "name": "sequence", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "attributes": { + "name": "attributes", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "events": { + "name": "events", + "type": "jsonb", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "deployment_trace_span_trace_span_idx": { + "name": "deployment_trace_span_trace_span_idx", + "columns": [ + { + "expression": "trace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "span_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "deployment_trace_span_trace_id_idx": { + "name": "deployment_trace_span_trace_id_idx", + "columns": [ + { + "expression": "trace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "deployment_trace_span_parent_span_id_idx": { + "name": "deployment_trace_span_parent_span_id_idx", + "columns": [ + { + "expression": "parent_span_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "deployment_trace_span_workspace_id_idx": { + "name": "deployment_trace_span_workspace_id_idx", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "deployment_trace_span_release_target_key_idx": { + "name": "deployment_trace_span_release_target_key_idx", + "columns": [ + { + "expression": "release_target_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "deployment_trace_span_release_id_idx": { + "name": "deployment_trace_span_release_id_idx", + "columns": [ + { + "expression": "release_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "deployment_trace_span_job_id_idx": { + "name": "deployment_trace_span_job_id_idx", + "columns": [ + { + "expression": "job_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "deployment_trace_span_parent_trace_id_idx": { + "name": "deployment_trace_span_parent_trace_id_idx", + "columns": [ + { + "expression": "parent_trace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "deployment_trace_span_created_at_idx": { + "name": "deployment_trace_span_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "deployment_trace_span_phase_idx": { + "name": "deployment_trace_span_phase_idx", + "columns": [ + { + "expression": "phase", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "deployment_trace_span_node_type_idx": { + "name": "deployment_trace_span_node_type_idx", + "columns": [ + { + "expression": "node_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "deployment_trace_span_status_idx": { + "name": "deployment_trace_span_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployment_trace_span_workspace_id_workspace_id_fk": { + "name": "deployment_trace_span_workspace_id_workspace_id_fk", + "tableFrom": "deployment_trace_span", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment_version": { + "name": "deployment_version", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "tag": { + "name": "tag", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "job_agent_config": { + "name": "job_agent_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "deployment_version_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'ready'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp (3) with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "deployment_version_deployment_id_tag_index": { + "name": "deployment_version_deployment_id_tag_index", + "columns": [ + { + "expression": "deployment_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "tag", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "deployment_version_created_at_idx": { + "name": "deployment_version_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "deployment_version_workspace_id_workspace_id_fk": { + "name": "deployment_version_workspace_id_workspace_id_fk", + "tableFrom": "deployment_version", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.computed_deployment_resource": { + "name": "computed_deployment_resource", + "schema": "", + "columns": { + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "computed_deployment_resource_deployment_id_deployment_id_fk": { + "name": "computed_deployment_resource_deployment_id_deployment_id_fk", + "tableFrom": "computed_deployment_resource", + "tableTo": "deployment", + "columnsFrom": ["deployment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "computed_deployment_resource_resource_id_resource_id_fk": { + "name": "computed_deployment_resource_resource_id_resource_id_fk", + "tableFrom": "computed_deployment_resource", + "tableTo": "resource", + "columnsFrom": ["resource_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "computed_deployment_resource_deployment_id_resource_id_pk": { + "name": "computed_deployment_resource_deployment_id_resource_id_pk", + "columns": ["deployment_id", "resource_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.deployment": { + "name": "deployment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "job_agent_id": { + "name": "job_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "job_agent_config": { + "name": "job_agent_config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "job_agents": { + "name": "job_agents", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "resource_selector": { + "name": "resource_selector", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'false'" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "deployment_workspace_id_workspace_id_fk": { + "name": "deployment_workspace_id_workspace_id_fk", + "tableFrom": "deployment", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.computed_environment_resource": { + "name": "computed_environment_resource", + "schema": "", + "columns": { + "environment_id": { + "name": "environment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "resource_id": { + "name": "resource_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "computed_environment_resource_environment_id_environment_id_fk": { + "name": "computed_environment_resource_environment_id_environment_id_fk", + "tableFrom": "computed_environment_resource", + "tableTo": "environment", + "columnsFrom": ["environment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "computed_environment_resource_resource_id_resource_id_fk": { + "name": "computed_environment_resource_resource_id_resource_id_fk", + "tableFrom": "computed_environment_resource", + "tableTo": "resource", + "columnsFrom": ["resource_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "computed_environment_resource_environment_id_resource_id_pk": { + "name": "computed_environment_resource_environment_id_resource_id_pk", + "columns": ["environment_id", "resource_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.environment": { + "name": "environment", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "''" + }, + "resource_selector": { + "name": "resource_selector", + "type": "text", + "primaryKey": false, + "notNull": false, + "default": "'false'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "environment_workspace_id_workspace_id_fk": { + "name": "environment_workspace_id_workspace_id_fk", + "tableFrom": "environment", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.event": { + "name": "event", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "action": { + "name": "action", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "event_workspace_id_workspace_id_fk": { + "name": "event_workspace_id_workspace_id_fk", + "tableFrom": "event", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.resource": { + "name": "resource", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "identifier": { + "name": "identifier", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "provider_id": { + "name": "provider_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": false, + "default": "'{}'" + }, + "deleted_at": { + "name": "deleted_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + } + }, + "indexes": { + "resource_identifier_workspace_id_index": { + "name": "resource_identifier_workspace_id_index", + "columns": [ + { + "expression": "identifier", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "resource_provider_id_resource_provider_id_fk": { + "name": "resource_provider_id_resource_provider_id_fk", + "tableFrom": "resource", + "tableTo": "resource_provider", + "columnsFrom": ["provider_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + }, + "resource_workspace_id_workspace_id_fk": { + "name": "resource_workspace_id_workspace_id_fk", + "tableFrom": "resource", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.resource_schema": { + "name": "resource_schema", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version": { + "name": "version", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "json_schema": { + "name": "json_schema", + "type": "json", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "resource_schema_version_kind_workspace_id_index": { + "name": "resource_schema_version_kind_workspace_id_index", + "columns": [ + { + "expression": "version", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "resource_schema_workspace_id_workspace_id_fk": { + "name": "resource_schema_workspace_id_workspace_id_fk", + "tableFrom": "resource_schema", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.resource_provider": { + "name": "resource_provider", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + } + }, + "indexes": { + "resource_provider_workspace_id_name_index": { + "name": "resource_provider_workspace_id_name_index", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "resource_provider_workspace_id_workspace_id_fk": { + "name": "resource_provider_workspace_id_workspace_id_fk", + "tableFrom": "resource_provider", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.system": { + "name": "system", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + } + }, + "indexes": {}, + "foreignKeys": { + "system_workspace_id_workspace_id_fk": { + "name": "system_workspace_id_workspace_id_fk", + "tableFrom": "system", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.system_deployment": { + "name": "system_deployment", + "schema": "", + "columns": { + "system_id": { + "name": "system_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "system_deployment_system_id_system_id_fk": { + "name": "system_deployment_system_id_system_id_fk", + "tableFrom": "system_deployment", + "tableTo": "system", + "columnsFrom": ["system_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "system_deployment_deployment_id_deployment_id_fk": { + "name": "system_deployment_deployment_id_deployment_id_fk", + "tableFrom": "system_deployment", + "tableTo": "deployment", + "columnsFrom": ["deployment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "system_deployment_system_id_deployment_id_pk": { + "name": "system_deployment_system_id_deployment_id_pk", + "columns": ["system_id", "deployment_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.system_environment": { + "name": "system_environment", + "schema": "", + "columns": { + "system_id": { + "name": "system_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "environment_id": { + "name": "environment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "system_environment_system_id_system_id_fk": { + "name": "system_environment_system_id_system_id_fk", + "tableFrom": "system_environment", + "tableTo": "system", + "columnsFrom": ["system_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "system_environment_environment_id_environment_id_fk": { + "name": "system_environment_environment_id_environment_id_fk", + "tableFrom": "system_environment", + "tableTo": "environment", + "columnsFrom": ["environment_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "system_environment_system_id_environment_id_pk": { + "name": "system_environment_system_id_environment_id_pk", + "columns": ["system_id", "environment_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team": { + "name": "team", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "text": { + "name": "text", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "team_workspace_id_workspace_id_fk": { + "name": "team_workspace_id_workspace_id_fk", + "tableFrom": "team", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.team_member": { + "name": "team_member", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "team_id": { + "name": "team_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "team_member_team_id_user_id_index": { + "name": "team_member_team_id_user_id_index", + "columns": [ + { + "expression": "team_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "user_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "team_member_team_id_team_id_fk": { + "name": "team_member_team_id_team_id_fk", + "tableFrom": "team_member", + "tableTo": "team", + "columnsFrom": ["team_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "team_member_user_id_user_id_fk": { + "name": "team_member_user_id_user_id_fk", + "tableFrom": "team_member", + "tableTo": "user", + "columnsFrom": ["user_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.job": { + "name": "job", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "job_agent_id": { + "name": "job_agent_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + }, + "job_agent_config": { + "name": "job_agent_config", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "external_id": { + "name": "external_id", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "status": { + "name": "status", + "type": "job_status", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'pending'" + }, + "message": { + "name": "message", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "reason": { + "name": "reason", + "type": "job_reason", + "typeSchema": "public", + "primaryKey": false, + "notNull": true, + "default": "'policy_passing'" + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "started_at": { + "name": "started_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "completed_at": { + "name": "completed_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "job_created_at_idx": { + "name": "job_created_at_idx", + "columns": [ + { + "expression": "created_at", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "job_status_idx": { + "name": "job_status_idx", + "columns": [ + { + "expression": "status", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + }, + "job_external_id_idx": { + "name": "job_external_id_idx", + "columns": [ + { + "expression": "external_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "job_job_agent_id_job_agent_id_fk": { + "name": "job_job_agent_id_job_agent_id_fk", + "tableFrom": "job", + "tableTo": "job_agent", + "columnsFrom": ["job_agent_id"], + "columnsTo": ["id"], + "onDelete": "set null", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.job_metadata": { + "name": "job_metadata", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "job_id": { + "name": "job_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "job_metadata_key_job_id_index": { + "name": "job_metadata_key_job_id_index", + "columns": [ + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "job_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "job_metadata_job_id_idx": { + "name": "job_metadata_job_id_idx", + "columns": [ + { + "expression": "job_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "job_metadata_job_id_job_id_fk": { + "name": "job_metadata_job_id_job_id_fk", + "tableFrom": "job_metadata", + "tableTo": "job", + "columnsFrom": ["job_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.job_variable": { + "name": "job_variable", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "job_id": { + "name": "job_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "json", + "primaryKey": false, + "notNull": false + }, + "sensitive": { + "name": "sensitive", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + } + }, + "indexes": { + "job_variable_job_id_key_index": { + "name": "job_variable_job_id_key_index", + "columns": [ + { + "expression": "job_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "job_variable_job_id_job_id_fk": { + "name": "job_variable_job_id_job_id_fk", + "tableFrom": "job_variable", + "tableTo": "job", + "columnsFrom": ["job_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace": { + "name": "workspace", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "slug": { + "name": "slug", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "workspace_slug_unique": { + "name": "workspace_slug_unique", + "nullsNotDistinct": false, + "columns": ["slug"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_email_domain_matching": { + "name": "workspace_email_domain_matching", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "domain": { + "name": "domain", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "verified": { + "name": "verified", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "verification_code": { + "name": "verification_code", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "verification_email": { + "name": "verification_email", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "workspace_email_domain_matching_workspace_id_domain_index": { + "name": "workspace_email_domain_matching_workspace_id_domain_index", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "domain", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "workspace_email_domain_matching_workspace_id_workspace_id_fk": { + "name": "workspace_email_domain_matching_workspace_id_workspace_id_fk", + "tableFrom": "workspace_email_domain_matching", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_email_domain_matching_role_id_role_id_fk": { + "name": "workspace_email_domain_matching_role_id_role_id_fk", + "tableFrom": "workspace_email_domain_matching", + "tableTo": "role", + "columnsFrom": ["role_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.workspace_invite_token": { + "name": "workspace_invite_token", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_by": { + "name": "created_by", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "token": { + "name": "token", + "type": "uuid", + "primaryKey": false, + "notNull": true, + "default": "gen_random_uuid()" + }, + "expires_at": { + "name": "expires_at", + "type": "timestamp", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "workspace_invite_token_role_id_role_id_fk": { + "name": "workspace_invite_token_role_id_role_id_fk", + "tableFrom": "workspace_invite_token", + "tableTo": "role", + "columnsFrom": ["role_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_invite_token_workspace_id_workspace_id_fk": { + "name": "workspace_invite_token_workspace_id_workspace_id_fk", + "tableFrom": "workspace_invite_token", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + }, + "workspace_invite_token_created_by_user_id_fk": { + "name": "workspace_invite_token_created_by_user_id_fk", + "tableFrom": "workspace_invite_token", + "tableTo": "user", + "columnsFrom": ["created_by"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": { + "workspace_invite_token_token_unique": { + "name": "workspace_invite_token_token_unique", + "nullsNotDistinct": false, + "columns": ["token"] + } + }, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.entity_role": { + "name": "entity_role", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "entity_type": { + "name": "entity_type", + "type": "entity_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + }, + "entity_id": { + "name": "entity_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "scope_id": { + "name": "scope_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "scope_type": { + "name": "scope_type", + "type": "scope_type", + "typeSchema": "public", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "entity_role_role_id_entity_type_entity_id_scope_id_scope_type_index": { + "name": "entity_role_role_id_entity_type_entity_id_scope_id_scope_type_index", + "columns": [ + { + "expression": "role_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "entity_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_type", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "entity_role_role_id_role_id_fk": { + "name": "entity_role_role_id_role_id_fk", + "tableFrom": "entity_role", + "tableTo": "role", + "columnsFrom": ["role_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.role": { + "name": "role", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": false + } + }, + "indexes": {}, + "foreignKeys": { + "role_workspace_id_workspace_id_fk": { + "name": "role_workspace_id_workspace_id_fk", + "tableFrom": "role", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.role_permission": { + "name": "role_permission", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "role_id": { + "name": "role_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "permission": { + "name": "permission", + "type": "text", + "primaryKey": false, + "notNull": true + } + }, + "indexes": { + "role_permission_role_id_permission_index": { + "name": "role_permission_role_id_permission_index", + "columns": [ + { + "expression": "role_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "permission", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "role_permission_role_id_role_id_fk": { + "name": "role_permission_role_id_role_id_fk", + "tableFrom": "role_permission", + "tableTo": "role", + "columnsFrom": ["role_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.release": { + "name": "release", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "resource_id": { + "name": "resource_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "environment_id": { + "name": "environment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "deployment_id": { + "name": "deployment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "version_id": { + "name": "version_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "release_resource_id_resource_id_fk": { + "name": "release_resource_id_resource_id_fk", + "tableFrom": "release", + "tableTo": "resource", + "columnsFrom": ["resource_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "release_environment_id_environment_id_fk": { + "name": "release_environment_id_environment_id_fk", + "tableFrom": "release", + "tableTo": "environment", + "columnsFrom": ["environment_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "release_deployment_id_deployment_id_fk": { + "name": "release_deployment_id_deployment_id_fk", + "tableFrom": "release", + "tableTo": "deployment", + "columnsFrom": ["deployment_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + }, + "release_version_id_deployment_version_id_fk": { + "name": "release_version_id_deployment_version_id_fk", + "tableFrom": "release", + "tableTo": "deployment_version", + "columnsFrom": ["version_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.release_variable": { + "name": "release_variable", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "release_id": { + "name": "release_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "jsonb", + "primaryKey": false, + "notNull": true + }, + "encrypted": { + "name": "encrypted", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "release_variable_release_id_key_index": { + "name": "release_variable_release_id_key_index", + "columns": [ + { + "expression": "release_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "release_variable_release_id_release_id_fk": { + "name": "release_variable_release_id_release_id_fk", + "tableFrom": "release_variable", + "tableTo": "release", + "columnsFrom": ["release_id"], + "columnsTo": ["id"], + "onDelete": "no action", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reconcile_work_payload": { + "name": "reconcile_work_payload", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "reconcile_work_payload_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "scope_ref": { + "name": "scope_ref", + "type": "bigint", + "primaryKey": false, + "notNull": true + }, + "payload_type": { + "name": "payload_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "payload_key": { + "name": "payload_key", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "payload": { + "name": "payload", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'::jsonb" + }, + "attempt_count": { + "name": "attempt_count", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "last_error": { + "name": "last_error", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reconcile_work_payload_scope_ref_payload_type_payload_key_index": { + "name": "reconcile_work_payload_scope_ref_payload_type_payload_key_index", + "columns": [ + { + "expression": "scope_ref", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "payload_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "payload_key", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reconcile_work_payload_scope_ref_index": { + "name": "reconcile_work_payload_scope_ref_index", + "columns": [ + { + "expression": "scope_ref", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "reconcile_work_payload_scope_ref_reconcile_work_scope_id_fk": { + "name": "reconcile_work_payload_scope_ref_reconcile_work_scope_id_fk", + "tableFrom": "reconcile_work_payload", + "tableTo": "reconcile_work_scope", + "columnsFrom": ["scope_ref"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.reconcile_work_scope": { + "name": "reconcile_work_scope", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "bigint", + "primaryKey": true, + "notNull": true, + "identity": { + "type": "byDefault", + "name": "reconcile_work_scope_id_seq", + "schema": "public", + "increment": "1", + "startWith": "1", + "minValue": "1", + "maxValue": "9223372036854775807", + "cache": "1", + "cycle": false + } + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "kind": { + "name": "kind", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "scope_type": { + "name": "scope_type", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "scope_id": { + "name": "scope_id", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "''" + }, + "event_ts": { + "name": "event_ts", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "priority": { + "name": "priority", + "type": "smallint", + "primaryKey": false, + "notNull": true, + "default": 100 + }, + "not_before": { + "name": "not_before", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "claimed_by": { + "name": "claimed_by", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "claimed_until": { + "name": "claimed_until", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + }, + "updated_at": { + "name": "updated_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": { + "reconcile_work_scope_workspace_id_kind_scope_type_scope_id_index": { + "name": "reconcile_work_scope_workspace_id_kind_scope_type_scope_id_index", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_type", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "scope_id", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + }, + "reconcile_work_scope_kind_not_before_priority_event_ts_claimed_until_index": { + "name": "reconcile_work_scope_kind_not_before_priority_event_ts_claimed_until_index", + "columns": [ + { + "expression": "kind", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "not_before", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "priority", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "event_ts", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "claimed_until", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": false, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": {}, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.policy": { + "name": "policy", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "selector": { + "name": "selector", + "type": "text", + "primaryKey": false, + "notNull": true, + "default": "'true'" + }, + "metadata": { + "name": "metadata", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + }, + "priority": { + "name": "priority", + "type": "integer", + "primaryKey": false, + "notNull": true, + "default": 0 + }, + "enabled": { + "name": "enabled", + "type": "boolean", + "primaryKey": false, + "notNull": true, + "default": true + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "policy_workspace_id_workspace_id_fk": { + "name": "policy_workspace_id_workspace_id_fk", + "tableFrom": "policy", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.policy_rule_any_approval": { + "name": "policy_rule_any_approval", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "policy_id": { + "name": "policy_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "min_approvals": { + "name": "min_approvals", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "policy_rule_any_approval_policy_id_policy_id_fk": { + "name": "policy_rule_any_approval_policy_id_policy_id_fk", + "tableFrom": "policy_rule_any_approval", + "tableTo": "policy", + "columnsFrom": ["policy_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.policy_rule_deployment_dependency": { + "name": "policy_rule_deployment_dependency", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "policy_id": { + "name": "policy_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "depends_on": { + "name": "depends_on", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "policy_rule_deployment_dependency_policy_id_policy_id_fk": { + "name": "policy_rule_deployment_dependency_policy_id_policy_id_fk", + "tableFrom": "policy_rule_deployment_dependency", + "tableTo": "policy", + "columnsFrom": ["policy_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.policy_rule_deployment_window": { + "name": "policy_rule_deployment_window", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "policy_id": { + "name": "policy_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "allow_window": { + "name": "allow_window", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "duration_minutes": { + "name": "duration_minutes", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "rrule": { + "name": "rrule", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "timezone": { + "name": "timezone", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "policy_rule_deployment_window_policy_id_policy_id_fk": { + "name": "policy_rule_deployment_window_policy_id_policy_id_fk", + "tableFrom": "policy_rule_deployment_window", + "tableTo": "policy", + "columnsFrom": ["policy_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.policy_rule_environment_progression": { + "name": "policy_rule_environment_progression", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "policy_id": { + "name": "policy_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "depends_on_environment_selector": { + "name": "depends_on_environment_selector", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "maximum_age_hours": { + "name": "maximum_age_hours", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "minimum_soak_time_minutes": { + "name": "minimum_soak_time_minutes", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "minimum_success_percentage": { + "name": "minimum_success_percentage", + "type": "real", + "primaryKey": false, + "notNull": false + }, + "success_statuses": { + "name": "success_statuses", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "policy_rule_environment_progression_policy_id_policy_id_fk": { + "name": "policy_rule_environment_progression_policy_id_policy_id_fk", + "tableFrom": "policy_rule_environment_progression", + "tableTo": "policy", + "columnsFrom": ["policy_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.policy_rule_gradual_rollout": { + "name": "policy_rule_gradual_rollout", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "policy_id": { + "name": "policy_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "rollout_type": { + "name": "rollout_type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "time_scale_interval": { + "name": "time_scale_interval", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "policy_rule_gradual_rollout_policy_id_policy_id_fk": { + "name": "policy_rule_gradual_rollout_policy_id_policy_id_fk", + "tableFrom": "policy_rule_gradual_rollout", + "tableTo": "policy", + "columnsFrom": ["policy_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.policy_rule_retry": { + "name": "policy_rule_retry", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "policy_id": { + "name": "policy_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "max_retries": { + "name": "max_retries", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "backoff_seconds": { + "name": "backoff_seconds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "backoff_strategy": { + "name": "backoff_strategy", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "max_backoff_seconds": { + "name": "max_backoff_seconds", + "type": "integer", + "primaryKey": false, + "notNull": false + }, + "retry_on_statuses": { + "name": "retry_on_statuses", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "policy_rule_retry_policy_id_policy_id_fk": { + "name": "policy_rule_retry_policy_id_policy_id_fk", + "tableFrom": "policy_rule_retry", + "tableTo": "policy", + "columnsFrom": ["policy_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.policy_rule_rollback": { + "name": "policy_rule_rollback", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "policy_id": { + "name": "policy_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "on_job_statuses": { + "name": "on_job_statuses", + "type": "text[]", + "primaryKey": false, + "notNull": false + }, + "on_verification_failure": { + "name": "on_verification_failure", + "type": "boolean", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "policy_rule_rollback_policy_id_policy_id_fk": { + "name": "policy_rule_rollback_policy_id_policy_id_fk", + "tableFrom": "policy_rule_rollback", + "tableTo": "policy", + "columnsFrom": ["policy_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.policy_rule_verification": { + "name": "policy_rule_verification", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "policy_id": { + "name": "policy_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "metrics": { + "name": "metrics", + "type": "jsonb", + "primaryKey": false, + "notNull": true, + "default": "'[]'" + }, + "trigger_on": { + "name": "trigger_on", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "policy_rule_verification_policy_id_policy_id_fk": { + "name": "policy_rule_verification_policy_id_policy_id_fk", + "tableFrom": "policy_rule_verification", + "tableTo": "policy", + "columnsFrom": ["policy_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.policy_rule_version_cooldown": { + "name": "policy_rule_version_cooldown", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "policy_id": { + "name": "policy_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "interval_seconds": { + "name": "interval_seconds", + "type": "integer", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "policy_rule_version_cooldown_policy_id_policy_id_fk": { + "name": "policy_rule_version_cooldown_policy_id_policy_id_fk", + "tableFrom": "policy_rule_version_cooldown", + "tableTo": "policy", + "columnsFrom": ["policy_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.policy_rule_version_selector": { + "name": "policy_rule_version_selector", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "policy_id": { + "name": "policy_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "description": { + "name": "description", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "selector": { + "name": "selector", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": { + "policy_rule_version_selector_policy_id_policy_id_fk": { + "name": "policy_rule_version_selector_policy_id_policy_id_fk", + "tableFrom": "policy_rule_version_selector", + "tableTo": "policy", + "columnsFrom": ["policy_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.user_approval_record": { + "name": "user_approval_record", + "schema": "", + "columns": { + "version_id": { + "name": "version_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "user_id": { + "name": "user_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "environment_id": { + "name": "environment_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "status": { + "name": "status", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "reason": { + "name": "reason", + "type": "text", + "primaryKey": false, + "notNull": false + }, + "created_at": { + "name": "created_at", + "type": "timestamp with time zone", + "primaryKey": false, + "notNull": true, + "default": "now()" + } + }, + "indexes": {}, + "foreignKeys": {}, + "compositePrimaryKeys": { + "user_approval_record_version_id_user_id_environment_id_pk": { + "name": "user_approval_record_version_id_user_id_environment_id_pk", + "columns": ["version_id", "user_id", "environment_id"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.resource_variable": { + "name": "resource_variable", + "schema": "", + "columns": { + "resource_id": { + "name": "resource_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "key": { + "name": "key", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "value": { + "name": "value", + "type": "jsonb", + "primaryKey": false, + "notNull": true + } + }, + "indexes": {}, + "foreignKeys": { + "resource_variable_resource_id_resource_id_fk": { + "name": "resource_variable_resource_id_resource_id_fk", + "tableFrom": "resource_variable", + "tableTo": "resource", + "columnsFrom": ["resource_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": { + "resource_variable_resource_id_key_pk": { + "name": "resource_variable_resource_id_key_pk", + "columns": ["resource_id", "key"] + } + }, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + }, + "public.job_agent": { + "name": "job_agent", + "schema": "", + "columns": { + "id": { + "name": "id", + "type": "uuid", + "primaryKey": true, + "notNull": true, + "default": "gen_random_uuid()" + }, + "workspace_id": { + "name": "workspace_id", + "type": "uuid", + "primaryKey": false, + "notNull": true + }, + "name": { + "name": "name", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "type": { + "name": "type", + "type": "text", + "primaryKey": false, + "notNull": true + }, + "config": { + "name": "config", + "type": "json", + "primaryKey": false, + "notNull": true, + "default": "'{}'" + } + }, + "indexes": { + "job_agent_workspace_id_name_index": { + "name": "job_agent_workspace_id_name_index", + "columns": [ + { + "expression": "workspace_id", + "isExpression": false, + "asc": true, + "nulls": "last" + }, + { + "expression": "name", + "isExpression": false, + "asc": true, + "nulls": "last" + } + ], + "isUnique": true, + "concurrently": false, + "method": "btree", + "with": {} + } + }, + "foreignKeys": { + "job_agent_workspace_id_workspace_id_fk": { + "name": "job_agent_workspace_id_workspace_id_fk", + "tableFrom": "job_agent", + "tableTo": "workspace", + "columnsFrom": ["workspace_id"], + "columnsTo": ["id"], + "onDelete": "cascade", + "onUpdate": "no action" + } + }, + "compositePrimaryKeys": {}, + "uniqueConstraints": {}, + "policies": {}, + "checkConstraints": {}, + "isRLSEnabled": false + } + }, + "enums": { + "public.system_role": { + "name": "system_role", + "schema": "public", + "values": ["user", "admin"] + }, + "public.deployment_version_status": { + "name": "deployment_version_status", + "schema": "public", + "values": [ + "unspecified", + "building", + "ready", + "failed", + "rejected", + "paused" + ] + }, + "public.job_reason": { + "name": "job_reason", + "schema": "public", + "values": [ + "policy_passing", + "policy_override", + "env_policy_override", + "config_policy_override" + ] + }, + "public.job_status": { + "name": "job_status", + "schema": "public", + "values": [ + "cancelled", + "skipped", + "in_progress", + "action_required", + "pending", + "failure", + "invalid_job_agent", + "invalid_integration", + "external_run_not_found", + "successful" + ] + }, + "public.entity_type": { + "name": "entity_type", + "schema": "public", + "values": ["user", "team"] + }, + "public.scope_type": { + "name": "scope_type", + "schema": "public", + "values": [ + "deploymentVersion", + "resource", + "resourceProvider", + "workspace", + "environment", + "system", + "deployment" + ] + } + }, + "schemas": {}, + "sequences": {}, + "roles": {}, + "policies": {}, + "views": {}, + "_meta": { + "columns": {}, + "schemas": {}, + "tables": {} + } +} diff --git a/packages/db/drizzle/meta/_journal.json b/packages/db/drizzle/meta/_journal.json index 8d0c9dc64..1a9ac3a19 100644 --- a/packages/db/drizzle/meta/_journal.json +++ b/packages/db/drizzle/meta/_journal.json @@ -1051,6 +1051,13 @@ "when": 1772042525109, "tag": "0149_milky_hammerhead", "breakpoints": true + }, + { + "idx": 150, + "version": "7", + "when": 1772049867969, + "tag": "0150_abandoned_eddie_brock", + "breakpoints": true } ] } diff --git a/packages/db/src/schema/index.ts b/packages/db/src/schema/index.ts index 3982656b9..8132bb5ed 100644 --- a/packages/db/src/schema/index.ts +++ b/packages/db/src/schema/index.ts @@ -18,3 +18,4 @@ export * from "./release.js"; export * from "./reconcile.js"; export * from "./policy.js"; export * from "./user-approval-record.js"; +export * from "./resource-variable.js"; diff --git a/packages/db/src/schema/resource-variable.ts b/packages/db/src/schema/resource-variable.ts new file mode 100644 index 000000000..89111e352 --- /dev/null +++ b/packages/db/src/schema/resource-variable.ts @@ -0,0 +1,19 @@ +import { jsonb, pgTable, primaryKey, text, uuid } from "drizzle-orm/pg-core"; + +import { resource } from "./resource.js"; +import { workspace } from "./workspace.js"; + +export const resourceVariable = pgTable( + "resource_variable", + { + resourceId: uuid("resource_id") + .notNull() + .references(() => resource.id, { onDelete: "cascade" }), + key: text("key").notNull(), + value: jsonb("value").notNull(), + workspaceId: uuid("workspace_id") + .notNull() + .references(() => workspace.id, { onDelete: "cascade" }), + }, + (t) => [primaryKey({ columns: [t.resourceId, t.key] })], +);