From 680db23ebc6030c3eaf40269c02d5154e4a011a9 Mon Sep 17 00:00:00 2001 From: Aleksandr Tarasov Date: Mon, 4 Dec 2023 12:27:29 +0300 Subject: [PATCH] Draft Tags implementation (#18) --- .github/workflows/release.yml | 1 + api/pbuf-registry/v1/entities.proto | 3 + api/pbuf-registry/v1/registry.proto | 6 + cmd/main.go | 35 ++- cmd/root.go | 64 ++++++ docker-compose.dev.yml | 20 +- docker-compose.yml | 19 +- gen/pbuf-registry/v1/entities.pb.go | 33 ++- gen/pbuf-registry/v1/registry.pb.go | 246 +++++++++++---------- gen/pbuf-registry/v1/registry.swagger.json | 15 ++ go.mod | 7 +- go.sum | 23 +- internal/background/compaction.go | 37 ++++ internal/config/config.go | 6 + internal/config/config.yaml | 7 +- internal/data/registry.go | 212 +++++++++++++++--- internal/data/registry_test.go | 211 +++++++++++++++++- internal/mocks/auth_middleware.go | 6 +- internal/mocks/compaction_daemon.go | 42 ++++ internal/mocks/registry_repository.go | 125 ++++++++++- internal/server/registry.go | 33 ++- migrations/20231130160944_draft_tags.sql | 17 ++ 22 files changed, 997 insertions(+), 171 deletions(-) create mode 100644 cmd/root.go create mode 100644 internal/background/compaction.go create mode 100644 internal/mocks/compaction_daemon.go create mode 100644 migrations/20231130160944_draft_tags.sql diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fca78af..3ecf2af 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -5,6 +5,7 @@ on: tags: - "v[0-9]+.[0-9]+.[0-9]+" - "v[0-9]+.[0-9]+.[0-9]+-rc.[0-9]+" + - "v[0-9]+.[0-9]+.[0-9]+-wip.[0-9]+" permissions: contents: write diff --git a/api/pbuf-registry/v1/entities.proto b/api/pbuf-registry/v1/entities.proto index 5b19e46..a895817 100644 --- a/api/pbuf-registry/v1/entities.proto +++ b/api/pbuf-registry/v1/entities.proto @@ -14,6 +14,9 @@ message Module { // The tags of the module. repeated string tags = 3; + + // The draft tags of the module. + repeated string draft_tags = 4; } // ProtoFile is a proto file registered in the registry. diff --git a/api/pbuf-registry/v1/registry.proto b/api/pbuf-registry/v1/registry.proto index f6753d0..7fd82ca 100644 --- a/api/pbuf-registry/v1/registry.proto +++ b/api/pbuf-registry/v1/registry.proto @@ -103,6 +103,9 @@ message ListModulesResponse { message GetModuleRequest { // The name of the module to retrieve. string name = 1; + + // Include draft tags or not + bool include_draft_tags = 2; } // PullModuleRequest is the request message for PullModule. @@ -142,6 +145,9 @@ message PushModuleRequest { // Dependencies repeated Dependency dependencies = 4; + + // Is tag a draft + bool is_draft = 5; } // DeleteModuleRequest is the request message for DeleteModule. diff --git a/cmd/main.go b/cmd/main.go index d56c3c7..cf44c89 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -7,6 +7,7 @@ import ( "github.com/go-kratos/kratos/v2" "github.com/go-kratos/kratos/v2/log" "github.com/jackc/pgx/v5/pgxpool" + "github.com/pbufio/pbuf-registry/internal/background" "github.com/pbufio/pbuf-registry/internal/config" "github.com/pbufio/pbuf-registry/internal/data" "github.com/pbufio/pbuf-registry/internal/server" @@ -22,6 +23,15 @@ var ( id, _ = os.Hostname() ) +type Launcher struct { + config *config.Config + + mainApp *kratos.App + debugApp *kratos.App + + compactionDaemon background.CompactionDaemon +} + func main() { config.NewLoader().MustLoad() @@ -51,8 +61,29 @@ func main() { ), ) - err = app.Run() + debugApp := kratos.New( + kratos.ID(id), + kratos.Name(Name), + kratos.Version(Version), + kratos.Metadata(map[string]string{}), + kratos.Logger(logger), + kratos.Server( + server.NewDebugServer(&config.Cfg.Server, logger), + ), + ) + + launcher := &Launcher{ + config: config.Cfg, + + mainApp: app, + debugApp: debugApp, + + compactionDaemon: background.NewCompactionDaemon(registryRepository, logger), + } + + err = CreateRootCommand(launcher).Execute() if err != nil { - logHelper.Errorf("failed to run application: %v", err) + logHelper.Errorf("failed to execute command: %v", err) + return } } diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000..689efba --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,64 @@ +package main + +import ( + "time" + + "github.com/go-co-op/gocron" + "github.com/go-kratos/kratos/v2/log" + "github.com/spf13/cobra" +) + +func CreateRootCommand(launcher *Launcher) *cobra.Command { + rootCommand := &cobra.Command{ + Use: "pbuf-registry", + Short: "Default command to launch main application", + RunE: func(cmd *cobra.Command, args []string) error { + err := launcher.mainApp.Run() + if err != nil { + return err + } + + return nil + }, + } + + rootCommand.AddCommand(CreateCompactionDaemon(launcher)) + + return rootCommand +} + +func CreateCompactionDaemon(launcher *Launcher) *cobra.Command { + compactionDaemonCommand := &cobra.Command{ + Use: "compaction", + Short: "Run compaction daemon", + Run: func(cmd *cobra.Command, args []string) { + s := gocron.NewScheduler(time.UTC) + + // start every hour + _, err := s.Cron(launcher.config.Daemons.Compaction.CronSchedule).Do(func() { + err := launcher.compactionDaemon.Run() + if err != nil { + log.Fatalf("failed to run compaction daemon: %v", err) + } + }) + + if err != nil { + log.Fatalf("failed to create cron job: %v", err) + } + + // start the scheduler + s.StartAsync() + + err = launcher.debugApp.Run() + if err != nil { + log.Fatalf("failed to run debug app: %v", err) + } + + s.Stop() + + log.Infof("Compaction daemon stopped") + }, + } + + return compactionDaemonCommand +} diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index ba847ae..1dd5b8b 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -32,4 +32,22 @@ services: environment: DATA_DATABASE_DSN: "postgres://pbuf:pbuf@db:5432/pbuf_registry?sslmode=disable" command: > - sh -c "/app/pbuf-migrations && /app/pbuf-registry" \ No newline at end of file + sh -c "/app/pbuf-migrations && /app/pbuf-registry" + pbuf-registry-compaction: + build: + context: . + restart: always + depends_on: + - db + - pbuf-registry + ports: + - "8083:8082" + healthcheck: + test: wget -O - http://localhost:8082/healthz || exit 1 + interval: 5s + timeout: 10s + retries: 5 + environment: + DATA_DATABASE_DSN: "postgres://pbuf:pbuf@db:5432/pbuf_registry?sslmode=disable" + command: > + sh -c "/app/pbuf-registry compaction" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index cf7ee3e..244a162 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -19,7 +19,7 @@ services: timeout: 10s retries: 5 pbuf-registry: - image: ghcr.io/pbufio/registry:v0.3.0 + image: ghcr.io/pbufio/registry:v0.4.0-wip.1 restart: always depends_on: - db @@ -48,6 +48,23 @@ services: SERVER_STATIC_TOKEN: ${SERVER_STATIC_TOKEN} command: > sh -c "/app/pbuf-migrations && /app/pbuf-registry" + pbuf-registry-compaction: + image: ghcr.io/pbufio/registry:v0.4.0-wip.1 + restart: always + depends_on: + - db + - pbuf-registry + ports: + - "127.0.0.1:8083:8082" + healthcheck: + test: wget -O - http://localhost:8082/healthz || exit 1 + interval: 5s + timeout: 10s + retries: 5 + environment: + DATA_DATABASE_DSN: "postgres://pbuf:pbuf@db:5432/pbuf_registry?sslmode=disable" + command: > + sh -c "/app/pbuf-registry compaction" networks: internal: \ No newline at end of file diff --git a/gen/pbuf-registry/v1/entities.pb.go b/gen/pbuf-registry/v1/entities.pb.go index 8566935..4cfec12 100644 --- a/gen/pbuf-registry/v1/entities.pb.go +++ b/gen/pbuf-registry/v1/entities.pb.go @@ -32,6 +32,8 @@ type Module struct { Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` // The tags of the module. Tags []string `protobuf:"bytes,3,rep,name=tags,proto3" json:"tags,omitempty"` + // The draft tags of the module. + DraftTags []string `protobuf:"bytes,4,rep,name=draft_tags,json=draftTags,proto3" json:"draft_tags,omitempty"` } func (x *Module) Reset() { @@ -87,6 +89,13 @@ func (x *Module) GetTags() []string { return nil } +func (x *Module) GetDraftTags() []string { + if x != nil { + return x.DraftTags + } + return nil +} + // ProtoFile is a proto file registered in the registry. type ProtoFile struct { state protoimpl.MessageState @@ -209,20 +218,22 @@ var file_pbuf_registry_v1_entities_proto_rawDesc = []byte{ 0x0a, 0x1f, 0x70, 0x62, 0x75, 0x66, 0x2d, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x76, 0x31, 0x2f, 0x65, 0x6e, 0x74, 0x69, 0x74, 0x69, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0f, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, - 0x76, 0x31, 0x22, 0x40, 0x0a, 0x06, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x0e, 0x0a, 0x02, + 0x76, 0x31, 0x22, 0x5f, 0x0a, 0x06, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, - 0x74, 0x61, 0x67, 0x73, 0x22, 0x41, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x46, 0x69, 0x6c, - 0x65, 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, - 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x32, 0x0a, 0x0a, 0x44, 0x65, 0x70, 0x65, 0x6e, - 0x64, 0x65, 0x6e, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x42, 0x18, 0x5a, 0x16, 0x70, - 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x61, 0x70, 0x69, 0x2f, - 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x74, 0x61, 0x67, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x64, 0x72, 0x61, 0x66, 0x74, 0x5f, 0x74, 0x61, + 0x67, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, 0x64, 0x72, 0x61, 0x66, 0x74, 0x54, + 0x61, 0x67, 0x73, 0x22, 0x41, 0x0a, 0x09, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x46, 0x69, 0x6c, 0x65, + 0x12, 0x1a, 0x0a, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, + 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, + 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x22, 0x32, 0x0a, 0x0a, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, + 0x65, 0x6e, 0x63, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x42, 0x18, 0x5a, 0x16, 0x70, 0x62, + 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, + 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gen/pbuf-registry/v1/registry.pb.go b/gen/pbuf-registry/v1/registry.pb.go index 86d6048..c9ed149 100644 --- a/gen/pbuf-registry/v1/registry.pb.go +++ b/gen/pbuf-registry/v1/registry.pb.go @@ -153,6 +153,8 @@ type GetModuleRequest struct { // The name of the module to retrieve. Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // Include draft tags or not + IncludeDraftTags bool `protobuf:"varint,2,opt,name=include_draft_tags,json=includeDraftTags,proto3" json:"include_draft_tags,omitempty"` } func (x *GetModuleRequest) Reset() { @@ -194,6 +196,13 @@ func (x *GetModuleRequest) GetName() string { return "" } +func (x *GetModuleRequest) GetIncludeDraftTags() bool { + if x != nil { + return x.IncludeDraftTags + } + return false +} + // PullModuleRequest is the request message for PullModule. type PullModuleRequest struct { state protoimpl.MessageState @@ -373,6 +382,8 @@ type PushModuleRequest struct { Protofiles []*ProtoFile `protobuf:"bytes,3,rep,name=protofiles,proto3" json:"protofiles,omitempty"` // Dependencies Dependencies []*Dependency `protobuf:"bytes,4,rep,name=dependencies,proto3" json:"dependencies,omitempty"` + // Is tag a draft + IsDraft bool `protobuf:"varint,5,opt,name=is_draft,json=isDraft,proto3" json:"is_draft,omitempty"` } func (x *PushModuleRequest) Reset() { @@ -435,6 +446,13 @@ func (x *PushModuleRequest) GetDependencies() []*Dependency { return nil } +func (x *PushModuleRequest) GetIsDraft() bool { + if x != nil { + return x.IsDraft + } + return false +} + // DeleteModuleRequest is the request message for DeleteModule. type DeleteModuleRequest struct { state protoimpl.MessageState @@ -778,124 +796,128 @@ var file_pbuf_registry_v1_registry_proto_rawDesc = []byte{ 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x07, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x28, 0x0a, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0f, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x70, - 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x26, 0x0a, 0x10, 0x47, 0x65, 0x74, + 0x61, 0x67, 0x65, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x22, 0x54, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x22, 0x39, 0x0a, 0x11, 0x50, 0x75, 0x6c, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, - 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x81, 0x01, 0x0a, - 0x12, 0x50, 0x75, 0x6c, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, - 0x64, 0x75, 0x6c, 0x65, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x69, 0x6c, - 0x65, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, - 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, - 0x46, 0x69, 0x6c, 0x65, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, - 0x22, 0x2b, 0x0a, 0x15, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x75, - 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xc4, 0x01, - 0x0a, 0x11, 0x50, 0x75, 0x73, 0x68, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x6e, 0x61, - 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, - 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, - 0x66, 0x69, 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x62, - 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, - 0x6f, 0x74, 0x6f, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x69, - 0x6c, 0x65, 0x73, 0x12, 0x3f, 0x0a, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, - 0x69, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x62, 0x75, 0x66, - 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x65, - 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, - 0x63, 0x69, 0x65, 0x73, 0x22, 0x29, 0x0a, 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x6f, - 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, - 0x2a, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3e, 0x0a, 0x16, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x61, 0x67, 0x52, 0x65, + 0x65, 0x12, 0x2c, 0x0a, 0x12, 0x69, 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x5f, 0x64, 0x72, 0x61, + 0x66, 0x74, 0x5f, 0x74, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x10, 0x69, + 0x6e, 0x63, 0x6c, 0x75, 0x64, 0x65, 0x44, 0x72, 0x61, 0x66, 0x74, 0x54, 0x61, 0x67, 0x73, 0x22, + 0x39, 0x0a, 0x11, 0x50, 0x75, 0x6c, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x81, 0x01, 0x0a, 0x12, 0x50, + 0x75, 0x6c, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x2f, 0x0a, 0x06, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x17, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, + 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x06, 0x6d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, + 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x46, 0x69, + 0x6c, 0x65, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x73, 0x22, 0x2b, + 0x0a, 0x15, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0xdf, 0x01, 0x0a, 0x11, + 0x50, 0x75, 0x73, 0x68, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, + 0x74, 0x12, 0x20, 0x0a, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x74, 0x61, 0x67, 0x12, 0x3a, 0x0a, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x69, + 0x6c, 0x65, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x70, 0x62, 0x75, 0x66, + 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x72, 0x6f, 0x74, + 0x6f, 0x46, 0x69, 0x6c, 0x65, 0x52, 0x0a, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x69, 0x6c, 0x65, + 0x73, 0x12, 0x3f, 0x0a, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, + 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, + 0x65, 0x6e, 0x63, 0x79, 0x52, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, + 0x65, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x69, 0x73, 0x5f, 0x64, 0x72, 0x61, 0x66, 0x74, 0x18, 0x05, + 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x69, 0x73, 0x44, 0x72, 0x61, 0x66, 0x74, 0x22, 0x29, 0x0a, + 0x13, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x2a, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, + 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, + 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3e, 0x0a, 0x16, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x54, 0x61, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x03, 0x74, 0x61, 0x67, 0x22, 0x3f, 0x0a, 0x17, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x54, 0x61, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x44, 0x0a, 0x1c, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, - 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x3f, 0x0a, 0x17, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x61, 0x67, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, - 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x44, 0x0a, 0x1c, - 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, - 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, - 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, - 0x61, 0x67, 0x22, 0x60, 0x0a, 0x1d, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x44, - 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, - 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x62, 0x75, 0x66, - 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x65, - 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x52, 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, - 0x63, 0x69, 0x65, 0x73, 0x32, 0xca, 0x07, 0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, - 0x79, 0x12, 0x6d, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, - 0x12, 0x23, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, - 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, - 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x75, - 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, - 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, - 0x12, 0x63, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x21, 0x2e, - 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x17, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, - 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x14, 0x3a, 0x01, 0x2a, 0x22, 0x0f, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, - 0x73, 0x2f, 0x67, 0x65, 0x74, 0x12, 0x69, 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, - 0x72, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x26, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, - 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, - 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, - 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, - 0x3a, 0x01, 0x2a, 0x22, 0x0b, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, - 0x12, 0x72, 0x0a, 0x0a, 0x50, 0x75, 0x6c, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x22, + 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0x60, 0x0a, 0x1d, 0x47, + 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, + 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3f, 0x0a, 0x0c, + 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, + 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x79, 0x52, + 0x0c, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x32, 0xca, 0x07, + 0x0a, 0x08, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x12, 0x6d, 0x0a, 0x0b, 0x4c, 0x69, + 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x23, 0x2e, 0x70, 0x62, 0x75, 0x66, + 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, + 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x24, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, - 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, - 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, - 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x3a, - 0x01, 0x2a, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2f, - 0x70, 0x75, 0x6c, 0x6c, 0x12, 0x66, 0x0a, 0x0a, 0x50, 0x75, 0x73, 0x68, 0x4d, 0x6f, 0x64, 0x75, - 0x6c, 0x65, 0x12, 0x22, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, - 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, - 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x22, - 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x3a, 0x01, 0x2a, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, - 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x12, 0x7a, 0x0a, 0x0c, - 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x24, 0x2e, 0x70, - 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, - 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, - 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, - 0x17, 0x3a, 0x01, 0x2a, 0x22, 0x12, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, - 0x73, 0x2f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x88, 0x01, 0x0a, 0x0f, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x61, 0x67, 0x12, 0x27, 0x2e, 0x70, - 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x44, - 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x61, 0x67, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, + 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x13, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x0d, 0x12, 0x0b, 0x2f, 0x76, + 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x63, 0x0a, 0x09, 0x47, 0x65, 0x74, + 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x21, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x70, 0x62, 0x75, 0x66, + 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x22, 0x1a, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x14, 0x3a, 0x01, 0x2a, 0x22, 0x0f, 0x2f, + 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2f, 0x67, 0x65, 0x74, 0x12, 0x69, + 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, + 0x12, 0x26, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, + 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x6f, 0x64, 0x75, 0x6c, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, + 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, + 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x3a, 0x01, 0x2a, 0x22, 0x0b, 0x2f, 0x76, + 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x12, 0x72, 0x0a, 0x0a, 0x50, 0x75, 0x6c, + 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x22, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, + 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x4d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x70, 0x62, + 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, + 0x6c, 0x6c, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, 0x3a, 0x01, 0x2a, 0x22, 0x10, 0x2f, 0x76, 0x31, + 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2f, 0x70, 0x75, 0x6c, 0x6c, 0x12, 0x66, 0x0a, + 0x0a, 0x50, 0x75, 0x73, 0x68, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x22, 0x2e, 0x70, 0x62, + 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x75, + 0x73, 0x68, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x17, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, + 0x31, 0x2e, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x22, 0x1b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x15, + 0x3a, 0x01, 0x2a, 0x22, 0x10, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, + 0x2f, 0x70, 0x75, 0x73, 0x68, 0x12, 0x7a, 0x0a, 0x0c, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, + 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x12, 0x24, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x6f, - 0x64, 0x75, 0x6c, 0x65, 0x54, 0x61, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, 0x3a, 0x01, 0x2a, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, - 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2f, 0x74, 0x61, 0x67, 0x73, 0x2f, 0x64, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x12, 0x9b, 0x01, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, - 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x2d, 0x2e, - 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, - 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, - 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x70, - 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x47, - 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, - 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, - 0xe4, 0x93, 0x02, 0x1d, 0x3a, 0x01, 0x2a, 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, - 0x75, 0x6c, 0x65, 0x73, 0x2f, 0x64, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, - 0x73, 0x42, 0x18, 0x5a, 0x16, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, - 0x79, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x33, + 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x70, 0x62, + 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, + 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x3a, 0x01, 0x2a, 0x22, 0x12, 0x2f, + 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2f, 0x64, 0x65, 0x6c, 0x65, 0x74, + 0x65, 0x12, 0x88, 0x01, 0x0a, 0x0f, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x54, 0x61, 0x67, 0x12, 0x27, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x6f, + 0x64, 0x75, 0x6c, 0x65, 0x54, 0x61, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x28, + 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, + 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x54, 0x61, 0x67, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x22, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1c, + 0x3a, 0x01, 0x2a, 0x22, 0x17, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, + 0x2f, 0x74, 0x61, 0x67, 0x73, 0x2f, 0x64, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x9b, 0x01, 0x0a, + 0x15, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, + 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x12, 0x2d, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, + 0x69, 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, + 0x6c, 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2e, 0x2e, 0x70, 0x62, 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, + 0x73, 0x74, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x6f, 0x64, 0x75, 0x6c, + 0x65, 0x44, 0x65, 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x23, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x3a, 0x01, 0x2a, + 0x22, 0x18, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x2f, 0x64, 0x65, + 0x70, 0x65, 0x6e, 0x64, 0x65, 0x6e, 0x63, 0x69, 0x65, 0x73, 0x42, 0x18, 0x5a, 0x16, 0x70, 0x62, + 0x75, 0x66, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74, 0x72, 0x79, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, + 0x31, 0x3b, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/gen/pbuf-registry/v1/registry.swagger.json b/gen/pbuf-registry/v1/registry.swagger.json index 8392d59..0dad71c 100644 --- a/gen/pbuf-registry/v1/registry.swagger.json +++ b/gen/pbuf-registry/v1/registry.swagger.json @@ -418,6 +418,10 @@ "name": { "type": "string", "description": "The name of the module to retrieve." + }, + "includeDraftTags": { + "type": "boolean", + "title": "Include draft tags or not" } }, "description": "GetModuleRequest is the request message for GetModule." @@ -457,6 +461,13 @@ "type": "string" }, "description": "The tags of the module." + }, + "draftTags": { + "type": "array", + "items": { + "type": "string" + }, + "description": "The draft tags of the module." } }, "description": "Module is a module registered in the registry." @@ -533,6 +544,10 @@ "$ref": "#/definitions/v1Dependency" }, "title": "Dependencies" + }, + "isDraft": { + "type": "boolean", + "title": "Is tag a draft" } }, "description": "PushModuleRequest is the request message for PushModule." diff --git a/go.mod b/go.mod index 8114884..3bd3218 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,13 @@ go 1.21 require ( github.com/docker/go-connections v0.4.0 + github.com/go-co-op/gocron v1.36.0 github.com/go-kratos/kratos/v2 v2.7.1 github.com/google/martian v2.1.0+incompatible github.com/jackc/pgx/v5 v5.4.3 github.com/lib/pq v1.10.9 github.com/pressly/goose/v3 v3.15.1 + github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.17.0 github.com/stretchr/testify v1.8.4 github.com/testcontainers/testcontainers-go v0.26.0 @@ -38,9 +40,10 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/protobuf v1.5.3 // indirect - github.com/google/uuid v1.3.1 // indirect + github.com/google/uuid v1.4.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect github.com/jackc/puddle/v2 v2.2.1 // indirect @@ -59,6 +62,7 @@ require ( github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20221212215047-62379fc7944b // indirect + github.com/robfig/cron/v3 v3.0.1 // indirect github.com/sagikazarmark/locafero v0.3.0 // indirect github.com/sagikazarmark/slog-shim v0.1.0 // indirect github.com/shirou/gopsutil/v3 v3.23.9 // indirect @@ -73,6 +77,7 @@ require ( github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect + go.uber.org/atomic v1.9.0 // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.14.0 // indirect golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect diff --git a/go.sum b/go.sum index 631f9d0..c7d6c93 100644 --- a/go.sum +++ b/go.sum @@ -68,6 +68,8 @@ github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -98,6 +100,8 @@ github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0X github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/go-co-op/gocron v1.36.0 h1:sEmAwg57l4JWQgzaVWYfKZ+w13uHOqeOtwjo72Ll5Wc= +github.com/go-co-op/gocron v1.36.0/go.mod h1:3L/n6BkO7ABj+TrfSVXLRzsP26zmikL4ISkLQ0O8iNY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= @@ -174,8 +178,8 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= -github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -187,6 +191,8 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= @@ -205,6 +211,8 @@ github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJw github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -238,6 +246,7 @@ github.com/opencontainers/runc v1.1.9 h1:XR0VIHTGce5eWPkaPesqTBrhW2yAcaraWfsEalN github.com/opencontainers/runc v1.1.9/go.mod h1:CbUumNnWCuTGFukNXahoo/RFBZvDAgRh/smNYNOhA50= github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= @@ -252,9 +261,14 @@ github.com/pressly/goose/v3 v3.15.1/go.mod h1:0E3Yg/+EwYzO6Rz2P98MlClFgIcoujbVRs github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= +github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ= github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U= github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= @@ -273,6 +287,8 @@ github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.17.0 h1:I5txKw7MJasPL/BrfkbA0Jyo/oELqVmux4pR/UxOMfI= @@ -287,6 +303,7 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= @@ -311,6 +328,8 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= +go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= diff --git a/internal/background/compaction.go b/internal/background/compaction.go new file mode 100644 index 0000000..36e30e7 --- /dev/null +++ b/internal/background/compaction.go @@ -0,0 +1,37 @@ +package background + +import ( + "context" + + "github.com/go-kratos/kratos/v2/log" + "github.com/pbufio/pbuf-registry/internal/data" +) + +type CompactionDaemon interface { + Run() error +} + +type compactionDaemon struct { + registryRepository data.RegistryRepository + log *log.Helper +} + +func NewCompactionDaemon(registryRepository data.RegistryRepository, logger log.Logger) CompactionDaemon { + return &compactionDaemon{ + registryRepository: registryRepository, + log: log.NewHelper(log.With(logger, "module", "background/CompactionDaemon")), + } +} + +func (d *compactionDaemon) Run() error { + d.log.Infof("Running compaction") + + err := d.registryRepository.DeleteObsoleteDraftTags(context.Background()) + if err != nil { + d.log.Errorf("DeleteObsoleteDraftTags error: %v", err) + return err + } + + d.log.Infof("Compaction finished") + return nil +} diff --git a/internal/config/config.go b/internal/config/config.go index 3cdb501..b198879 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -47,6 +47,12 @@ type Config struct { DSN string `mapstructure:"dsn"` } `mapstructure:"database"` } `mapstructure:"data"` + + Daemons struct { + Compaction struct { + CronSchedule string `mapstructure:"cron"` + } + } } // Cfg is the global config diff --git a/internal/config/config.yaml b/internal/config/config.yaml index 9584f55..277f73e 100644 --- a/internal/config/config.yaml +++ b/internal/config/config.yaml @@ -19,7 +19,10 @@ server: addr: 0.0.0.0:8082 timeout: 10s - data: database: - dsn: "postgres://pbuf:pbuf@localhost:5432/pbuf_registry?sslmode=disable" \ No newline at end of file + dsn: "postgres://pbuf:pbuf@localhost:5432/pbuf_registry?sslmode=disable" + +daemons: + compaction: + cron: "0 * * * *" \ No newline at end of file diff --git a/internal/data/registry.go b/internal/data/registry.go index dc8246c..a53cf69 100644 --- a/internal/data/registry.go +++ b/internal/data/registry.go @@ -3,6 +3,7 @@ package data import ( "context" "encoding/base64" + "encoding/json" "errors" "fmt" @@ -12,16 +13,21 @@ import ( v1 "github.com/pbufio/pbuf-registry/gen/pbuf-registry/v1" ) +var TagNotFoundError = errors.New("tag not found") + type RegistryRepository interface { RegisterModule(ctx context.Context, moduleName string) error GetModule(ctx context.Context, name string) (*v1.Module, error) ListModules(ctx context.Context, pageSize int, token string) ([]*v1.Module, string, error) DeleteModule(ctx context.Context, name string) error PushModule(ctx context.Context, name string, tag string, protofiles []*v1.ProtoFile) (*v1.Module, error) + PushDraftModule(ctx context.Context, name string, tag string, protofiles []*v1.ProtoFile, dependencies []*v1.Dependency) (*v1.Module, error) PullModule(ctx context.Context, name string, tag string) (*v1.Module, []*v1.ProtoFile, error) + PullDraftModule(ctx context.Context, name string, tag string) (*v1.Module, []*v1.ProtoFile, error) DeleteModuleTag(ctx context.Context, name string, tag string) error AddModuleDependencies(ctx context.Context, name string, tag string, dependencies []*v1.Dependency) error GetModuleDependencies(ctx context.Context, name string, tag string) ([]*v1.Dependency, error) + DeleteObsoleteDraftTags(ctx context.Context) error } type registryRepository struct { @@ -32,7 +38,7 @@ type registryRepository struct { func NewRegistryRepository(pool *pgxpool.Pool, logger log.Logger) RegistryRepository { return ®istryRepository{ pool: pool, - logger: log.NewHelper(logger), + logger: log.NewHelper(log.With(logger, "module", "data/RegistryRepository")), } } @@ -63,7 +69,7 @@ func (r *registryRepository) GetModule(ctx context.Context, name string) (*v1.Mo // fetch tags tags, err := r.pool.Query(ctx, - "SELECT tag FROM tags WHERE module_id = $1 ORDER BY updated_at DESC", + "SELECT tag FROM tags WHERE module_id = $1 ORDER BY updated_at DESC LIMIT 10", module.Id) if err != nil { // tags not found @@ -84,6 +90,29 @@ func (r *registryRepository) GetModule(ctx context.Context, name string) (*v1.Mo module.Tags = append(module.Tags, tag) } + // fetch draft tags + draftTags, err := r.pool.Query(ctx, + "SELECT tag FROM draft_tags WHERE module_id = $1 ORDER BY updated_at DESC LIMIT 10", + module.Id) + if err != nil { + // tags not found + if errors.Is(err, pgx.ErrNoRows) { + r.logger.Infof("no draft tags found for module %s", name) + return &module, nil + } + + return nil, fmt.Errorf("could not select draft tags from database: %w", err) + } + + for draftTags.Next() { + var tag string + err := draftTags.Scan(&tag) + if err != nil { + return nil, fmt.Errorf("could not scan draft tag: %w", err) + } + module.DraftTags = append(module.DraftTags, tag) + } + return &module, nil } @@ -167,6 +196,18 @@ func (r *registryRepository) DeleteModule(ctx context.Context, name string) erro r.logger.Infof("deleted %d tags for module %s", res.RowsAffected(), name) } + // delete all module draft tags + res, err = r.pool.Exec(ctx, + "DELETE FROM draft_tags WHERE module_id = (SELECT id FROM modules WHERE name = $1)", + name) + if err != nil { + return fmt.Errorf("could not delete module draft tags from database: %w", err) + } + + if res.RowsAffected() > 0 { + r.logger.Infof("deleted %d draft tags for module %s", res.RowsAffected(), name) + } + // delete module res, err = r.pool.Exec(ctx, "DELETE FROM modules WHERE name = $1", @@ -244,13 +285,13 @@ func (r *registryRepository) PullModule(ctx context.Context, name string, tag st return nil, nil, errors.New("module not found") } - // check if tag exists - var tagId string - err = r.pool.QueryRow(ctx, - "SELECT id FROM tags WHERE module_id = $1 AND tag = $2", - module.Id, tag).Scan(&tagId) + tagId, err := r.getModuleTagId(ctx, name, tag) if err != nil { - return nil, nil, fmt.Errorf("could not select tag from database: %w", err) + return nil, nil, fmt.Errorf("could not get tag id: %w", err) + } + + if tagId == "" { + return nil, nil, TagNotFoundError } // fetch protofiles @@ -276,24 +317,48 @@ func (r *registryRepository) PullModule(ctx context.Context, name string, tag st return module, protofiles, nil } -func (r *registryRepository) DeleteModuleTag(ctx context.Context, name string, tag string) error { - // check if module exists +func (r *registryRepository) PullDraftModule(ctx context.Context, name string, tag string) (*v1.Module, []*v1.ProtoFile, error) { + // fetch module with GetModule method and get all other from draft_tags table module, err := r.GetModule(ctx, name) if err != nil { - return fmt.Errorf("could not get module: %w", err) + return nil, nil, fmt.Errorf("could not get module: %w", err) } if module == nil { - return errors.New("module not found") + return nil, nil, errors.New("module not found") } - // check if tag exists - var tagId string + // get info from draft_tags table + var protofilesJson string err = r.pool.QueryRow(ctx, - "SELECT id FROM tags WHERE module_id = $1 AND tag = $2", - module.Id, tag).Scan(&tagId) + "SELECT proto_files FROM draft_tags WHERE module_id = $1 AND tag = $2", + module.Id, tag).Scan(&protofilesJson) + if err != nil { + // if not found raise specific error + if errors.Is(err, pgx.ErrNoRows) { + return nil, nil, errors.New("draft tag not found") + } + return nil, nil, fmt.Errorf("could not select draft tag from database: %w", err) + } + + // unmarshal protofiles + var protofiles []*v1.ProtoFile + err = json.Unmarshal([]byte(protofilesJson), &protofiles) if err != nil { - return fmt.Errorf("could not select tag from database: %w", err) + return nil, nil, fmt.Errorf("could not unmarshal protofiles: %w", err) + } + + return module, protofiles, nil +} + +func (r *registryRepository) DeleteModuleTag(ctx context.Context, name string, tag string) error { + tagId, err := r.getModuleTagId(ctx, name, tag) + if err != nil { + return fmt.Errorf("could not get tag id: %w", err) + } + + if tagId == "" { + return TagNotFoundError } // delete protofiles @@ -331,7 +396,7 @@ func (r *registryRepository) DeleteModuleTag(ctx context.Context, name string, t if res.RowsAffected() > 0 { r.logger.Infof("deleted tag %s", tag) } else { - return errors.New("tag not found") + return TagNotFoundError } return nil @@ -339,12 +404,13 @@ func (r *registryRepository) DeleteModuleTag(ctx context.Context, name string, t func (r *registryRepository) AddModuleDependencies(ctx context.Context, name string, tag string, dependencies []*v1.Dependency) error { // find the tag id by name and tag - var tagId string - err := r.pool.QueryRow(ctx, - "SELECT id FROM tags WHERE module_id = (SELECT id FROM modules WHERE name = $1) AND tag = $2", - name, tag).Scan(&tagId) + tagId, err := r.getModuleTagId(ctx, name, tag) if err != nil { - return fmt.Errorf("could not find tag %s for module %s: %w", tag, name, err) + return fmt.Errorf("could not get tag id: %w", err) + } + + if tagId == "" { + return TagNotFoundError } // for each dependency find tag id and insert into dependencies table @@ -369,23 +435,28 @@ func (r *registryRepository) AddModuleDependencies(ctx context.Context, name str } func (r *registryRepository) GetModuleDependencies(ctx context.Context, name string, tag string) ([]*v1.Dependency, error) { + var dependencies []*v1.Dependency + // find the latest tag if tag is empty if tag == "" { err := r.pool.QueryRow(ctx, "SELECT tag FROM tags WHERE module_id = (SELECT id FROM modules WHERE name = $1) ORDER BY updated_at DESC LIMIT 1", name).Scan(&tag) if err != nil { - return nil, fmt.Errorf("could not find tag for module %s: %w", name, err) + if errors.Is(err, pgx.ErrNoRows) { + return dependencies, nil + } + return nil, fmt.Errorf("could not select tag from database: %w", err) } } - // find the tag id by name and tag - var tagId string - err := r.pool.QueryRow(ctx, - "SELECT id FROM tags WHERE module_id = (SELECT id FROM modules WHERE name = $1) AND tag = $2", - name, tag).Scan(&tagId) + tagId, err := r.getModuleTagId(ctx, name, tag) if err != nil { - return nil, fmt.Errorf("could not find tag %s for module %s: %w", tag, name, err) + return nil, fmt.Errorf("could not get tag id: %w", err) + } + + if tagId == "" { + return nil, TagNotFoundError } // find all dependencies @@ -400,8 +471,6 @@ func (r *registryRepository) GetModuleDependencies(ctx context.Context, name str return nil, fmt.Errorf("could not select dependencies from database: %w", err) } - var dependencies []*v1.Dependency - for rows.Next() { var dependencyTagId string err := rows.Scan(&dependencyTagId) @@ -427,3 +496,82 @@ func (r *registryRepository) GetModuleDependencies(ctx context.Context, name str return dependencies, nil } + +func (r *registryRepository) PushDraftModule(ctx context.Context, name string, tag string, protofiles []*v1.ProtoFile, dependencies []*v1.Dependency) (*v1.Module, error) { + // check if module exists + module, err := r.GetModule(ctx, name) + if err != nil { + return nil, fmt.Errorf("could not get module: %w", err) + } + + if module == nil { + return nil, errors.New("module not found") + } + + // serialize protofiles and dependencies in json + protofilesJson, err := json.Marshal(protofiles) + if err != nil { + return nil, fmt.Errorf("could not serialize protofiles: %w", err) + } + + dependenciesJson, err := json.Marshal(dependencies) + if err != nil { + return nil, fmt.Errorf("could not serialize dependencies: %w", err) + } + + // create the tag + // if exists update proto_files, dependencies and updated_at + _, err = r.pool.Exec(ctx, "INSERT INTO draft_tags (module_id, tag, proto_files, dependencies) VALUES ($1, $2, $3, $4) ON CONFLICT (module_id, tag) DO UPDATE SET proto_files = $3, dependencies = $4, updated_at = NOW()", + module.Id, tag, protofilesJson, dependenciesJson) + if err != nil { + return nil, fmt.Errorf("could not insert draft tag into database: %w", err) + } + + // append if not exists + // check by DraftTags property + var draftTagExists bool + for _, t := range module.DraftTags { + if t == tag { + draftTagExists = true + break + } + } + + if !draftTagExists { + module.DraftTags = append(module.DraftTags, tag) + } + + return module, nil +} + +func (r *registryRepository) getModuleTagId(ctx context.Context, moduleName string, tag string) (string, error) { + // check if tag exists + var tagId string + err := r.pool.QueryRow(ctx, + "SELECT id FROM tags WHERE module_id = (SELECT id FROM modules WHERE name = $1) AND tag = $2", + moduleName, tag).Scan(&tagId) + if err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return "", nil + } + return "", fmt.Errorf("could not select tag from database: %w", err) + } + + return tagId, nil +} + +// DeleteObsoleteDraftTags deletes all draft tags +// that are older than 7 days +func (r *registryRepository) DeleteObsoleteDraftTags(ctx context.Context) error { + res, err := r.pool.Exec(ctx, + "DELETE FROM draft_tags WHERE updated_at < NOW() - INTERVAL '7 days'") + if err != nil { + return fmt.Errorf("could not delete draft tags from database: %w", err) + } + + if res.RowsAffected() > 0 { + r.logger.Infof("deleted %d draft tags", res.RowsAffected()) + } + + return nil +} diff --git a/internal/data/registry_test.go b/internal/data/registry_test.go index 7d10233..dca24a9 100644 --- a/internal/data/registry_test.go +++ b/internal/data/registry_test.go @@ -269,6 +269,112 @@ func Test_registryRepository_PushModule(t *testing.T) { } } +func Test_registryRepository_PushDraftModule(t *testing.T) { + type args struct { + ctx context.Context + name string + tag string + protofiles []*v1.ProtoFile + dependencies []*v1.Dependency + } + tests := []struct { + name string + args args + want *v1.Module + wantErr bool + }{ + { + name: "Push draft module", + args: args{ + ctx: context.Background(), + name: "pbuf.io/pbuf-registry", + tag: "v0.0.0-rc.1", + protofiles: protofiles, + }, + want: &v1.Module{ + Id: fakeUUID, + Name: "pbuf.io/pbuf-registry", + Tags: []string{"v0.0.0"}, + DraftTags: []string{"v0.0.0-rc.1"}, + }, + wantErr: false, + }, + { + name: "Push draft module with existing tag", + args: args{ + ctx: context.Background(), + name: "pbuf.io/pbuf-registry", + tag: "v0.0.0-rc.1", + protofiles: protofiles, + }, + want: &v1.Module{ + Id: fakeUUID, + Name: "pbuf.io/pbuf-registry", + Tags: []string{"v0.0.0"}, + DraftTags: []string{"v0.0.0-rc.1"}, + }, + wantErr: false, + }, + { + name: "Push draft module", + args: args{ + ctx: context.Background(), + name: "pbuf.io/pbuf-registry-2", + tag: "v0.0.0-rc.1", + protofiles: protofiles, + }, + want: &v1.Module{ + Id: fakeUUID, + Name: "pbuf.io/pbuf-registry-2", + Tags: []string{"v0.0.0"}, + DraftTags: []string{"v0.0.0-rc.1"}, + }, + wantErr: false, + }, + { + name: "Push draft module with dependencies", + args: args{ + ctx: context.Background(), + name: "pbuf.io/pbuf-registry-2", + tag: "v0.0.0-rc.1", + protofiles: protofiles, + dependencies: []*v1.Dependency{ + { + Name: "pbuf.io/pbuf-registry", + Tag: "v0.0.0", + }, + }, + }, + want: &v1.Module{ + Id: fakeUUID, + Name: "pbuf.io/pbuf-registry-2", + Tags: []string{"v0.0.0"}, + DraftTags: []string{"v0.0.0-rc.1"}, + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := suite.registryRepository + + got, err := r.PushDraftModule(tt.args.ctx, tt.args.name, tt.args.tag, tt.args.protofiles, tt.args.dependencies) + if (err != nil) != tt.wantErr { + t.Errorf("PushDraftModule() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if got != nil { + got.Id = fakeUUID + } + + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("PushDraftModule() got = %v, want %v", got, tt.want) + } + }) + } +} + func Test_registryRepository_PullModule(t *testing.T) { type args struct { ctx context.Context @@ -290,9 +396,10 @@ func Test_registryRepository_PullModule(t *testing.T) { tag: "v0.0.0", }, wantModule: &v1.Module{ - Id: fakeUUID, - Name: "pbuf.io/pbuf-registry", - Tags: []string{"v0.0.0"}, + Id: fakeUUID, + Name: "pbuf.io/pbuf-registry", + Tags: []string{"v0.0.0"}, + DraftTags: []string{"v0.0.0-rc.1"}, }, want: protofiles, wantErr: false, @@ -344,6 +451,104 @@ func Test_registryRepository_PullModule(t *testing.T) { } } +func Test_registryRepository_PullDraftModule(t *testing.T) { + type args struct { + ctx context.Context + name string + tag string + } + tests := []struct { + name string + args args + want []*v1.ProtoFile + wantModule *v1.Module + wantErr bool + }{ + { + name: "Pull draft module", + args: args{ + ctx: context.Background(), + name: "pbuf.io/pbuf-registry", + tag: "v0.0.0-rc.1", + }, + wantModule: &v1.Module{ + Id: fakeUUID, + Name: "pbuf.io/pbuf-registry", + Tags: []string{"v0.0.0"}, + DraftTags: []string{"v0.0.0-rc.1"}, + }, + want: protofiles, + wantErr: false, + }, + { + name: "Pull draft module not found", + args: args{ + ctx: context.Background(), + name: "pbuf.io/pbuf-registry-not-found", + tag: "v0.0.0-rc.1", + }, + wantModule: nil, + want: nil, + wantErr: true, + }, + { + name: "Pull draft module - tag not found", + args: args{ + ctx: context.Background(), + name: "pbuf.io/pbuf-registry", + tag: "v0.1.0", + }, + wantModule: nil, + want: nil, + wantErr: true, + }, + { + name: "Pull draft module - tag not found", + args: args{ + ctx: context.Background(), + name: "pbuf.io/pbuf-registry", + tag: "v0.1.0", + }, + wantModule: nil, + want: nil, + wantErr: true, + }, + { + name: "Pull draft module - tag not found", + args: args{ + ctx: context.Background(), + name: "pbuf.io/pbuf-registry", + tag: "v0.1.0", + }, + wantModule: nil, + want: nil, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + r := suite.registryRepository + + module, protoFiles, err := r.PullDraftModule(tt.args.ctx, tt.args.name, tt.args.tag) + if (err != nil) != tt.wantErr { + t.Errorf("PullDraftModule() error = %v, wantErr %v", err, tt.wantErr) + return + } + + if module != nil { + module.Id = fakeUUID + } + + if !reflect.DeepEqual(module, tt.wantModule) { + t.Errorf("PullDraftModule() got = %v, want %v", module, tt.want) + } + if !reflect.DeepEqual(protoFiles, tt.want) { + t.Errorf("PullDraftModule() got1 = %v, want %v", protoFiles, tt.want) + } + }) + } +} + func Test_registryRepository_AddModuleDependencies(t *testing.T) { type args struct { ctx context.Context diff --git a/internal/mocks/auth_middleware.go b/internal/mocks/auth_middleware.go index e878655..c0f82a6 100644 --- a/internal/mocks/auth_middleware.go +++ b/internal/mocks/auth_middleware.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.37.1. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -16,6 +16,10 @@ type AuthMiddleware struct { func (_m *AuthMiddleware) NewAuthMiddleware() middleware.Middleware { ret := _m.Called() + if len(ret) == 0 { + panic("no return value specified for NewAuthMiddleware") + } + var r0 middleware.Middleware if rf, ok := ret.Get(0).(func() middleware.Middleware); ok { r0 = rf() diff --git a/internal/mocks/compaction_daemon.go b/internal/mocks/compaction_daemon.go new file mode 100644 index 0000000..4bc09a2 --- /dev/null +++ b/internal/mocks/compaction_daemon.go @@ -0,0 +1,42 @@ +// Code generated by mockery v2.38.0. DO NOT EDIT. + +package mocks + +import mock "github.com/stretchr/testify/mock" + +// CompactionDaemon is an autogenerated mock type for the CompactionDaemon type +type CompactionDaemon struct { + mock.Mock +} + +// Run provides a mock function with given fields: +func (_m *CompactionDaemon) Run() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Run") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NewCompactionDaemon creates a new instance of CompactionDaemon. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewCompactionDaemon(t interface { + mock.TestingT + Cleanup(func()) +}) *CompactionDaemon { + mock := &CompactionDaemon{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/mocks/registry_repository.go b/internal/mocks/registry_repository.go index 7160922..cab4105 100644 --- a/internal/mocks/registry_repository.go +++ b/internal/mocks/registry_repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.37.1. DO NOT EDIT. +// Code generated by mockery v2.38.0. DO NOT EDIT. package mocks @@ -19,6 +19,10 @@ type RegistryRepository struct { func (_m *RegistryRepository) AddModuleDependencies(ctx context.Context, name string, tag string, dependencies []*v1.Dependency) error { ret := _m.Called(ctx, name, tag, dependencies) + if len(ret) == 0 { + panic("no return value specified for AddModuleDependencies") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, string, []*v1.Dependency) error); ok { r0 = rf(ctx, name, tag, dependencies) @@ -33,6 +37,10 @@ func (_m *RegistryRepository) AddModuleDependencies(ctx context.Context, name st func (_m *RegistryRepository) DeleteModule(ctx context.Context, name string) error { ret := _m.Called(ctx, name) + if len(ret) == 0 { + panic("no return value specified for DeleteModule") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { r0 = rf(ctx, name) @@ -47,6 +55,10 @@ func (_m *RegistryRepository) DeleteModule(ctx context.Context, name string) err func (_m *RegistryRepository) DeleteModuleTag(ctx context.Context, name string, tag string) error { ret := _m.Called(ctx, name, tag) + if len(ret) == 0 { + panic("no return value specified for DeleteModuleTag") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string, string) error); ok { r0 = rf(ctx, name, tag) @@ -57,10 +69,32 @@ func (_m *RegistryRepository) DeleteModuleTag(ctx context.Context, name string, return r0 } +// DeleteObsoleteDraftTags provides a mock function with given fields: ctx +func (_m *RegistryRepository) DeleteObsoleteDraftTags(ctx context.Context) error { + ret := _m.Called(ctx) + + if len(ret) == 0 { + panic("no return value specified for DeleteObsoleteDraftTags") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context) error); ok { + r0 = rf(ctx) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // GetModule provides a mock function with given fields: ctx, name func (_m *RegistryRepository) GetModule(ctx context.Context, name string) (*v1.Module, error) { ret := _m.Called(ctx, name) + if len(ret) == 0 { + panic("no return value specified for GetModule") + } + var r0 *v1.Module var r1 error if rf, ok := ret.Get(0).(func(context.Context, string) (*v1.Module, error)); ok { @@ -87,6 +121,10 @@ func (_m *RegistryRepository) GetModule(ctx context.Context, name string) (*v1.M func (_m *RegistryRepository) GetModuleDependencies(ctx context.Context, name string, tag string) ([]*v1.Dependency, error) { ret := _m.Called(ctx, name, tag) + if len(ret) == 0 { + panic("no return value specified for GetModuleDependencies") + } + var r0 []*v1.Dependency var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string) ([]*v1.Dependency, error)); ok { @@ -113,6 +151,10 @@ func (_m *RegistryRepository) GetModuleDependencies(ctx context.Context, name st func (_m *RegistryRepository) ListModules(ctx context.Context, pageSize int, token string) ([]*v1.Module, string, error) { ret := _m.Called(ctx, pageSize, token) + if len(ret) == 0 { + panic("no return value specified for ListModules") + } + var r0 []*v1.Module var r1 string var r2 error @@ -142,10 +184,53 @@ func (_m *RegistryRepository) ListModules(ctx context.Context, pageSize int, tok return r0, r1, r2 } +// PullDraftModule provides a mock function with given fields: ctx, name, tag +func (_m *RegistryRepository) PullDraftModule(ctx context.Context, name string, tag string) (*v1.Module, []*v1.ProtoFile, error) { + ret := _m.Called(ctx, name, tag) + + if len(ret) == 0 { + panic("no return value specified for PullDraftModule") + } + + var r0 *v1.Module + var r1 []*v1.ProtoFile + var r2 error + if rf, ok := ret.Get(0).(func(context.Context, string, string) (*v1.Module, []*v1.ProtoFile, error)); ok { + return rf(ctx, name, tag) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string) *v1.Module); ok { + r0 = rf(ctx, name, tag) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*v1.Module) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string) []*v1.ProtoFile); ok { + r1 = rf(ctx, name, tag) + } else { + if ret.Get(1) != nil { + r1 = ret.Get(1).([]*v1.ProtoFile) + } + } + + if rf, ok := ret.Get(2).(func(context.Context, string, string) error); ok { + r2 = rf(ctx, name, tag) + } else { + r2 = ret.Error(2) + } + + return r0, r1, r2 +} + // PullModule provides a mock function with given fields: ctx, name, tag func (_m *RegistryRepository) PullModule(ctx context.Context, name string, tag string) (*v1.Module, []*v1.ProtoFile, error) { ret := _m.Called(ctx, name, tag) + if len(ret) == 0 { + panic("no return value specified for PullModule") + } + var r0 *v1.Module var r1 []*v1.ProtoFile var r2 error @@ -177,10 +262,44 @@ func (_m *RegistryRepository) PullModule(ctx context.Context, name string, tag s return r0, r1, r2 } +// PushDraftModule provides a mock function with given fields: ctx, name, tag, protofiles, dependencies +func (_m *RegistryRepository) PushDraftModule(ctx context.Context, name string, tag string, protofiles []*v1.ProtoFile, dependencies []*v1.Dependency) (*v1.Module, error) { + ret := _m.Called(ctx, name, tag, protofiles, dependencies) + + if len(ret) == 0 { + panic("no return value specified for PushDraftModule") + } + + var r0 *v1.Module + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string, string, []*v1.ProtoFile, []*v1.Dependency) (*v1.Module, error)); ok { + return rf(ctx, name, tag, protofiles, dependencies) + } + if rf, ok := ret.Get(0).(func(context.Context, string, string, []*v1.ProtoFile, []*v1.Dependency) *v1.Module); ok { + r0 = rf(ctx, name, tag, protofiles, dependencies) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*v1.Module) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, string, string, []*v1.ProtoFile, []*v1.Dependency) error); ok { + r1 = rf(ctx, name, tag, protofiles, dependencies) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // PushModule provides a mock function with given fields: ctx, name, tag, protofiles func (_m *RegistryRepository) PushModule(ctx context.Context, name string, tag string, protofiles []*v1.ProtoFile) (*v1.Module, error) { ret := _m.Called(ctx, name, tag, protofiles) + if len(ret) == 0 { + panic("no return value specified for PushModule") + } + var r0 *v1.Module var r1 error if rf, ok := ret.Get(0).(func(context.Context, string, string, []*v1.ProtoFile) (*v1.Module, error)); ok { @@ -207,6 +326,10 @@ func (_m *RegistryRepository) PushModule(ctx context.Context, name string, tag s func (_m *RegistryRepository) RegisterModule(ctx context.Context, moduleName string) error { ret := _m.Called(ctx, moduleName) + if len(ret) == 0 { + panic("no return value specified for RegisterModule") + } + var r0 error if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { r0 = rf(ctx, moduleName) diff --git a/internal/server/registry.go b/internal/server/registry.go index 1273da8..27e8bf4 100644 --- a/internal/server/registry.go +++ b/internal/server/registry.go @@ -3,6 +3,7 @@ package server import ( "context" "errors" + "fmt" "github.com/go-kratos/kratos/v2/log" v1 "github.com/pbufio/pbuf-registry/gen/pbuf-registry/v1" @@ -117,6 +118,16 @@ func (r *RegistryServer) PushModule(ctx context.Context, request *v1.PushModuleR return nil, err } + if request.IsDraft { + module, err := r.registryRepository.PushDraftModule(ctx, name, tag, request.Protofiles, request.Dependencies) + if err != nil { + r.logger.Infof("error pushing draft module: %v", err) + return nil, err + } + + return module, nil + } + module, err := r.registryRepository.PushModule(ctx, name, tag, request.Protofiles) if err != nil { r.logger.Infof("error pushing module: %v", err) @@ -143,10 +154,28 @@ func (r *RegistryServer) PullModule(ctx context.Context, request *v1.PullModuleR return nil, errors.New("tag cannot be empty") } + tagNotFound := false + module, protoFiles, err := r.registryRepository.PullModule(ctx, name, tag) if err != nil { - r.logger.Infof("error pulling module: %v", err) - return nil, err + if errors.Is(err, data.TagNotFoundError) { + tagNotFound = true + } else { + r.logger.Infof("error pulling module: %v", err) + return nil, err + } + } + + if tagNotFound { + module, protoFiles, err = r.registryRepository.PullDraftModule(ctx, name, tag) + if err != nil { + if errors.Is(err, data.TagNotFoundError) { + return nil, fmt.Errorf("tag %s not found for module %s", tag, name) + } else { + r.logger.Infof("error pulling draft module: %v", err) + return nil, err + } + } } return &v1.PullModuleResponse{ diff --git a/migrations/20231130160944_draft_tags.sql b/migrations/20231130160944_draft_tags.sql new file mode 100644 index 0000000..4bd2e15 --- /dev/null +++ b/migrations/20231130160944_draft_tags.sql @@ -0,0 +1,17 @@ +-- +goose Up +-- +goose StatementBegin +CREATE TABLE IF NOT EXISTS draft_tags ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + module_id UUID NOT NULL, + tag VARCHAR(255) NOT NULL, + proto_files JSONB NOT NULL, + dependencies JSONB NOT NULL, + updated_at timestamp NOT NULL DEFAULT NOW(), + UNIQUE (module_id, tag) +); +-- +goose StatementEnd + +-- +goose Down +-- +goose StatementBegin +DROP TABLE IF EXISTS draft_tags; +-- +goose StatementEnd