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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 59 additions & 0 deletions catalog/changesets/delete_chain_metadata.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package changesets

import (
"errors"
"fmt"

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"
)

// DeleteChainMetadataChangeset deletes chain metadata entries from the Catalog service.
type DeleteChainMetadataChangeset struct{}

type DeleteChainMetadataChangesetInput struct {
ChainMetadataKeys []cldfdatastore.ChainMetadataKey `json:"chainMetadataKeys"`
}

// VerifyPreconditions ensures the input is valid.
func (DeleteChainMetadataChangeset) VerifyPreconditions(e cldf.Environment, input DeleteChainMetadataChangesetInput) error {
if len(input.ChainMetadataKeys) == 0 {
return errors.New("missing chain metadata keys input")
}
if e.DataStore == nil {
return errors.New("missing datastore in environment")
}

for _, key := range input.ChainMetadataKeys {
_, err := e.DataStore.ChainMetadata().Get(key)
if err != nil {
if errors.Is(err, cldfdatastore.ErrChainMetadataNotFound) {
return fmt.Errorf("chain metadata entry for chain selector %v does not exist", key.ChainSelector())
}

return fmt.Errorf("failed to retrieve chain metadata entry for chain selector %v: %w", key.ChainSelector(), err)
}
}

return nil
}

// Apply executes the changeset, staging the chain metadata entries to be deleted from the Catalog service or local datastore files.
func (DeleteChainMetadataChangeset) Apply(e cldf.Environment, input DeleteChainMetadataChangesetInput) (cldf.ChangesetOutput, error) {
deps := operations.DeleteChainMetadataDeps{DataStore: e.DataStore}
opInput := operations.DeleteChainMetadataInput{ChainMetadataKeys: input.ChainMetadataKeys}

report, err := cldfops.ExecuteOperation(e.OperationsBundle, operations.DeleteChainMetadataOp, 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
}
118 changes: 118 additions & 0 deletions catalog/changesets/delete_chain_metadata_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package changesets

import (
"fmt"
"testing"

"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"
)

func TestDeleteChainMetadataChangeset_VerifyPreconditions(t *testing.T) {
t.Parallel()

chainMetadata1 := cldfdatastore.ChainMetadata{ChainSelector: 1234, Metadata: "value1"}
chainMetadata2 := cldfdatastore.ChainMetadata{ChainSelector: 5678, Metadata: "value2"}

tests := []struct {
name string
env cldf.Environment
input DeleteChainMetadataChangesetInput
wantErr string
}{
{
name: "success: valid preconditions",
env: cldf.Environment{
DataStore: testDataStoreWithChainMetadata(t, chainMetadata1, chainMetadata2).Seal(),
},
input: DeleteChainMetadataChangesetInput{
ChainMetadataKeys: []cldfdatastore.ChainMetadataKey{chainMetadata1.Key(), chainMetadata2.Key()},
},
},
{
name: "failure: missing datastore",
env: cldf.Environment{},
input: DeleteChainMetadataChangesetInput{
ChainMetadataKeys: []cldfdatastore.ChainMetadataKey{chainMetadata1.Key()},
},
wantErr: "missing datastore in environment",
},
{
name: "failure: no chain metadata keys given",
env: cldf.Environment{
DataStore: cldfdatastore.NewMemoryDataStore().Seal(),
},
input: DeleteChainMetadataChangesetInput{ChainMetadataKeys: []cldfdatastore.ChainMetadataKey{}},
wantErr: "missing chain metadata keys input",
},
{
name: "failure: chain metadata entry does not exist",
env: cldf.Environment{
DataStore: cldfdatastore.NewMemoryDataStore().Seal(),
},
input: DeleteChainMetadataChangesetInput{ChainMetadataKeys: []cldfdatastore.ChainMetadataKey{chainMetadata2.Key()}},
wantErr: fmt.Sprintf("chain metadata entry for chain selector %v does not exist", chainMetadata2.ChainSelector),
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

err := DeleteChainMetadataChangeset{}.VerifyPreconditions(tt.env, tt.input)
if tt.wantErr == "" {
require.NoError(t, err)
} else {
require.ErrorContains(t, err, tt.wantErr)
}
})
}
}

func TestDeleteChainMetadataChangeset_Apply(t *testing.T) {
t.Parallel()

chainMetadata1 := cldfdatastore.ChainMetadata{ChainSelector: 1234, Metadata: "value1"}
chainMetadata2 := cldfdatastore.ChainMetadata{ChainSelector: 5678, Metadata: "value2"}

tests := []struct {
name string
env cldf.Environment
input DeleteChainMetadataChangesetInput
wantDeletedKeys []string
wantErr string
}{
{
name: "success: stages two chain metadata entries for deletion",
env: cldf.Environment{
DataStore: testDataStoreWithChainMetadata(t, chainMetadata1, chainMetadata2).Seal(),
OperationsBundle: cldfoperations.NewBundle(t.Context, cldflogger.Test(t), cldfoperations.NewMemoryReporter()),
},
input: DeleteChainMetadataChangesetInput{
ChainMetadataKeys: []cldfdatastore.ChainMetadataKey{chainMetadata1.Key(), chainMetadata2.Key()},
},
wantDeletedKeys: []string{chainMetadata1.Key().String(), chainMetadata2.Key().String()},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()

got, err := DeleteChainMetadataChangeset{}.Apply(tt.env, tt.input)

if tt.wantErr == "" {
require.NoError(t, err)
require.Len(t, got.Reports, 1)
memDS := got.DataStore.(*cldfdatastore.MemoryDataStore)
require.ElementsMatch(t, tt.wantDeletedKeys, memDS.ChainMetadataStore.DeletedRemoteKeys)
} else {
require.ErrorContains(t, err, tt.wantErr)
}
})
}
}
49 changes: 49 additions & 0 deletions catalog/operations/delete_chain_metadata.go
Original file line number Diff line number Diff line change
@@ -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"
)

// DeleteChainMetadataDeps holds non-serializable dependencies for the DeleteChainMetadataOp operation.
type DeleteChainMetadataDeps struct {
DataStore cldfdatastore.DataStore
}

// DeleteChainMetadataInput is the serializable input of a DeleteChainMetadataOp invocation.
type DeleteChainMetadataInput struct {
ChainMetadataKeys []cldfdatastore.ChainMetadataKey
}

// DeleteChainMetadataOutput is the serializable output of a DeleteChainMetadataOp invocation.
type DeleteChainMetadataOutput struct {
DataStore cldfdatastore.MutableDataStore
}

// DeleteChainMetadataOp deletes chain metadata entries from the Catalog service or local datastore files.
var DeleteChainMetadataOp = cldfops.NewOperation(
"datastore-delete-chain-metadata",
semver.MustParse("1.0.0"),
"Delete chain metadata entries from the Catalog service or local datastore files",
func(b cldfops.Bundle, deps DeleteChainMetadataDeps, input DeleteChainMetadataInput) (DeleteChainMetadataOutput, error) {
dataStore := cldfdatastore.NewMemoryDataStore()
err := dataStore.Merge(deps.DataStore)
if err != nil {
return DeleteChainMetadataOutput{}, fmt.Errorf("failed to create memory data store: %w", err)
}

for i, key := range input.ChainMetadataKeys {
err = dataStore.ChainMetadata().RemoteDelete(key)
if err != nil {
return DeleteChainMetadataOutput{}, fmt.Errorf("failed to delete chain metadata entry %d in datastore: %w", i, err)
}
}

b.Logger.Infow("Catalog ChainMetadata successfully staged for deletion")

return DeleteChainMetadataOutput{DataStore: dataStore}, nil
},
)
Loading