From 9d02cc65f6a305c2ec585b0c716f49e996bf3d33 Mon Sep 17 00:00:00 2001 From: Michael Jerger Date: Thu, 13 Mar 2025 13:28:00 +0100 Subject: [PATCH] add migration for normalized_federated_uri --- .../federated_user.yaml | 5 ++ .../federation_host.yaml | 5 ++ .../user.yaml | 3 + models/migrations/migrations.go | 2 + models/migrations/v1_23/v305.go | 54 ++++++++++++++ models/migrations/v1_23/v305_test.go | 74 +++++++++++++++++++ 6 files changed, 143 insertions(+) create mode 100644 models/migrations/fixtures/Test_MigrateNormalizedFederatedURI/federated_user.yaml create mode 100644 models/migrations/fixtures/Test_MigrateNormalizedFederatedURI/federation_host.yaml create mode 100644 models/migrations/fixtures/Test_MigrateNormalizedFederatedURI/user.yaml create mode 100644 models/migrations/v1_23/v305.go create mode 100644 models/migrations/v1_23/v305_test.go diff --git a/models/migrations/fixtures/Test_MigrateNormalizedFederatedURI/federated_user.yaml b/models/migrations/fixtures/Test_MigrateNormalizedFederatedURI/federated_user.yaml new file mode 100644 index 0000000000..4eb525e810 --- /dev/null +++ b/models/migrations/fixtures/Test_MigrateNormalizedFederatedURI/federated_user.yaml @@ -0,0 +1,5 @@ +- + id: 1 + user_id: 3 + federation_host_id: 1 + external_id: "18" \ No newline at end of file diff --git a/models/migrations/fixtures/Test_MigrateNormalizedFederatedURI/federation_host.yaml b/models/migrations/fixtures/Test_MigrateNormalizedFederatedURI/federation_host.yaml new file mode 100644 index 0000000000..46eeba9c69 --- /dev/null +++ b/models/migrations/fixtures/Test_MigrateNormalizedFederatedURI/federation_host.yaml @@ -0,0 +1,5 @@ +- + id: 1 + host_fqdn: "my.host.x" + software_name: forgejo + latest_activity: 2024-04-26 14:14:50 \ No newline at end of file diff --git a/models/migrations/fixtures/Test_MigrateNormalizedFederatedURI/user.yaml b/models/migrations/fixtures/Test_MigrateNormalizedFederatedURI/user.yaml new file mode 100644 index 0000000000..f6d8a182b3 --- /dev/null +++ b/models/migrations/fixtures/Test_MigrateNormalizedFederatedURI/user.yaml @@ -0,0 +1,3 @@ +- + id: 3 + normalized_federated_uri: "https://my.host.x/api/activitypub/user-id/18" \ No newline at end of file diff --git a/models/migrations/migrations.go b/models/migrations/migrations.go index 1674af08cd..0e38de10b4 100644 --- a/models/migrations/migrations.go +++ b/models/migrations/migrations.go @@ -366,6 +366,8 @@ func prepareMigrationTasks() []*migration { // Migration to Forgejo v10 newMigration(303, "Gitea last drop", v1_23.GiteaLastDrop), newMigration(304, "Migrate `secret` column to store keying material", forgejo_migrations.MigrateTwoFactorToKeying), + + newMigration(305, "Migrate `User.NormalizedFederatedURI` column to extract port & schema into FederatedHost", v1_23.MigrateNormalizedFederatedURI), } return preparedMigrations } diff --git a/models/migrations/v1_23/v305.go b/models/migrations/v1_23/v305.go new file mode 100644 index 0000000000..ff2c1a4189 --- /dev/null +++ b/models/migrations/v1_23/v305.go @@ -0,0 +1,54 @@ +// Copyright 2025 The Forgejo Authors. All rights reserved. +// SPDX-License-Identifier: MIT + +package v1_23 //nolint + +import ( + "code.gitea.io/gitea/models/migrations/base" + "xorm.io/xorm" +) + +func MigrateNormalizedFederatedURI(x *xorm.Engine) error { + // New Fields + type FederatedUser struct { + ID int64 `xorm:"pk autoincr"` + UserID int64 `xorm:"NOT NULL"` + ExternalID string `xorm:"UNIQUE(federation_user_mapping) NOT NULL"` + FederationHostID int64 `xorm:"UNIQUE(federation_user_mapping) NOT NULL"` + NormalizedOriginalURL string + } + type User struct { + NormalizedFederatedURI string + } + // TODO: add new fields to FederationHost + if err := x.Sync(new(User), new(FederatedUser)); err != nil { + return err + } + + // Migrate User.NormalizedFederatedURI -> FederatedUser.NormalizedOriginalUrl + sessMigration := x.NewSession() + defer sessMigration.Close() + if err := sessMigration.Begin(); err != nil { + return err + } + if _, err := sessMigration.Exec("UPDATE `federated_user` SET `normalized_original_url` = (SELECT normalized_federated_uri FROM `user` WHERE `user`.id = federated_user.user_id)"); err != nil { + return err + } + if err := sessMigration.Commit(); err != nil { + return err + } + + // Migrate (Port, Schema) FederatedUser.NormalizedOriginalUrl -> FederationHost.(Port, Schema) + // TODO + + // Drop User.NormalizedFederatedURI field in extra transaction + sessSchema := x.NewSession() + defer sessSchema.Close() + if err := sessSchema.Begin(); err != nil { + return err + } + if err := base.DropTableColumns(sessSchema, "user", "normalized_federated_uri"); err != nil { + return err + } + return sessSchema.Commit() +} diff --git a/models/migrations/v1_23/v305_test.go b/models/migrations/v1_23/v305_test.go new file mode 100644 index 0000000000..f87a10b6a5 --- /dev/null +++ b/models/migrations/v1_23/v305_test.go @@ -0,0 +1,74 @@ +// Copyright 2025 The Forgejo Authors. +// SPDX-License-Identifier: GPL-3.0-or-later + +package v1_23 //nolint + +import ( + "testing" + "time" + + migration_tests "code.gitea.io/gitea/models/migrations/test" + "code.gitea.io/gitea/modules/timeutil" + + "github.com/stretchr/testify/require" + "xorm.io/xorm/schemas" +) + +func Test_MigrateNormalizedFederatedURI(t *testing.T) { + // Old structs + type User struct { + ID int64 `xorm:"pk autoincr"` + NormalizedFederatedURI string + } + type FederatedUser struct { + ID int64 `xorm:"pk autoincr"` + UserID int64 `xorm:"NOT NULL"` + ExternalID string `xorm:"UNIQUE(federation_user_mapping) NOT NULL"` + FederationHostID int64 `xorm:"UNIQUE(federation_user_mapping) NOT NULL"` + } + type FederationHost struct { + ID int64 `xorm:"pk autoincr"` + HostFqdn string `xorm:"host_fqdn UNIQUE INDEX VARCHAR(255) NOT NULL"` + SoftwareName string `xorm:"NOT NULL"` + LatestActivity time.Time `xorm:"NOT NULL"` + Created timeutil.TimeStamp `xorm:"created"` + Updated timeutil.TimeStamp `xorm:"updated"` + } + + // Prepare TestEnv + x, deferable := migration_tests.PrepareTestEnv(t, 0, + new(User), + new(FederatedUser), + new(FederationHost), + ) + defer deferable() + if x == nil || t.Failed() { + return + } + + // test for expected results + getColumn := func() *schemas.Column { + tables, err := x.DBMetas() + require.NoError(t, err) + var table *schemas.Table + for _, elem := range tables { + if elem.Name == "user" { + table = elem + break + } + } + require.Equal(t, "user", table.Name) + return table.GetColumn("normalized_federated_uri") + } + + require.NotNil(t, getColumn(), "NormalizedFederatedURI column exists") + require.NoError(t, MigrateNormalizedFederatedURI(x)) + require.Nil(t, getColumn(), "NormalizedFederatedURI column was deleted") + // idempotent + require.NoError(t, MigrateNormalizedFederatedURI(x)) + + // TODO: Find a good way to test + // res, err := x.Query("SELECT normalized_original_url FROM federated_user WHERE user_id= ?", 3) + // require.NoError(t, err) + // assert.EqualValues(t, "https://my.host.x/api/activitypub/user-id/18", string(res[0]["normalized_original_url"])) +}