Skip to content

Commit 68d4cda

Browse files
authored
fix!: make datastore delete inputs JSON-deserializable (#87)
The Input types for the three datastore delete changesets and their matching operations declared their key slices as `[]cldfdatastore.XKey`, where `XKey` is a framework interface with unexported implementations. `encoding/json` cannot unmarshal a JSON array into a slice of interface values, so any YAML/JSON pipeline driving these changesets failed at the deserialization boundary, and operation reports could not be re-executed from disk. Introduce `datastore/internal/keys/` with plain-DTO mirrors (`ChainMetadataKey`, `ContractMetadataKey`, `AddressRefKey`) of the framework key interfaces. Each exposes `ToFrameworkKey()` for conversion at the boundary; only `AddressRefKey.ToFrameworkKey()` can fail (returns `ErrAddressRefVersionRequired` when `Version` is nil). The six in-scope input fields (three changesets, three operations) now hold `[]keys.X` instead of `[]cldfdatastore.X`. The earlier inline `DeleteAddressRefKey` from PR #84 is removed in favor of the shared `keys.AddressRefKey`. Adds JSON-round-trip regression tests for each key type, JSON-driven end-to-end tests for each delete changeset, and operation-report round-trip tests for each delete operation. BREAKING CHANGE: `DeleteChainMetadataChangesetInput.ChainMetadataKeys`, `DeleteContractMetadataChangesetInput.ContractMetadataKeys`, and `DeleteAddressRefChangesetInput.AddressRefKeys` — plus the matching operation Input fields — now hold concrete key structs instead of the framework interface. The `DeleteAddressRefKey` type previously exported from `datastore/changesets/` is removed. In-Go callers should switch to JSON construction (no in-tree callers in chainlink-deployments today).
1 parent 0db7a36 commit 68d4cda

14 files changed

Lines changed: 467 additions & 59 deletions

datastore/changesets/delete_address_ref.go

Lines changed: 12 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4,48 +4,33 @@ import (
44
"errors"
55
"fmt"
66

7-
"github.com/Masterminds/semver/v3"
87
cldfdatastore "github.com/smartcontractkit/chainlink-deployments-framework/datastore"
98
cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"
109
cldfops "github.com/smartcontractkit/chainlink-deployments-framework/operations"
1110

11+
"github.com/smartcontractkit/cld-changesets/datastore/internal/keys"
1212
"github.com/smartcontractkit/cld-changesets/datastore/operations"
1313
)
1414

1515
// DeleteAddressRefChangeset deletes address ref entries from the Datastore.
1616
type DeleteAddressRefChangeset struct{}
1717

1818
type DeleteAddressRefChangesetInput struct {
19-
AddressRefKeys []DeleteAddressRefKey `json:"addressRefKeys"`
20-
}
21-
22-
type DeleteAddressRefKey struct {
23-
ChainSelector uint64 `json:"chainSelector"`
24-
Type cldfdatastore.ContractType `json:"type"`
25-
Version *semver.Version `json:"version"`
26-
Qualifier string `json:"qualifier"`
27-
}
28-
29-
func (k DeleteAddressRefKey) addressRefKey() (cldfdatastore.AddressRefKey, error) {
30-
if k.Version == nil {
31-
return nil, cldfdatastore.ErrAddressRefVersionRequired
32-
}
33-
34-
return cldfdatastore.NewAddressRefKey(k.ChainSelector, k.Type, k.Version, k.Qualifier), nil
19+
AddressRefKeys []keys.AddressRefKey `json:"addressRefKeys"`
3520
}
3621

3722
func (i DeleteAddressRefChangesetInput) addressRefKeys() ([]cldfdatastore.AddressRefKey, error) {
38-
keys := make([]cldfdatastore.AddressRefKey, 0, len(i.AddressRefKeys))
23+
fwKeys := make([]cldfdatastore.AddressRefKey, 0, len(i.AddressRefKeys))
3924
for idx, inputKey := range i.AddressRefKeys {
40-
key, err := inputKey.addressRefKey()
25+
key, err := inputKey.ToFrameworkKey()
4126
if err != nil {
4227
return nil, fmt.Errorf("addressRefKeys[%d]: %w", idx, err)
4328
}
4429

45-
keys = append(keys, key)
30+
fwKeys = append(fwKeys, key)
4631
}
4732

48-
return keys, nil
33+
return fwKeys, nil
4934
}
5035

5136
// VerifyPreconditions ensures the input is valid.
@@ -57,12 +42,12 @@ func (DeleteAddressRefChangeset) VerifyPreconditions(e cldf.Environment, input D
5742
return errors.New("missing datastore in environment")
5843
}
5944

60-
keys, err := input.addressRefKeys()
45+
fwKeys, err := input.addressRefKeys()
6146
if err != nil {
6247
return fmt.Errorf("invalid address ref keys input: %w", err)
6348
}
6449

65-
for _, key := range keys {
50+
for _, key := range fwKeys {
6651
_, err := e.DataStore.Addresses().Get(key)
6752
if err != nil {
6853
if errors.Is(err, cldfdatastore.ErrAddressRefNotFound) {
@@ -80,12 +65,12 @@ func (DeleteAddressRefChangeset) VerifyPreconditions(e cldf.Environment, input D
8065

8166
// Apply executes the changeset, staging the address refs to be deleted from the Datastore.
8267
func (DeleteAddressRefChangeset) Apply(e cldf.Environment, input DeleteAddressRefChangesetInput) (cldf.ChangesetOutput, error) {
83-
deps := operations.DeleteAddressRefDeps{DataStore: e.DataStore}
84-
addressRefKeys, err := input.addressRefKeys()
85-
if err != nil {
68+
if _, err := input.addressRefKeys(); err != nil {
8669
return cldf.ChangesetOutput{}, fmt.Errorf("invalid address ref keys input: %w", err)
8770
}
88-
opInput := operations.DeleteAddressRefInput{AddressRefKeys: addressRefKeys}
71+
72+
deps := operations.DeleteAddressRefDeps{DataStore: e.DataStore}
73+
opInput := operations.DeleteAddressRefInput{AddressRefKeys: input.AddressRefKeys}
8974

9075
report, err := cldfops.ExecuteOperation(e.OperationsBundle, operations.DeleteAddressRefOp, deps, opInput)
9176
out := cldf.ChangesetOutput{

datastore/changesets/delete_address_ref_test.go

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package changesets
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"testing"
67

@@ -11,6 +12,8 @@ import (
1112
cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"
1213
cldfoperations "github.com/smartcontractkit/chainlink-deployments-framework/operations"
1314
cldflogger "github.com/smartcontractkit/chainlink-deployments-framework/pkg/logger"
15+
16+
"github.com/smartcontractkit/cld-changesets/datastore/internal/keys"
1417
)
1518

1619
func TestDeleteAddressRefChangeset_VerifyPreconditions(t *testing.T) {
@@ -32,14 +35,14 @@ func TestDeleteAddressRefChangeset_VerifyPreconditions(t *testing.T) {
3235
DataStore: testDataStoreWithAddressRefs(t, addressRef1).Seal(),
3336
},
3437
input: DeleteAddressRefChangesetInput{
35-
AddressRefKeys: []DeleteAddressRefKey{deleteAddressRefKey(addressRef1), deleteAddressRefKey(addressRef2)},
38+
AddressRefKeys: []keys.AddressRefKey{addressRefKey(addressRef1), addressRefKey(addressRef2)},
3639
},
3740
},
3841
{
3942
name: "failure: missing datastore",
4043
env: cldf.Environment{},
4144
input: DeleteAddressRefChangesetInput{
42-
AddressRefKeys: []DeleteAddressRefKey{deleteAddressRefKey(addressRef1), deleteAddressRefKey(addressRef2)},
45+
AddressRefKeys: []keys.AddressRefKey{addressRefKey(addressRef1), addressRefKey(addressRef2)},
4346
},
4447
wantErr: "missing datastore in environment",
4548
},
@@ -48,15 +51,15 @@ func TestDeleteAddressRefChangeset_VerifyPreconditions(t *testing.T) {
4851
env: cldf.Environment{
4952
DataStore: cldfdatastore.NewMemoryDataStore().Seal(),
5053
},
51-
input: DeleteAddressRefChangesetInput{AddressRefKeys: []DeleteAddressRefKey{}},
54+
input: DeleteAddressRefChangesetInput{AddressRefKeys: []keys.AddressRefKey{}},
5255
wantErr: "missing address ref keys input",
5356
},
5457
{
5558
name: "failure: address ref entry does not exist",
5659
env: cldf.Environment{
5760
DataStore: cldfdatastore.NewMemoryDataStore().Seal(),
5861
},
59-
input: DeleteAddressRefChangesetInput{AddressRefKeys: []DeleteAddressRefKey{deleteAddressRefKey(addressRef2)}},
62+
input: DeleteAddressRefChangesetInput{AddressRefKeys: []keys.AddressRefKey{addressRefKey(addressRef2)}},
6063
wantErr: fmt.Sprintf("address ref entry for chain selector %v, type %v, version %v and qualifier %q does not exist", addressRef2.ChainSelector, addressRef2.Type, addressRef2.Version, addressRef2.Qualifier),
6164
},
6265
}
@@ -79,7 +82,7 @@ func TestDeleteAddressRefChangeset_MissingVersion(t *testing.T) {
7982
t.Parallel()
8083

8184
input := DeleteAddressRefChangesetInput{
82-
AddressRefKeys: []DeleteAddressRefKey{{
85+
AddressRefKeys: []keys.AddressRefKey{{
8386
ChainSelector: 1234,
8487
Type: "MyContract",
8588
Qualifier: "q1",
@@ -117,7 +120,7 @@ func TestDeleteAddressRefChangeset_Apply(t *testing.T) {
117120
OperationsBundle: cldfoperations.NewBundle(t.Context, cldflogger.Test(t), cldfoperations.NewMemoryReporter()),
118121
},
119122
input: DeleteAddressRefChangesetInput{
120-
AddressRefKeys: []DeleteAddressRefKey{deleteAddressRefKey(addressRef1), deleteAddressRefKey(addressRef2)},
123+
AddressRefKeys: []keys.AddressRefKey{addressRefKey(addressRef1), addressRefKey(addressRef2)},
121124
},
122125
wantDeletedKeys: []string{addressRef1.Key().String(), addressRef2.Key().String()},
123126
},
@@ -141,8 +144,37 @@ func TestDeleteAddressRefChangeset_Apply(t *testing.T) {
141144
}
142145
}
143146

144-
func deleteAddressRefKey(ref cldfdatastore.AddressRef) DeleteAddressRefKey {
145-
return DeleteAddressRefKey{
147+
func TestDeleteAddressRefChangeset_JSONRoundTrip(t *testing.T) {
148+
t.Parallel()
149+
150+
version := semver.MustParse("1.0.0")
151+
addressRef1 := cldfdatastore.AddressRef{Address: "0x01", ChainSelector: 1234, Type: "MyContract", Version: version, Qualifier: "q1"}
152+
addressRef2 := cldfdatastore.AddressRef{Address: "0x02", ChainSelector: 5678, Type: "OtherContract", Version: version, Qualifier: ""}
153+
154+
raw := `{"addressRefKeys":[{"chainSelector":1234,"type":"MyContract","version":"1.0.0","qualifier":"q1"},{"chainSelector":5678,"type":"OtherContract","version":"1.0.0","qualifier":""}]}`
155+
156+
var got DeleteAddressRefChangesetInput
157+
require.NoError(t, json.Unmarshal([]byte(raw), &got))
158+
require.Equal(t, []keys.AddressRefKey{addressRefKey(addressRef1), addressRefKey(addressRef2)}, got.AddressRefKeys)
159+
160+
env := cldf.Environment{
161+
DataStore: testDataStoreWithAddressRefs(t, addressRef1, addressRef2).Seal(),
162+
OperationsBundle: cldfoperations.NewBundle(t.Context, cldflogger.Test(t), cldfoperations.NewMemoryReporter()),
163+
}
164+
165+
require.NoError(t, DeleteAddressRefChangeset{}.VerifyPreconditions(env, got))
166+
167+
out, err := DeleteAddressRefChangeset{}.Apply(env, got)
168+
require.NoError(t, err)
169+
memDS := out.DataStore.(*cldfdatastore.MemoryDataStore)
170+
require.ElementsMatch(t,
171+
[]string{addressRef1.Key().String(), addressRef2.Key().String()},
172+
memDS.AddressRefStore.DeletedRemoteKeys,
173+
)
174+
}
175+
176+
func addressRefKey(ref cldfdatastore.AddressRef) keys.AddressRefKey {
177+
return keys.AddressRefKey{
146178
ChainSelector: ref.ChainSelector,
147179
Type: ref.Type,
148180
Version: ref.Version,

datastore/changesets/delete_chain_metadata.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ import (
88
cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"
99
cldfops "github.com/smartcontractkit/chainlink-deployments-framework/operations"
1010

11+
"github.com/smartcontractkit/cld-changesets/datastore/internal/keys"
1112
"github.com/smartcontractkit/cld-changesets/datastore/operations"
1213
)
1314

1415
// DeleteChainMetadataChangeset deletes chain metadata entries from the Datastore.
1516
type DeleteChainMetadataChangeset struct{}
1617

1718
type DeleteChainMetadataChangesetInput struct {
18-
ChainMetadataKeys []cldfdatastore.ChainMetadataKey `json:"chainMetadataKeys"`
19+
ChainMetadataKeys []keys.ChainMetadataKey `json:"chainMetadataKeys"`
1920
}
2021

2122
// VerifyPreconditions ensures the input is valid.
@@ -28,13 +29,14 @@ func (DeleteChainMetadataChangeset) VerifyPreconditions(e cldf.Environment, inpu
2829
}
2930

3031
for _, key := range input.ChainMetadataKeys {
31-
_, err := e.DataStore.ChainMetadata().Get(key)
32+
fwKey := key.ToFrameworkKey()
33+
_, err := e.DataStore.ChainMetadata().Get(fwKey)
3234
if err != nil {
3335
if errors.Is(err, cldfdatastore.ErrChainMetadataNotFound) {
34-
return fmt.Errorf("chain metadata entry for chain selector %v does not exist", key.ChainSelector())
36+
return fmt.Errorf("chain metadata entry for chain selector %v does not exist", fwKey.ChainSelector())
3537
}
3638

37-
return fmt.Errorf("failed to retrieve chain metadata entry for chain selector %v: %w", key.ChainSelector(), err)
39+
return fmt.Errorf("failed to retrieve chain metadata entry for chain selector %v: %w", fwKey.ChainSelector(), err)
3840
}
3941
}
4042

datastore/changesets/delete_chain_metadata_test.go

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package changesets
22

33
import (
4+
"encoding/json"
45
"fmt"
56
"testing"
67

@@ -10,6 +11,8 @@ import (
1011
cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"
1112
cldfoperations "github.com/smartcontractkit/chainlink-deployments-framework/operations"
1213
cldflogger "github.com/smartcontractkit/chainlink-deployments-framework/pkg/logger"
14+
15+
"github.com/smartcontractkit/cld-changesets/datastore/internal/keys"
1316
)
1417

1518
func TestDeleteChainMetadataChangeset_VerifyPreconditions(t *testing.T) {
@@ -30,14 +33,14 @@ func TestDeleteChainMetadataChangeset_VerifyPreconditions(t *testing.T) {
3033
DataStore: testDataStoreWithChainMetadata(t, chainMetadata1, chainMetadata2).Seal(),
3134
},
3235
input: DeleteChainMetadataChangesetInput{
33-
ChainMetadataKeys: []cldfdatastore.ChainMetadataKey{chainMetadata1.Key(), chainMetadata2.Key()},
36+
ChainMetadataKeys: []keys.ChainMetadataKey{{ChainSelector: chainMetadata1.ChainSelector}, {ChainSelector: chainMetadata2.ChainSelector}},
3437
},
3538
},
3639
{
3740
name: "failure: missing datastore",
3841
env: cldf.Environment{},
3942
input: DeleteChainMetadataChangesetInput{
40-
ChainMetadataKeys: []cldfdatastore.ChainMetadataKey{chainMetadata1.Key()},
43+
ChainMetadataKeys: []keys.ChainMetadataKey{{ChainSelector: chainMetadata1.ChainSelector}},
4144
},
4245
wantErr: "missing datastore in environment",
4346
},
@@ -46,15 +49,15 @@ func TestDeleteChainMetadataChangeset_VerifyPreconditions(t *testing.T) {
4649
env: cldf.Environment{
4750
DataStore: cldfdatastore.NewMemoryDataStore().Seal(),
4851
},
49-
input: DeleteChainMetadataChangesetInput{ChainMetadataKeys: []cldfdatastore.ChainMetadataKey{}},
52+
input: DeleteChainMetadataChangesetInput{ChainMetadataKeys: []keys.ChainMetadataKey{}},
5053
wantErr: "missing chain metadata keys input",
5154
},
5255
{
5356
name: "failure: chain metadata entry does not exist",
5457
env: cldf.Environment{
5558
DataStore: cldfdatastore.NewMemoryDataStore().Seal(),
5659
},
57-
input: DeleteChainMetadataChangesetInput{ChainMetadataKeys: []cldfdatastore.ChainMetadataKey{chainMetadata2.Key()}},
60+
input: DeleteChainMetadataChangesetInput{ChainMetadataKeys: []keys.ChainMetadataKey{{ChainSelector: chainMetadata2.ChainSelector}}},
5861
wantErr: fmt.Sprintf("chain metadata entry for chain selector %v does not exist", chainMetadata2.ChainSelector),
5962
},
6063
}
@@ -93,7 +96,7 @@ func TestDeleteChainMetadataChangeset_Apply(t *testing.T) {
9396
OperationsBundle: cldfoperations.NewBundle(t.Context, cldflogger.Test(t), cldfoperations.NewMemoryReporter()),
9497
},
9598
input: DeleteChainMetadataChangesetInput{
96-
ChainMetadataKeys: []cldfdatastore.ChainMetadataKey{chainMetadata1.Key(), chainMetadata2.Key()},
99+
ChainMetadataKeys: []keys.ChainMetadataKey{{ChainSelector: chainMetadata1.ChainSelector}, {ChainSelector: chainMetadata2.ChainSelector}},
97100
},
98101
wantDeletedKeys: []string{chainMetadata1.Key().String(), chainMetadata2.Key().String()},
99102
},
@@ -116,3 +119,34 @@ func TestDeleteChainMetadataChangeset_Apply(t *testing.T) {
116119
})
117120
}
118121
}
122+
123+
func TestDeleteChainMetadataChangeset_JSONRoundTrip(t *testing.T) {
124+
t.Parallel()
125+
126+
chainMetadata1 := cldfdatastore.ChainMetadata{ChainSelector: 1234, Metadata: "value1"}
127+
chainMetadata2 := cldfdatastore.ChainMetadata{ChainSelector: 5678, Metadata: "value2"}
128+
129+
raw := `{"chainMetadataKeys":[{"chainSelector":1234},{"chainSelector":5678}]}`
130+
131+
var got DeleteChainMetadataChangesetInput
132+
require.NoError(t, json.Unmarshal([]byte(raw), &got))
133+
require.Equal(t,
134+
[]keys.ChainMetadataKey{{ChainSelector: 1234}, {ChainSelector: 5678}},
135+
got.ChainMetadataKeys,
136+
)
137+
138+
env := cldf.Environment{
139+
DataStore: testDataStoreWithChainMetadata(t, chainMetadata1, chainMetadata2).Seal(),
140+
OperationsBundle: cldfoperations.NewBundle(t.Context, cldflogger.Test(t), cldfoperations.NewMemoryReporter()),
141+
}
142+
143+
require.NoError(t, DeleteChainMetadataChangeset{}.VerifyPreconditions(env, got))
144+
145+
out, err := DeleteChainMetadataChangeset{}.Apply(env, got)
146+
require.NoError(t, err)
147+
memDS := out.DataStore.(*cldfdatastore.MemoryDataStore)
148+
require.ElementsMatch(t,
149+
[]string{chainMetadata1.Key().String(), chainMetadata2.Key().String()},
150+
memDS.ChainMetadataStore.DeletedRemoteKeys,
151+
)
152+
}

datastore/changesets/delete_contract_metadata.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,15 @@ import (
88
cldf "github.com/smartcontractkit/chainlink-deployments-framework/deployment"
99
cldfops "github.com/smartcontractkit/chainlink-deployments-framework/operations"
1010

11+
"github.com/smartcontractkit/cld-changesets/datastore/internal/keys"
1112
"github.com/smartcontractkit/cld-changesets/datastore/operations"
1213
)
1314

1415
// DeleteContractMetadataChangeset deletes contract metadata entries from the Datastore.
1516
type DeleteContractMetadataChangeset struct{}
1617

1718
type DeleteContractMetadataChangesetInput struct {
18-
ContractMetadataKeys []cldfdatastore.ContractMetadataKey `json:"contractMetadataKeys"`
19+
ContractMetadataKeys []keys.ContractMetadataKey `json:"contractMetadataKeys"`
1920
}
2021

2122
// VerifyPreconditions ensures the input is valid.
@@ -28,15 +29,16 @@ func (DeleteContractMetadataChangeset) VerifyPreconditions(e cldf.Environment, i
2829
}
2930

3031
for _, key := range input.ContractMetadataKeys {
31-
_, err := e.DataStore.ContractMetadata().Get(key)
32+
fwKey := key.ToFrameworkKey()
33+
_, err := e.DataStore.ContractMetadata().Get(fwKey)
3234
if err != nil {
3335
if errors.Is(err, cldfdatastore.ErrContractMetadataNotFound) {
3436
return fmt.Errorf("contract metadata entry for chain selector %v and address %v does not exist",
35-
key.ChainSelector(), key.Address())
37+
fwKey.ChainSelector(), fwKey.Address())
3638
}
3739

3840
return fmt.Errorf("failed to retrieve contract metadata entry for chain selector %v and address %v: %w",
39-
key.ChainSelector(), key.Address(), err)
41+
fwKey.ChainSelector(), fwKey.Address(), err)
4042
}
4143
}
4244

0 commit comments

Comments
 (0)