From 43877805fab981cf32bc6554d371a27a76d658fd Mon Sep 17 00:00:00 2001 From: Gustavo Gama Date: Fri, 8 May 2026 00:37:54 -0300 Subject: [PATCH] feat: add catalog update env metadata changeset --- catalog/changesets/update_env_metadata.go | 49 ++++++ .../changesets/update_env_metadata_test.go | 147 ++++++++++++++++++ catalog/operations/update_env_metadata.go | 49 ++++++ 3 files changed, 245 insertions(+) create mode 100644 catalog/changesets/update_env_metadata.go create mode 100644 catalog/changesets/update_env_metadata_test.go create mode 100644 catalog/operations/update_env_metadata.go diff --git a/catalog/changesets/update_env_metadata.go b/catalog/changesets/update_env_metadata.go new file mode 100644 index 0000000..e792f22 --- /dev/null +++ b/catalog/changesets/update_env_metadata.go @@ -0,0 +1,49 @@ +package changesets + +import ( + "errors" + + cldfdatastore "github.com/smartcontractkit/chainlink-deployments-framework/datastore" + cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" + cldfops "github.com/smartcontractkit/chainlink-deployments-framework/operations" + + "github.com/smartcontractkit/cld-changesets/catalog/operations" +) + +// UpdateEnvMetadataChangeset updates existing env metadata entries in the Catalog service. +type UpdateEnvMetadataChangeset struct{} + +type UpdateEnvMetadataChangesetInput struct { + EnvMetadata cldfdatastore.EnvMetadata `json:"envMetadata"` +} + +// VerifyPreconditions ensures the input is valid. +func (UpdateEnvMetadataChangeset) VerifyPreconditions(e cldf.Environment, input UpdateEnvMetadataChangesetInput) error { + if input.EnvMetadata.Metadata == nil { + return errors.New("missing env metadata input") + } + if e.DataStore == nil { + return errors.New("missing datastore in environment") + } + + return nil +} + +// Apply executes the changeset, updating the env metadata in the Catalog service. +func (UpdateEnvMetadataChangeset) Apply( + e cldf.Environment, input UpdateEnvMetadataChangesetInput, +) (cldf.ChangesetOutput, error) { + deps := operations.UpdateEnvMetadataDeps{DataStore: e.DataStore} + opInput := operations.UpdateEnvMetadataInput{EnvMetadata: input.EnvMetadata} + + report, err := cldfops.ExecuteOperation(e.OperationsBundle, operations.UpdateEnvMetadataOp, deps, opInput) + out := cldf.ChangesetOutput{ + DataStore: report.Output.DataStore, + Reports: []cldfops.Report[any, any]{report.ToGenericReport()}, + } + if err != nil { + return out, err + } + + return out, nil +} diff --git a/catalog/changesets/update_env_metadata_test.go b/catalog/changesets/update_env_metadata_test.go new file mode 100644 index 0000000..b80a0d4 --- /dev/null +++ b/catalog/changesets/update_env_metadata_test.go @@ -0,0 +1,147 @@ +package changesets + +import ( + "testing" + + "github.com/Masterminds/semver/v3" + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/stretchr/testify/require" + + cldfdatastore "github.com/smartcontractkit/chainlink-deployments-framework/datastore" + cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment" + cldfoperations "github.com/smartcontractkit/chainlink-deployments-framework/operations" + cldflogger "github.com/smartcontractkit/chainlink-deployments-framework/pkg/logger" + + "github.com/smartcontractkit/cld-changesets/catalog/operations" +) + +func TestUpdateEnvMetadataChangeset_VerifyPreconditions(t *testing.T) { + t.Parallel() + + envMetadata := cldfdatastore.EnvMetadata{Metadata: "value"} + + tests := []struct { + name string + env cldf.Environment + input UpdateEnvMetadataChangesetInput + wantErr string + }{ + { + name: "success: valid preconditions", + env: cldf.Environment{DataStore: func() cldfdatastore.DataStore { + ds := cldfdatastore.NewMemoryDataStore() + err := ds.EnvMetadata().Set(envMetadata) + require.NoError(t, err) + + return ds.Seal() + }()}, + input: UpdateEnvMetadataChangesetInput{ + EnvMetadata: envMetadata, + }, + }, + { + name: "failure: missing datastore", + env: cldf.Environment{}, + input: UpdateEnvMetadataChangesetInput{ + EnvMetadata: envMetadata, + }, + wantErr: "missing datastore in environment", + }, + { + name: "failure: no env metadata given", + env: cldf.Environment{DataStore: cldfdatastore.NewMemoryDataStore().Seal()}, + input: UpdateEnvMetadataChangesetInput{ + EnvMetadata: cldfdatastore.EnvMetadata{}, + }, + wantErr: "missing env metadata input", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + err := UpdateEnvMetadataChangeset{}.VerifyPreconditions(tt.env, tt.input) + + if tt.wantErr == "" { + require.NoError(t, err) + } else { + require.ErrorContains(t, err, tt.wantErr) + } + }) + } +} + +func TestUpdateEnvMetadataChangeset_Apply(t *testing.T) { + t.Parallel() + + envMetadata := cldfdatastore.EnvMetadata{Metadata: "value"} + envMetadataUpdated := cldfdatastore.EnvMetadata{Metadata: "updated-value"} + + tests := []struct { + name string + env cldf.Environment + input UpdateEnvMetadataChangesetInput + want cldf.ChangesetOutput + wantErr string + }{ + { + name: "success: updates env metadata", + env: cldf.Environment{ + DataStore: testDataStoreWithEnvMetadata(t, envMetadata).Seal(), + OperationsBundle: cldfoperations.NewBundle(t.Context, cldflogger.Test(t), cldfoperations.NewMemoryReporter()), + }, + input: UpdateEnvMetadataChangesetInput{ + EnvMetadata: envMetadataUpdated, + }, + want: cldf.ChangesetOutput{ + DataStore: testDataStoreWithEnvMetadata(t, envMetadataUpdated), + Reports: []cldfoperations.Report[any, any]{{ + Def: cldfoperations.Definition{ + ID: "catalog-update-env-metadata", + Version: semver.MustParse("1.0.0"), + Description: "Update env metadata entries in the Catalog service", + }, + Input: operations.UpdateEnvMetadataInput{ + EnvMetadata: envMetadataUpdated, + }, + Output: operations.UpdateEnvMetadataOutput{ + DataStore: testDataStoreWithEnvMetadata(t, envMetadataUpdated), + }, + }}, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + t.Parallel() + + got, err := UpdateEnvMetadataChangeset{}.Apply(tt.env, tt.input) + + if tt.wantErr == "" { + require.NoError(t, err) + require.Empty(t, + cmp.Diff(tt.want, got, + cmpopts.IgnoreFields(cldfoperations.Report[any, any]{}, "ID", "Timestamp"), + cmpopts.IgnoreUnexported(cldfdatastore.MemoryAddressRefStore{}, cldfdatastore.MemoryChainMetadataStore{}, + cldfdatastore.MemoryContractMetadataStore{}, cldfdatastore.MemoryEnvMetadataStore{}))) + } else { + require.ErrorContains(t, err, tt.wantErr) + } + }) + } +} + +// ----- helpers ----- + +func testDataStoreWithEnvMetadata( + t *testing.T, metadata cldfdatastore.EnvMetadata, +) cldfdatastore.MutableDataStore { + t.Helper() + + ds := cldfdatastore.NewMemoryDataStore() + err := ds.EnvMetadata().Set(metadata) + require.NoError(t, err) + + return ds +} diff --git a/catalog/operations/update_env_metadata.go b/catalog/operations/update_env_metadata.go new file mode 100644 index 0000000..9ec2e17 --- /dev/null +++ b/catalog/operations/update_env_metadata.go @@ -0,0 +1,49 @@ +package operations + +import ( + "fmt" + + "github.com/Masterminds/semver/v3" + + cldfdatastore "github.com/smartcontractkit/chainlink-deployments-framework/datastore" + cldfops "github.com/smartcontractkit/chainlink-deployments-framework/operations" +) + +// UpdateEnvMetadataDeps holds non-serializable dependencies for the +// UpdateEnvMetadataOp operation. +type UpdateEnvMetadataDeps struct { + DataStore cldfdatastore.DataStore +} + +// UpdateEnvMetadataInput is the serializable input of an UpdateEnvMetadataOp invocation. +type UpdateEnvMetadataInput struct { + EnvMetadata cldfdatastore.EnvMetadata +} + +// UpdateEnvMetadataOutput is the serializable output of an UpdateEnvMetadataOp invocation. +type UpdateEnvMetadataOutput struct { + DataStore cldfdatastore.MutableDataStore +} + +// UpdateEnvMetadataOp updates existing env metadata entries in the Catalog service. +var UpdateEnvMetadataOp = cldfops.NewOperation( + "catalog-update-env-metadata", + semver.MustParse("1.0.0"), + "Update env metadata entries in the Catalog service", + func(b cldfops.Bundle, deps UpdateEnvMetadataDeps, input UpdateEnvMetadataInput) (UpdateEnvMetadataOutput, error) { + dataStore := cldfdatastore.NewMemoryDataStore() + err := dataStore.Merge(deps.DataStore) + if err != nil { + return UpdateEnvMetadataOutput{}, fmt.Errorf("failed to create memory data store: %w", err) + } + + err = dataStore.EnvMetadata().Set(input.EnvMetadata) + if err != nil { + return UpdateEnvMetadataOutput{}, fmt.Errorf("failed to update env metadata in catalog store: %w", err) + } + + b.Logger.Infow("Catalog EnvMetadata updated successfully") + + return UpdateEnvMetadataOutput{DataStore: dataStore}, nil + }, +)