refactor(documents): rename document owner fields
This commit is contained in:
@@ -366,7 +366,7 @@ func (s *Service) DepositItem(userID uint, itemIdentity, projectIdentity string)
|
||||
var document models.SaDocumentTree
|
||||
err = s.db.Transaction(func(tx *gorm.DB) error {
|
||||
document = models.SaDocumentTree{
|
||||
ProjectID: project.ID, CreatedBy: userID, Kind: models.DocumentKindFile,
|
||||
ProjectID: project.ID, OwnerID: userID, Kind: models.DocumentKindFile,
|
||||
Name: name, NormalizedName: strings.ToLower(name), Extension: ".md",
|
||||
MimeType: "text/markdown; charset=utf-8", Size: int64(len([]byte(body))),
|
||||
Revision: 1, ScanStatus: "not_scanned",
|
||||
|
||||
@@ -153,7 +153,7 @@ func (s *Service) createNode(userID uint, projectIdentity string, input CreateIn
|
||||
ProjectID: project.ID, ProjectIdentity: project.Identity,
|
||||
ParentID: parentID, ParentIdentity: parentIdentity,
|
||||
ParentScope: identityValue(parentIdentity),
|
||||
CreatedBy: userID, SourceInboxItemID: input.SourceInboxItemID,
|
||||
OwnerID: userID, SourceInboxItemID: input.SourceInboxItemID,
|
||||
Kind: kind, Name: name, NormalizedName: normalizeName(name),
|
||||
Extension: extension, MimeType: mimeType, Size: size, Revision: 1,
|
||||
ScanStatus: "not_scanned",
|
||||
|
||||
@@ -280,7 +280,7 @@ func createConfirmedObject(tx *gorm.DB, item models.SaInboxItem, suggestion mode
|
||||
name += ".md"
|
||||
}
|
||||
document := models.SaDocumentTree{
|
||||
ProjectID: item.ProjectID, ProjectIdentity: item.ProjectIdentity, CreatedBy: item.CreatedBy,
|
||||
ProjectID: item.ProjectID, ProjectIdentity: item.ProjectIdentity, OwnerID: item.CreatedBy,
|
||||
SourceInboxItemID: &sourceInboxItemID, Kind: models.DocumentKindFile, Name: name,
|
||||
NormalizedName: strings.ToLower(name), Extension: ".md", MimeType: "text/markdown; charset=utf-8",
|
||||
Size: int64(len([]byte(suggestion.Body))), Revision: 1, ScanStatus: "not_scanned",
|
||||
|
||||
@@ -162,7 +162,8 @@ func TestConfirmCreatesOnlySelectedSavedSuggestionsWithInboxIdentity(t *testing.
|
||||
require.Equal(t, "背景资料.md", documents[1].Name)
|
||||
for _, document := range documents {
|
||||
require.Equal(t, fixture.project.ID, document.ProjectID)
|
||||
require.Equal(t, fixture.owner.ID, document.CreatedBy)
|
||||
require.Equal(t, fixture.owner.ID, document.OwnerID)
|
||||
require.Equal(t, fixture.owner.Identity, document.OwnerIdentity)
|
||||
requireSourceInboxIdentity(t, item, document.SourceInboxItemID, document.SourceInboxItemIdentity)
|
||||
}
|
||||
var secondContent models.SaDocumentContent
|
||||
@@ -267,7 +268,7 @@ func TestRepeatedConfirmReturnsPersistedResultAfterRelatedObjectsChange(t *testi
|
||||
|
||||
sourceInboxItemID := item.ID
|
||||
extraDocument := models.SaDocumentTree{
|
||||
ProjectID: item.ProjectID, CreatedBy: fixture.owner.ID, SourceInboxItemID: &sourceInboxItemID,
|
||||
ProjectID: item.ProjectID, OwnerID: fixture.owner.ID, SourceInboxItemID: &sourceInboxItemID,
|
||||
Kind: models.DocumentKindFile, Name: "后续关联资料.md", NormalizedName: "后续关联资料.md",
|
||||
Extension: ".md", Revision: 1,
|
||||
}
|
||||
|
||||
@@ -127,8 +127,8 @@ func TestWorkspaceMatchesFrontendContract(t *testing.T) {
|
||||
require.NoError(t, database.Create(&models.SaTask{ProjectID: project.ID, CreatedBy: 1, Title: "Confirm IA", Description: "Review channel layout.", Status: "open", DueAt: &nextRun}).Error)
|
||||
require.NoError(t, database.Create(&models.SaTask{ProjectID: project.ID, CreatedBy: 1, Title: "Archive notes", Description: "Move outdated notes.", Status: "done"}).Error)
|
||||
require.NoError(t, database.Create(&models.SaAISession{ProjectID: project.ID, CreatedBy: 1, Title: "UI prototype critique", Context: "Discuss channel behavior."}).Error)
|
||||
require.NoError(t, database.Create(&models.SaDocumentTree{ProjectID: project.ID, CreatedBy: 1, Kind: models.DocumentKindFile, Name: "Workbench principles.md", NormalizedName: "workbench principles.md", Extension: ".md", Revision: 1}).Error)
|
||||
require.NoError(t, database.Create(&models.SaDocumentTree{ProjectID: project.ID, CreatedBy: 1, Kind: models.DocumentKindFile, Name: "Reference board.pdf", NormalizedName: "reference board.pdf", Extension: ".pdf", Revision: 1}).Error)
|
||||
require.NoError(t, database.Create(&models.SaDocumentTree{ProjectID: project.ID, OwnerID: 1, Kind: models.DocumentKindFile, Name: "Workbench principles.md", NormalizedName: "workbench principles.md", Extension: ".md", Revision: 1}).Error)
|
||||
require.NoError(t, database.Create(&models.SaDocumentTree{ProjectID: project.ID, OwnerID: 1, Kind: models.DocumentKindFile, Name: "Reference board.pdf", NormalizedName: "reference board.pdf", Extension: ".pdf", Revision: 1}).Error)
|
||||
require.NoError(t, database.Create(&models.SaProjectChannel{ProjectID: project.ID, Title: "Roadmap Board", Icon: "link", URL: "https://example.com/roadmap-a1", SortOrder: 7}).Error)
|
||||
require.NoError(t, database.Create(&models.SaProjectChannel{ProjectID: other.ID, Title: "Other Board", Icon: "link", URL: "https://example.com/other", SortOrder: 7}).Error)
|
||||
require.NoError(t, database.Create(&models.SaCronPlan{ProjectID: project.ID, CreatedBy: 1, Title: "Weekly inbox review reminder", Schedule: "0 9 * * 1", NextRunAt: &nextRun, Enabled: true, LastResult: "Not run yet"}).Error)
|
||||
|
||||
@@ -33,8 +33,8 @@ func TestWorkspaceUsesIdentitiesUTCTimesAndProjectScopedTags(t *testing.T) {
|
||||
inbox := models.SaInboxItem{ProjectID: project.ID, CreatedBy: 1, SourceType: "Manual", Title: "Collect notes", Body: "Turn research into tasks.", Status: "open", CreatedAt: createdAt, UpdatedAt: updatedAt}
|
||||
task := models.SaTask{ProjectID: project.ID, CreatedBy: 1, TagID: &projectTag.ID, Title: "Confirm IA", Description: "Review channel layout.", Status: "done", DueAt: &nextRun, CreatedAt: createdAt, UpdatedAt: updatedAt}
|
||||
session := models.SaAISession{ProjectID: project.ID, CreatedBy: 1, Title: "UI critique", Context: "Discuss channel behavior.", CreatedAt: createdAt, UpdatedAt: updatedAt}
|
||||
firstDocument := models.SaDocumentTree{ProjectID: project.ID, CreatedBy: 1, Kind: models.DocumentKindFile, Name: "Workbench principles.md", NormalizedName: "workbench principles.md", Extension: ".md", MimeType: "text/markdown", Revision: 1, CreatedAt: createdAt, UpdatedAt: updatedAt}
|
||||
secondDocument := models.SaDocumentTree{ProjectID: project.ID, CreatedBy: 1, Kind: models.DocumentKindFile, Name: "Reference board.pdf", NormalizedName: "reference board.pdf", Extension: ".pdf", MimeType: "application/pdf", Revision: 1, CreatedAt: createdAt, UpdatedAt: updatedAt}
|
||||
firstDocument := models.SaDocumentTree{ProjectID: project.ID, OwnerID: 1, Kind: models.DocumentKindFile, Name: "Workbench principles.md", NormalizedName: "workbench principles.md", Extension: ".md", MimeType: "text/markdown", Revision: 1, CreatedAt: createdAt, UpdatedAt: updatedAt}
|
||||
secondDocument := models.SaDocumentTree{ProjectID: project.ID, OwnerID: 1, Kind: models.DocumentKindFile, Name: "Reference board.pdf", NormalizedName: "reference board.pdf", Extension: ".pdf", MimeType: "application/pdf", Revision: 1, CreatedAt: createdAt, UpdatedAt: updatedAt}
|
||||
channel := models.SaProjectChannel{ProjectID: project.ID, Title: "Roadmap Board", Icon: "link", URL: "https://example.com/roadmap", SortOrder: 7, CreatedAt: createdAt, UpdatedAt: updatedAt}
|
||||
plan := models.SaCronPlan{ProjectID: project.ID, CreatedBy: 1, Title: "Weekly review", Schedule: "0 9 * * 1", NextRunAt: &nextRun, Enabled: true, CreatedAt: createdAt, UpdatedAt: updatedAt}
|
||||
for _, record := range []any{&inbox, &task, &session, &firstDocument, &secondDocument, &channel, &plan} {
|
||||
|
||||
@@ -116,7 +116,7 @@ func createPostgresSearchDocument(
|
||||
t.Helper()
|
||||
document.ProjectID = project.ID
|
||||
document.ProjectIdentity = project.Identity
|
||||
document.CreatedBy = ownerID
|
||||
document.OwnerID = ownerID
|
||||
document.Kind = models.DocumentKindFile
|
||||
document.Extension = ".md"
|
||||
document.MimeType = "text/markdown; charset=utf-8"
|
||||
|
||||
@@ -55,7 +55,7 @@ func TestSearchAppliesStableFairGlobalLimitAcrossResultTypes(t *testing.T) {
|
||||
document := models.SaDocumentTree{
|
||||
Identity: fmt.Sprintf("00000000-0000-7003-8000-%012d", index),
|
||||
ProjectID: project.ID,
|
||||
CreatedBy: 7,
|
||||
OwnerID: 7,
|
||||
Kind: models.DocumentKindFile,
|
||||
Name: fmt.Sprintf("稳定排序文档 %02d.md", index),
|
||||
NormalizedName: fmt.Sprintf("稳定排序文档 %02d.md", index),
|
||||
@@ -139,7 +139,7 @@ func TestSearchOnlyReturnsNavigableProjectTaskAndDocumentResults(t *testing.T) {
|
||||
func createSearchDocument(t *testing.T, database *gorm.DB, projectID, userID uint, name, markdown, identity string, updatedAt time.Time) models.SaDocumentTree {
|
||||
t.Helper()
|
||||
document := models.SaDocumentTree{
|
||||
Identity: identity, ProjectID: projectID, CreatedBy: userID, Kind: models.DocumentKindFile,
|
||||
Identity: identity, ProjectID: projectID, OwnerID: userID, Kind: models.DocumentKindFile,
|
||||
Name: name, NormalizedName: strings.ToLower(name), Extension: ".md",
|
||||
MimeType: "text/markdown", Size: int64(len([]byte(markdown))), Revision: 1,
|
||||
UpdatedAt: updatedAt,
|
||||
|
||||
@@ -29,7 +29,7 @@ func TestPostgresMovePreventsOldProjectShareFromBeingInsertedConcurrently(t *tes
|
||||
require.NoError(t, database.Create(&first).Error)
|
||||
require.NoError(t, database.Create(&second).Error)
|
||||
task := models.SaTask{ProjectID: first.ID, CreatedBy: owner.ID, Title: "Move", Status: "open"}
|
||||
document := models.SaDocumentTree{ProjectID: first.ID, CreatedBy: owner.ID, Kind: models.DocumentKindFile, Name: "Old context.md", NormalizedName: "old context.md", Extension: ".md", Revision: 1}
|
||||
document := models.SaDocumentTree{ProjectID: first.ID, OwnerID: owner.ID, Kind: models.DocumentKindFile, Name: "Old context.md", NormalizedName: "old context.md", Extension: ".md", Revision: 1}
|
||||
require.NoError(t, database.Create(&task).Error)
|
||||
require.NoError(t, database.Create(&document).Error)
|
||||
t.Cleanup(func() {
|
||||
|
||||
@@ -50,7 +50,7 @@ func TestTaskRegistrarMovesTaskByIdentityAndClearsForeignProjectTag(t *testing.T
|
||||
require.NoError(t, database.Create(&tag).Error)
|
||||
task := models.SaTask{ProjectID: first.ID, CreatedBy: 1, TagID: &tag.ID, Title: "迁移任务", Status: "open"}
|
||||
require.NoError(t, database.Create(&task).Error)
|
||||
document := models.SaDocumentTree{ProjectID: first.ID, CreatedBy: 1, Kind: models.DocumentKindFile, Name: "旧项目资料.md", NormalizedName: "旧项目资料.md", Extension: ".md", Revision: 1}
|
||||
document := models.SaDocumentTree{ProjectID: first.ID, OwnerID: 1, Kind: models.DocumentKindFile, Name: "旧项目资料.md", NormalizedName: "旧项目资料.md", Extension: ".md", Revision: 1}
|
||||
require.NoError(t, database.Create(&document).Error)
|
||||
require.NoError(t, NewService(database).ShareObject(task.ID, "document", document.ID))
|
||||
body := bytes.NewBufferString(fmt.Sprintf(`{"title":"迁移任务","description":"已移动","completed":false,"nextProjectId":%q}`, strings.ToUpper(second.Identity)))
|
||||
|
||||
@@ -24,7 +24,7 @@ func TestUpdateLocksTaskBeforeProjectValidation(t *testing.T) {
|
||||
|
||||
func TestShareObjectLocksTaskBeforeObjectValidation(t *testing.T) {
|
||||
database, _, project, task := newTaskLockFixture(t)
|
||||
document := models.SaDocumentTree{ProjectID: project.ID, CreatedBy: task.CreatedBy, Kind: models.DocumentKindFile, Name: "Context.md", NormalizedName: "context.md", Extension: ".md", Revision: 1}
|
||||
document := models.SaDocumentTree{ProjectID: project.ID, OwnerID: task.CreatedBy, Kind: models.DocumentKindFile, Name: "Context.md", NormalizedName: "context.md", Extension: ".md", Revision: 1}
|
||||
require.NoError(t, database.Create(&document).Error)
|
||||
queries := captureTaskQueryOrder(t, database)
|
||||
|
||||
@@ -39,7 +39,7 @@ func TestAssigneeOnlySeesExplicitlySharedObjects(t *testing.T) {
|
||||
database := newTestDB(t)
|
||||
assigneeID := uint(2)
|
||||
task := models.SaTask{ProjectID: 1, CreatedBy: 1, AssigneeID: &assigneeID, Title: "处理合同"}
|
||||
document := models.SaDocumentTree{ProjectID: 1, CreatedBy: 1, Kind: models.DocumentKindFile, Name: "合同背景.md", NormalizedName: "合同背景.md", Extension: ".md", Revision: 1}
|
||||
document := models.SaDocumentTree{ProjectID: 1, OwnerID: 1, Kind: models.DocumentKindFile, Name: "合同背景.md", NormalizedName: "合同背景.md", Extension: ".md", Revision: 1}
|
||||
require.NoError(t, database.Create(&task).Error)
|
||||
require.NoError(t, database.Create(&document).Error)
|
||||
service := NewService()
|
||||
@@ -68,7 +68,7 @@ func TestShareObjectRejectsUnsupportedType(t *testing.T) {
|
||||
func TestShareObjectRejectsObjectFromAnotherProject(t *testing.T) {
|
||||
database := newTestDB(t)
|
||||
task := models.SaTask{ProjectID: 1, CreatedBy: 1, Title: "Review"}
|
||||
document := models.SaDocumentTree{ProjectID: 2, CreatedBy: 1, Kind: models.DocumentKindFile, Name: "Other project.md", NormalizedName: "other project.md", Extension: ".md", Revision: 1}
|
||||
document := models.SaDocumentTree{ProjectID: 2, OwnerID: 1, Kind: models.DocumentKindFile, Name: "Other project.md", NormalizedName: "other project.md", Extension: ".md", Revision: 1}
|
||||
require.NoError(t, database.Create(&task).Error)
|
||||
require.NoError(t, database.Create(&document).Error)
|
||||
service := NewService()
|
||||
|
||||
@@ -19,8 +19,8 @@ type SaDocumentTree struct {
|
||||
ParentID *uint `gorm:"index"`
|
||||
ParentIdentity *string `gorm:"type:char(36);index"`
|
||||
ParentScope string `gorm:"type:char(36);not null;default:'';uniqueIndex:uidx_sa_document_sibling,priority:2"`
|
||||
CreatedBy uint `gorm:"index;not null"`
|
||||
CreatedByIdentity string `gorm:"type:char(36);index"`
|
||||
OwnerID uint `gorm:"index;not null"`
|
||||
OwnerIdentity string `gorm:"type:char(36);index"`
|
||||
SourceInboxItemID *uint `gorm:"index"`
|
||||
SourceInboxItemIdentity *string `gorm:"type:char(36);index"`
|
||||
Kind string `gorm:"size:16;not null"`
|
||||
|
||||
@@ -82,7 +82,7 @@ func (m *SaDocumentTree) BeforeCreate(tx *gorm.DB) error {
|
||||
if err := resolveIdentity(tx, &SaProject{}, m.ProjectID, &m.ProjectIdentity); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := resolveIdentity(tx, &SaUser{}, m.CreatedBy, &m.CreatedByIdentity); err != nil {
|
||||
if err := resolveIdentity(tx, &SaUser{}, m.OwnerID, &m.OwnerIdentity); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := resolveOptionalIdentity(tx, &SaDocumentTree{}, m.ParentID, &m.ParentIdentity); err != nil {
|
||||
|
||||
@@ -44,5 +44,21 @@ func renameLegacyTables(database *gorm.DB) error {
|
||||
return fmt.Errorf("rename legacy table %s to %s: %w", table.legacy, table.current, err)
|
||||
}
|
||||
}
|
||||
if migrator.HasTable("sa_documents") {
|
||||
for _, column := range []struct {
|
||||
legacy string
|
||||
current string
|
||||
}{
|
||||
{legacy: "created_by", current: "owner_id"},
|
||||
{legacy: "created_by_identity", current: "owner_identity"},
|
||||
} {
|
||||
if !migrator.HasColumn("sa_documents", column.legacy) || migrator.HasColumn("sa_documents", column.current) {
|
||||
continue
|
||||
}
|
||||
if err := migrator.RenameColumn("sa_documents", column.legacy, column.current); err != nil {
|
||||
return fmt.Errorf("rename sa_documents column %s to %s: %w", column.legacy, column.current, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -87,6 +87,36 @@ func TestAutoMigrateRejectsAmbiguousLegacyAndCurrentTables(t *testing.T) {
|
||||
require.EqualError(t, err, "cannot rename legacy table senlin_agent_users: target table sa_users already exists")
|
||||
}
|
||||
|
||||
func TestRenameLegacyTablesRenamesDocumentOwnerColumns(t *testing.T) {
|
||||
database, err := gorm.Open(sqlite.Open(fmt.Sprintf("file:%s?mode=memory&cache=shared", t.Name())), &gorm.Config{})
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, database.Exec(`
|
||||
CREATE TABLE sa_documents (
|
||||
id integer primary key,
|
||||
created_by integer not null,
|
||||
created_by_identity text
|
||||
)
|
||||
`).Error)
|
||||
require.NoError(t, database.Exec(`
|
||||
INSERT INTO sa_documents (id, created_by, created_by_identity)
|
||||
VALUES (1, 7, 'owner-identity')
|
||||
`).Error)
|
||||
|
||||
require.NoError(t, renameLegacyTables(database))
|
||||
require.False(t, database.Migrator().HasColumn("sa_documents", "created_by"))
|
||||
require.False(t, database.Migrator().HasColumn("sa_documents", "created_by_identity"))
|
||||
require.True(t, database.Migrator().HasColumn("sa_documents", "owner_id"))
|
||||
require.True(t, database.Migrator().HasColumn("sa_documents", "owner_identity"))
|
||||
|
||||
var stored struct {
|
||||
OwnerID uint
|
||||
OwnerIdentity string
|
||||
}
|
||||
require.NoError(t, database.Table("sa_documents").Where("id = ?", 1).Take(&stored).Error)
|
||||
require.Equal(t, uint(7), stored.OwnerID)
|
||||
require.Equal(t, "owner-identity", stored.OwnerIdentity)
|
||||
}
|
||||
|
||||
func projectIdentifiers(projects []SaProject) []string {
|
||||
result := make([]string, 0, len(projects))
|
||||
for _, project := range projects {
|
||||
|
||||
@@ -266,7 +266,7 @@ func firstOrCreateDocument(tx *gorm.DB, projectID, userID uint, name, markdown s
|
||||
return err
|
||||
}
|
||||
document = models.SaDocumentTree{
|
||||
ProjectID: projectID, CreatedBy: userID, Kind: models.DocumentKindFile,
|
||||
ProjectID: projectID, OwnerID: userID, Kind: models.DocumentKindFile,
|
||||
Name: name, NormalizedName: strings.ToLower(name), Extension: ".md",
|
||||
MimeType: "text/markdown; charset=utf-8", Size: int64(len([]byte(markdown))),
|
||||
Revision: 1, ScanStatus: "not_scanned",
|
||||
|
||||
Reference in New Issue
Block a user