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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions backend/internal/db/db_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,91 @@ func TestSyncSchemaAddsWorkspaceTeamModel(t *testing.T) {
require.Equal(t, workspace.Name, loadedProject.Workspace.Name)
}

func TestProjectDomainTablesCarryWorkspaceID(t *testing.T) {
database, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err)
require.NoError(t, syncSchema(database))

for _, tc := range []struct {
model any
column string
}{
{&models.CollabDocument{}, "workspace_id"},
{&models.CollabDocumentState{}, "workspace_id"},
{&models.CollabDocumentUpdateBatch{}, "workspace_id"},
{&models.ProjectPlatformPublication{}, "workspace_id"},
{&models.ProjectActivity{}, "workspace_id"},
{&models.ProjectComment{}, "workspace_id"},
{&models.ProjectVersion{}, "workspace_id"},
{&models.ProjectShareLink{}, "workspace_id"},
{&models.PublishEvent{}, "workspace_id"},
{&models.ScheduledPublication{}, "workspace_id"},
{&models.MediaAsset{}, "workspace_id"},
{&models.MediaAssetUsage{}, "workspace_id"},
{&models.ExtensionCallbackToken{}, "workspace_id"},
{&models.ExtensionExecutionEvent{}, "workspace_id"},
{&models.AIContextSnapshot{}, "workspace_id"},
{&models.AIGrowthOptimizationRun{}, "workspace_id"},
{&models.AIProposal{}, "workspace_id"},
{&models.AIDraftingSession{}, "workspace_id"},
} {
require.True(t, database.Migrator().HasColumn(tc.model, tc.column), "%T", tc.model)
}
}

func TestProjectDomainRowsDeriveWorkspaceID(t *testing.T) {
database, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err)
require.NoError(t, syncSchema(database))

owner := models.User{Username: "tenant-owner", Email: "tenant-owner@example.com"}
require.NoError(t, database.Create(&owner).Error)
workspace := models.Workspace{
OwnerUserID: owner.ID,
Name: "Tenant workspace",
Slug: "tenant-workspace",
}
require.NoError(t, database.Create(&workspace).Error)
project := models.Project{
UserID: owner.ID,
WorkspaceID: &workspace.ID,
Title: "Tenant project",
SourceContent: "content",
Status: models.ProjectStatusReady,
}
require.NoError(t, database.Create(&project).Error)
document := models.CollabDocument{
OwnerUserID: owner.ID,
Title: "Tenant document",
}
require.NoError(t, database.Create(&document).Error)

publication := models.ProjectPlatformPublication{
ProjectID: project.ID,
Platform: "wechat",
Status: models.PublicationStatusDraft,
}
require.NoError(t, database.Create(&publication).Error)
require.Equal(t, workspace.ID, publication.WorkspaceID)

activity := models.ProjectActivity{
ProjectID: project.ID,
ActorUserID: owner.ID,
EventType: models.ProjectActivityContentSaved,
Metadata: []byte(`{}`),
}
require.NoError(t, database.Create(&activity).Error)
require.Equal(t, workspace.ID, activity.WorkspaceID)

state := models.CollabDocumentState{
DocumentID: document.ID,
YDocState: []byte("state"),
StateSizeBytes: 5,
}
require.NoError(t, database.Create(&state).Error)
require.Equal(t, models.PersonalWorkspaceID(owner.ID), state.WorkspaceID)
}

func TestSyncSchemaAddsArchiveScanIndexes(t *testing.T) {
database, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{})
require.NoError(t, err)
Expand Down
2 changes: 2 additions & 0 deletions backend/internal/db/hash_partitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ var collabUpdateBatchHashPartitionedTable = hashPartitionedTable{
CREATE TABLE IF NOT EXISTS collab_document_update_batches (
id bigserial NOT NULL,
document_id uuid NOT NULL,
workspace_id uuid NOT NULL,
from_seq bigint NOT NULL,
to_seq bigint NOT NULL,
update_payload bytea NOT NULL,
Expand All @@ -39,6 +40,7 @@ var collabUpdateBatchHashPartitionedTable = hashPartitionedTable{
columns: []string{
"id",
"document_id",
"workspace_id",
"from_seq",
"to_seq",
"update_payload",
Expand Down
6 changes: 6 additions & 0 deletions backend/internal/db/monthly_partitions.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ var monthlyEventPartitionedTables = []monthlyPartitionedTable{
CREATE TABLE IF NOT EXISTS publish_events (
id uuid NOT NULL,
publication_id uuid NOT NULL,
workspace_id uuid NOT NULL,
project_id uuid NOT NULL,
user_id uuid NOT NULL,
platform text NOT NULL,
Expand All @@ -46,6 +47,7 @@ var monthlyEventPartitionedTables = []monthlyPartitionedTable{
columns: []string{
"id",
"publication_id",
"workspace_id",
"project_id",
"user_id",
"platform",
Expand All @@ -67,6 +69,7 @@ var monthlyEventPartitionedTables = []monthlyPartitionedTable{
CREATE TABLE IF NOT EXISTS extension_execution_events (
id uuid NOT NULL,
callback_token_id uuid NOT NULL,
workspace_id uuid NOT NULL,
execution_id text NOT NULL,
project_id uuid NOT NULL,
user_id uuid NOT NULL,
Expand All @@ -85,6 +88,7 @@ var monthlyEventPartitionedTables = []monthlyPartitionedTable{
columns: []string{
"id",
"callback_token_id",
"workspace_id",
"execution_id",
"project_id",
"user_id",
Expand All @@ -104,6 +108,7 @@ var monthlyEventPartitionedTables = []monthlyPartitionedTable{
createSQL: `
CREATE TABLE IF NOT EXISTS project_activities (
id uuid NOT NULL,
workspace_id uuid NOT NULL,
project_id uuid NOT NULL,
actor_user_id uuid NOT NULL,
target_user_id uuid,
Expand All @@ -115,6 +120,7 @@ var monthlyEventPartitionedTables = []monthlyPartitionedTable{
`,
columns: []string{
"id",
"workspace_id",
"project_id",
"actor_user_id",
"target_user_id",
Expand Down
8 changes: 8 additions & 0 deletions backend/internal/handlers/dashboard_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ func setupHandlerTestDB(t *testing.T) *gorm.DB {

require.NoError(t, db.Exec(`CREATE TABLE collab_documents (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL,
owner_user_id TEXT NOT NULL,
title TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'active',
Expand All @@ -148,6 +149,7 @@ func setupHandlerTestDB(t *testing.T) *gorm.DB {

require.NoError(t, db.Exec(`CREATE TABLE project_activities (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL,
project_id TEXT NOT NULL,
actor_user_id TEXT NOT NULL,
target_user_id TEXT,
Expand All @@ -158,6 +160,7 @@ func setupHandlerTestDB(t *testing.T) *gorm.DB {

require.NoError(t, db.Exec(`CREATE TABLE project_comments (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL,
project_id TEXT NOT NULL,
author_id TEXT NOT NULL,
body TEXT NOT NULL,
Expand All @@ -170,6 +173,7 @@ func setupHandlerTestDB(t *testing.T) *gorm.DB {

require.NoError(t, db.Exec(`CREATE TABLE project_versions (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL,
project_id TEXT NOT NULL,
created_by TEXT NOT NULL,
version_number INTEGER NOT NULL,
Expand All @@ -183,6 +187,7 @@ func setupHandlerTestDB(t *testing.T) *gorm.DB {

require.NoError(t, db.Exec(`CREATE TABLE project_share_links (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL,
project_id TEXT NOT NULL,
created_by TEXT NOT NULL,
token_hash TEXT NOT NULL UNIQUE,
Expand Down Expand Up @@ -235,6 +240,7 @@ func setupHandlerTestDB(t *testing.T) *gorm.DB {

require.NoError(t, db.Exec(`CREATE TABLE project_platform_publications (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL,
project_id TEXT NOT NULL,
platform TEXT NOT NULL,
platform_account_id TEXT,
Expand Down Expand Up @@ -286,6 +292,7 @@ func setupHandlerTestDB(t *testing.T) *gorm.DB {

require.NoError(t, db.Exec(`CREATE TABLE extension_callback_tokens (
id TEXT PRIMARY KEY,
workspace_id TEXT NOT NULL,
execution_id TEXT NOT NULL,
project_id TEXT NOT NULL,
user_id TEXT NOT NULL,
Expand All @@ -299,6 +306,7 @@ func setupHandlerTestDB(t *testing.T) *gorm.DB {
require.NoError(t, db.Exec(`CREATE TABLE extension_execution_events (
id TEXT NOT NULL,
callback_token_id TEXT NOT NULL,
workspace_id TEXT NOT NULL,
execution_id TEXT NOT NULL,
project_id TEXT NOT NULL,
user_id TEXT NOT NULL,
Expand Down
22 changes: 21 additions & 1 deletion backend/internal/models/collab.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const (

type CollabDocument struct {
ID uuid.UUID `gorm:"type:uuid;primaryKey"`
WorkspaceID uuid.UUID `gorm:"type:uuid;not null;index:idx_collab_documents_workspace_updated,priority:1"`
OwnerUserID uuid.UUID `gorm:"type:uuid;not null;index:idx_collab_documents_owner_updated,priority:1"`
Title string `gorm:"not null"`
Status string `gorm:"not null;default:'active'"`
Expand All @@ -26,7 +27,7 @@ type CollabDocument struct {
LastEditedBy *uuid.UUID `gorm:"type:uuid"`
LastEditedAt *time.Time
CreatedAt time.Time `gorm:"not null"`
UpdatedAt time.Time `gorm:"not null;index:idx_collab_documents_owner_updated,priority:2,sort:desc"`
UpdatedAt time.Time `gorm:"not null;index:idx_collab_documents_owner_updated,priority:2,sort:desc;index:idx_collab_documents_workspace_updated,priority:2,sort:desc"`
DeletedAt gorm.DeletedAt `gorm:"index"`

Owner User `gorm:"foreignKey:OwnerUserID;references:ID"`
Expand All @@ -48,6 +49,7 @@ type CollabDocumentCollaborator struct {

type CollabDocumentState struct {
DocumentID uuid.UUID `gorm:"type:uuid;primaryKey;not null"`
WorkspaceID uuid.UUID `gorm:"type:uuid;not null;index"`
YDocState []byte `gorm:"type:bytea;not null"`
StateVector []byte `gorm:"type:bytea"`
CompactedUntilSeq int64 `gorm:"not null;default:0"`
Expand All @@ -60,6 +62,7 @@ type CollabDocumentState struct {
type CollabDocumentUpdateBatch struct {
ID int64 `gorm:"primaryKey;autoIncrement:false"`
DocumentID uuid.UUID `gorm:"type:uuid;primaryKey;not null;uniqueIndex:ux_collab_update_batch_doc_seq,priority:1;index:idx_collab_update_batches_doc_seq,priority:1"`
WorkspaceID uuid.UUID `gorm:"type:uuid;not null;index"`
FromSeq int64 `gorm:"not null;uniqueIndex:ux_collab_update_batch_doc_seq,priority:2"`
ToSeq int64 `gorm:"not null;uniqueIndex:ux_collab_update_batch_doc_seq,priority:3;index:idx_collab_update_batches_doc_seq,priority:2,sort:desc"`
UpdatePayload []byte `gorm:"type:bytea;not null"`
Expand All @@ -76,5 +79,22 @@ func (d *CollabDocument) BeforeCreate(_ *gorm.DB) (err error) {
if d.ID == uuid.Nil {
d.ID = uuid.New()
}
if d.WorkspaceID == uuid.Nil && d.OwnerUserID != uuid.Nil {
d.WorkspaceID = PersonalWorkspaceID(d.OwnerUserID)
}
return
}

func (s *CollabDocumentState) BeforeCreate(tx *gorm.DB) (err error) {
if s.WorkspaceID == uuid.Nil {
s.WorkspaceID = deriveWorkspaceIDFromDocument(tx, s.DocumentID)
}
return
}

func (b *CollabDocumentUpdateBatch) BeforeCreate(tx *gorm.DB) (err error) {
if b.WorkspaceID == uuid.Nil {
b.WorkspaceID = deriveWorkspaceIDFromDocument(tx, b.DocumentID)
}
return
}
Loading
Loading