Repository: TBD54566975/ssi-service
Pull request #390 (2023-04-25 00:12:35)

Fix ION returning DID Key objects
- fix ion returning DID key objects
- refactored DB options
- updated config examples
- now able to run the ssi service without docker again 😄 

codecov-commenter: ## [Codecov](https://codecov.io/gh/TBD54566975/ssi-service/pull/390?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) Report
> Merging [#390](https://codecov.io/gh/TBD54566975/ssi-service/pull/390?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) (572c055) into [main](https://codecov.io/gh/TBD54566975/ssi-service/commit/27c4d7dc2671b26bd6c89c679b22c9e534b0f540?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) (27c4d7d) will **decrease** coverage by `0.03%`.
> The diff coverage is `28.75%`.

```diff
@@            Coverage Diff             @@
##             main     #390      +/-   ##
==========================================
- Coverage   20.44%   20.42%   -0.03%     
==========================================
  Files          48       48              
  Lines        5380     5426      +46     
==========================================
+ Hits         1100     1108       +8     
- Misses       4116     4142      +26     
- Partials      164      176      +12     
```


| [Impacted Files](https://codecov.io/gh/TBD54566975/ssi-service/pull/390?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975) | Coverage Δ | |
|---|---|---|
| [config/config.go](https://codecov.io/gh/TBD54566975/ssi-service/pull/390?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975#diff-Y29uZmlnL2NvbmZpZy5nbw==) | `16.56% <0.00%> (+0.41%)` | :arrow_up: |
| [pkg/service/did/ion.go](https://codecov.io/gh/TBD54566975/ssi-service/pull/390?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975#diff-cGtnL3NlcnZpY2UvZGlkL2lvbi5nbw==) | `49.48% <0.00%> (ø)` | |
| [pkg/storage/bolt.go](https://codecov.io/gh/TBD54566975/ssi-service/pull/390?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975#diff-cGtnL3N0b3JhZ2UvYm9sdC5nbw==) | `60.75% <28.57%> (-3.40%)` | :arrow_down: |
| [pkg/storage/redis.go](https://codecov.io/gh/TBD54566975/ssi-service/pull/390?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975#diff-cGtnL3N0b3JhZ2UvcmVkaXMuZ28=) | `51.62% <31.81%> (-4.68%)` | :arrow_down: |
| [internal/keyaccess/dataintegrity.go](https://codecov.io/gh/TBD54566975/ssi-service/pull/390?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975#diff-aW50ZXJuYWwva2V5YWNjZXNzL2RhdGFpbnRlZ3JpdHkuZ28=) | `58.33% <50.00%> (ø)` | |
| [pkg/storage/storage.go](https://codecov.io/gh/TBD54566975/ssi-service/pull/390?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975#diff-cGtnL3N0b3JhZ2Uvc3RvcmFnZS5nbw==) | `40.62% <100.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=TBD54566975)

decentralgabe: @andresuribe87 going to merge, if anything blocking we can re open or discuss more in #340 

Patch:
diff --git a/config/compose.toml b/config/compose.toml
index 1f53666..092fef3 100644
--- a/config/compose.toml
+++ b/config/compose.toml
@@ -27,9 +27,13 @@ enable_allow_all_cors = true
 service_endpoint = "http://localhost:8080"
 storage = "redis"
 
-[services.storage_option]
-address = "redis:6379"
-password = ""
+[[services.storage_option]]
+id = "redis-address-option"
+option = "redis:6379"
+
+[[services.storage_option]]
+id = "storage-password-option"
+option = "password"
 
 # per-service configuration
 [services.keystore]
diff --git a/config/config.go b/config/config.go
index 25a50a3..1c18365 100644
--- a/config/config.go
+++ b/config/config.go
@@ -12,14 +12,16 @@ import (
 	"github.com/joho/godotenv"
 	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
+
+	"github.com/tbd54566975/ssi-service/pkg/storage"
 )
 
 const (
 	DefaultConfigPath = "config/config.toml"
 	DefaultEnvPath    = "config/.env"
-	ConfigFileName    = "config.toml"
+	Filename          = "config.toml"
 	ServiceName       = "ssi-service"
-	ConfigExtension   = ".toml"
+	Extension         = ".toml"
 
 	DefaultServiceEndpoint = "http://localhost:8080"
 
@@ -29,6 +31,10 @@ const (
 
 type EnvironmentVariable string
 
+func (e EnvironmentVariable) String() string {
+	return string(e)
+}
+
 type SSIServiceConfig struct {
 	conf.Version
 	Server   ServerConfig   `toml:"server"`
@@ -66,9 +72,9 @@ type ServicesConfig struct {
 	// at present, it is assumed that a single storage provider works for all services
 	// in the future it may make sense to have per-service storage providers (e.g. mysql for one service,
 	// mongo for another)
-	StorageProvider string      `toml:"storage"`
-	StorageOption   interface{} `toml:"storage_option"`
-	ServiceEndpoint string      `toml:"service_endpoint"`
+	StorageProvider string           `toml:"storage"`
+	StorageOptions  []storage.Option `toml:"storage_option"`
+	ServiceEndpoint string           `toml:"service_endpoint"`
 
 	// Embed all service-specific configs here. The order matters: from which should be instantiated first, to last
 	KeyStoreConfig       KeyStoreServiceConfig     `toml:"keystore,omitempty"`
@@ -211,7 +217,7 @@ func checkValidConfigPath(path string) (bool, error) {
 	if path == "" {
 		logrus.Info("no config path provided, loading default config...")
 		defaultConfig = true
-	} else if filepath.Ext(path) != ConfigExtension {
+	} else if filepath.Ext(path) != Extension {
 		return false, fmt.Errorf("path<%s> did not match the expected TOML format", path)
 	}
 
@@ -309,26 +315,21 @@ func applyEnvVariables(config *SSIServiceConfig) error {
 		return errors.Wrap(err, "dotenv parsing")
 	}
 
-	keystorePassword, present := os.LookupEnv(string(KeystorePassword))
-
+	keystorePassword, present := os.LookupEnv(KeystorePassword.String())
 	if present {
 		config.Services.KeyStoreConfig.ServiceKeyPassword = keystorePassword
 	}
 
-	dbPassword, present := os.LookupEnv(string(DBPassword))
-
+	dbPassword, present := os.LookupEnv(DBPassword.String())
 	if present {
-		if config.Services.StorageOption == nil {
-			config.Services.StorageOption = make(map[string]interface{})
-		}
-
-		storageOptionMap, ok := config.Services.StorageOption.(map[string]interface{})
-		if !ok {
-			return errors.New("storage option must be of type map[string]interface{}")
+		if len(config.Services.StorageOptions) != 0 {
+			for _, storageOption := range config.Services.StorageOptions {
+				if storageOption.ID == storage.PasswordOption {
+					storageOption.Option = dbPassword
+					break
+				}
+			}
 		}
-
-		storageOptionMap["password"] = dbPassword
-		config.Services.StorageOption = storageOptionMap
 	}
 
 	return nil
diff --git a/config/config.toml b/config/config.toml
index 7e928d2..aa76489 100644
--- a/config/config.toml
+++ b/config/config.toml
@@ -25,11 +25,24 @@ enable_allow_all_cors = true
 # Storage Configuration
 [services]
 service_endpoint = "http://localhost:3000"
-storage = "redis"
 
-[services.storage_option]
-address = "localhost:6379"
-password = ""
+# example bolt config with filepath option
+storage = "bolt"
+
+[[services.storage_option]]
+id = "boltdb-filepath-option"
+option = "bolt.db"
+
+# example redis config with two options
+# storage = "redis"
+#
+# [[services.storage_option]]
+# id = "redis-address-option"
+# option = "localhost:6379"
+#
+# [[services.storage_option]]
+# id = "storage-password-option"
+# option = "password"
 
 # per-service configuration
 [services.keystore]
@@ -38,7 +51,7 @@ password = "default-password"
 
 [services.did]
 name = "did"
-methods = ["key", "web", "ion"]
+methods = ["key", "web"]
 local_resolution_methods = ["key", "web", "pkh", "peer"]
 universal_resolver_url = "http://localhost:8088"
 universal_resolver_methods = ["ion"]
diff --git a/config/config.toml.example b/config/config.toml.example
index 75ba0b9..e7e5ee2 100644
--- a/config/config.toml.example
+++ b/config/config.toml.example
@@ -19,20 +19,29 @@ log_level = "debug"
 
 enable_schema_caching = true
 
+[services]
+service_endpoint = "http://localhost:8080"
+
 # Uncomment one of the following database configurations
+
 # Bolt Configuration
-# [services]
-# service_endpoint = "http://localhost:8080"
 # storage = "bolt"
+# [[services.storage_option]]
+# id = "boltdb-filepath-option"
+# option = "bolt.db"
 
 # Redis Configuration
 # [services]
 # service_endpoint = "http://localhost:8080"
 # storage = "redis"
 
-# [services.storage_option]
-# address = "redis:6379"
-# password = ""
+# [[services.storage_option]]
+# id = "redis-address-option"
+# option = "localhost:6379"
+
+# [[services.storage_option]]
+# id = "storage-password-option"
+# option = "password"
 
 # per-service configuration
 [services.keystore]
diff --git a/internal/keyaccess/dataintegrity.go b/internal/keyaccess/dataintegrity.go
index 7c9b351..9473bcf 100644
--- a/internal/keyaccess/dataintegrity.go
+++ b/internal/keyaccess/dataintegrity.go
@@ -78,10 +78,10 @@ func (ka DataIntegrityKeyAccess) Verify(payload cryptosuite.Provable) error {
 	return nil
 }
 
-func (ka DataIntegrityKeyAccess) SignVerifiablePresentation(audience string, presentation credential.VerifiablePresentation) (*DataIntegrityJSON, error) {
+func (ka DataIntegrityKeyAccess) SignVerifiablePresentation(_ string, _ credential.VerifiablePresentation) (*DataIntegrityJSON, error) {
 	return nil, errors.New("not implemented")
 }
 
-func (ka DataIntegrityKeyAccess) VerifyVerifiablePresentation(presentation cryptosuite.Provable) error {
+func (ka DataIntegrityKeyAccess) VerifyVerifiablePresentation(_ cryptosuite.Provable) error {
 	return errors.New("not implemented")
 }
diff --git a/magefile.go b/magefile.go
index f27e5e9..90b4f62 100644
--- a/magefile.go
+++ b/magefile.go
@@ -189,7 +189,7 @@ func ColorizeTestOutput(w io.Writer) io.Writer {
 }
 
 func ColorizeTestStdout() io.Writer {
-	stdout := int(syscall.Stdout)
+	stdout := syscall.Stdout
 	if terminal.IsTerminal(stdout) {
 		return ColorizeTestOutput(os.Stdout)
 	}
diff --git a/pkg/service/did/ion.go b/pkg/service/did/ion.go
index 2b5f18d..4562341 100644
--- a/pkg/service/did/ion.go
+++ b/pkg/service/did/ion.go
@@ -257,7 +257,7 @@ func (h *ionHandler) GetDID(ctx context.Context, request GetDIDRequest) (*GetDID
 func (h *ionHandler) GetDIDs(ctx context.Context) (*GetDIDsResponse, error) {
 	logrus.Debug("getting stored did:ion DIDs")
 
-	gotDIDs, err := h.storage.GetDIDs(ctx, did.KeyMethod.String(), new(ionStoredDID))
+	gotDIDs, err := h.storage.GetDIDs(ctx, did.IONMethod.String(), new(ionStoredDID))
 	if err != nil {
 		return nil, fmt.Errorf("error getting did:ion DIDs")
 	}
diff --git a/pkg/service/service.go b/pkg/service/service.go
index 51e0af3..df77540 100644
--- a/pkg/service/service.go
+++ b/pkg/service/service.go
@@ -85,7 +85,7 @@ func (ssi *SSIService) GetService(serviceType framework.Type) framework.Service
 
 // instantiateServices begins all instantiates and their dependencies
 func instantiateServices(config config.ServicesConfig) ([]framework.Service, error) {
-	storageProvider, err := storage.NewStorage(storage.Type(config.StorageProvider), config.StorageOption)
+	storageProvider, err := storage.NewStorage(storage.Type(config.StorageProvider), config.StorageOptions...)
 	if err != nil {
 		return nil, sdkutil.LoggingErrorMsgf(err, "could not instantiate storage provider: %s", config.StorageProvider)
 	}
diff --git a/pkg/storage/bolt.go b/pkg/storage/bolt.go
index 9ecc6aa..750d60b 100644
--- a/pkg/storage/bolt.go
+++ b/pkg/storage/bolt.go
@@ -21,7 +21,8 @@ func init() {
 }
 
 const (
-	DBFilePrefix = "ssi-service"
+	DBFilePrefix                   = "ssi-service"
+	BoltDBFilePathOption OptionKey = "boltdb-filepath-option"
 )
 
 type BoltDB struct {
@@ -29,20 +30,16 @@ type BoltDB struct {
 }
 
 // Init instantiates a file-based storage instance for Bolt https://github.com/boltdb/bolt
-func (b *BoltDB) Init(options interface{}) error {
+func (b *BoltDB) Init(opts ...Option) error {
 	if b.db != nil && b.IsOpen() {
-		return fmt.Errorf("bolit db already opened with name %s", b.URI())
+		return fmt.Errorf("bolt db already opened with name %s", b.URI())
 	}
-	dbFilePath := fmt.Sprintf("%s_%s.db", DBFilePrefix, b.Type())
-	if options != nil {
-		customPath, ok := options.(string)
-		if !ok {
-			return fmt.Errorf("options should be a string value")
-		}
-		if customPath != "" {
-			dbFilePath = customPath
-		}
+	defaultDBFilePath := fmt.Sprintf("%s_%s.db", DBFilePrefix, b.Type())
+	dbFilePath, err := processBoltOptions(defaultDBFilePath, opts...)
+	if err != nil {
+		return errors.Wrap(err, "processing bolt options")
 	}
+
 	db, err := bolt.Open(dbFilePath, 0600, &bolt.Options{Timeout: 3 * time.Second})
 	if err != nil {
 		return err
@@ -51,6 +48,23 @@ func (b *BoltDB) Init(options interface{}) error {
 	return nil
 }
 
+func processBoltOptions(filePath string, opts ...Option) (string, error) {
+	if len(opts) == 0 {
+		return filePath, nil
+	}
+	if len(opts) > 1 {
+		return filePath, fmt.Errorf("invalid number of options provided")
+	}
+	if opts[0].ID != BoltDBFilePathOption {
+		return filePath, fmt.Errorf("invalid option provided: %s", opts[0].ID)
+	}
+	customFilePath, ok := opts[0].Option.(string)
+	if !ok || customFilePath == "" {
+		return filePath, fmt.Errorf("options should be a non-empty string value")
+	}
+	return customFilePath, nil
+}
+
 // URI return filepath of boltDB,
 func (b *BoltDB) URI() string {
 	return b.db.Path()
diff --git a/pkg/storage/redis.go b/pkg/storage/redis.go
index 48d9569..2d03dc7 100644
--- a/pkg/storage/redis.go
+++ b/pkg/storage/redis.go
@@ -20,10 +20,11 @@ func init() {
 }
 
 const (
-	NamespaceKeySeparator = ":"
-	Pong                  = "PONG"
-	RedisScanBatchSize    = 1000
-	MaxElapsedTime        = 6 * time.Second
+	NamespaceKeySeparator           = ":"
+	Pong                            = "PONG"
+	RedisScanBatchSize              = 1000
+	MaxElapsedTime                  = 6 * time.Second
+	RedisAddressOption    OptionKey = "redis-address-option"
 )
 
 type RedisDB struct {
@@ -39,12 +40,14 @@ func (rtx *redisTx) Write(ctx context.Context, namespace, key string, value []by
 	return rtx.pipe.Set(ctx, nameSpaceKey, value, 0).Err()
 }
 
-func (b *RedisDB) Init(i interface{}) error {
-	options := i.(map[string]interface{})
-
+func (b *RedisDB) Init(opts ...Option) error {
+	address, password, err := processRedisOptions(opts...)
+	if err != nil {
+		return errors.Wrap(err, "processing redis options")
+	}
 	client := goredislib.NewClient(&goredislib.Options{
-		Addr:     options["address"].(string),
-		Password: options["password"].(string),
+		Addr:     address,
+		Password: password,
 	})
 
 	if err := redisotel.InstrumentTracing(client); err != nil {
@@ -60,6 +63,43 @@ func (b *RedisDB) Init(i interface{}) error {
 	return nil
 }
 
+func processRedisOptions(opts ...Option) (address, password string, err error) {
+	if len(opts) != 2 {
+		return "", "", errors.New("redis options must contain address and password")
+	}
+	for _, opt := range opts {
+		switch opt.ID {
+		case RedisAddressOption:
+			maybeAddress, ok := opt.Option.(string)
+			if !ok {
+				err = errors.New("redis address must be a string")
+				return
+			}
+			if len(maybeAddress) == 0 {
+				err = errors.New("redis address must not be empty")
+				return
+			}
+			address = maybeAddress
+		case PasswordOption:
+			maybePassword, ok := opt.Option.(string)
+			if !ok {
+				err = errors.New("redis password must be a string")
+				return
+			}
+			if len(maybePassword) == 0 {
+				err = errors.New("redis password must not be empty")
+				return
+			}
+			password = maybePassword
+		}
+	}
+	if len(address) == 0 || len(password) == 0 {
+		err = errors.New("redis address and password must not be empty")
+		return
+	}
+	return address, password, nil
+}
+
 func (b *RedisDB) URI() string {
 	return b.db.Options().Addr
 }
diff --git a/pkg/storage/storage.go b/pkg/storage/storage.go
index a70b755..f99bfcb 100644
--- a/pkg/storage/storage.go
+++ b/pkg/storage/storage.go
@@ -22,15 +22,29 @@ type Tx interface {
 const (
 	Bolt  Type = "bolt"
 	Redis Type = "redis"
+
+	// Common options
+	PasswordOption OptionKey = "storage-password-option"
 )
 
 var (
 	availableStorages = make(map[Type]ServiceStorage)
 )
 
+type (
+	// OptionKey uniquely represents an option to be used in a storage provider
+	OptionKey string
+)
+
+// Option represents a single option that may be required for a storage provider
+type Option struct {
+	ID     OptionKey `json:"id,omitempty"`
+	Option any       `json:"option,omitempty"`
+}
+
 // ServiceStorage describes the api for storage independent of DB providers
 type ServiceStorage interface {
-	Init(interface{}) error
+	Init(opts ...Option) error
 	Type() Type
 	URI() string
 	IsOpen() bool
@@ -51,12 +65,12 @@ type ServiceStorage interface {
 
 // NewStorage returns the instance of the given storageProvider. If it doesn't exist, then a default implementation
 // is created with the given option parameter.
-func NewStorage(storageProvider Type, option interface{}) (ServiceStorage, error) {
+func NewStorage(storageProvider Type, opts ...Option) (ServiceStorage, error) {
 	impl := GetStorage(storageProvider)
 	if impl == nil {
 		return impl, fmt.Errorf("unsupported storage provider: %s", storageProvider)
 	}
-	err := impl.Init(option)
+	err := impl.Init(opts...)
 	return impl, err
 }
 


Test patch:
diff --git a/config/config_test.go b/config/config_test.go
index 42d1983..a40cedf 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -7,7 +7,7 @@ import (
 )
 
 func TestConfig(t *testing.T) {
-	config, err := LoadConfig(ConfigFileName)
+	config, err := LoadConfig(Filename)
 	assert.NoError(t, err)
 	assert.NotEmpty(t, config)
 
diff --git a/pkg/server/router/router_test.go b/pkg/server/router/router_test.go
index 7e263dd..34f68a8 100644
--- a/pkg/server/router/router_test.go
+++ b/pkg/server/router/router_test.go
@@ -39,8 +39,12 @@ func setupTestDB(t *testing.T) storage.ServiceStorage {
 	file, err := os.CreateTemp("", "bolt")
 	require.NoError(t, err)
 	name := file.Name()
-	file.Close()
-	s, err := storage.NewStorage(storage.Bolt, name)
+	err = file.Close()
+	require.NoError(t, err)
+	s, err := storage.NewStorage(storage.Bolt, storage.Option{
+		ID:     storage.BoltDBFilePathOption,
+		Option: name,
+	})
 	require.NoError(t, err)
 	t.Cleanup(func() {
 		_ = s.Close()
diff --git a/pkg/server/server_operation_test.go b/pkg/server/server_operation_test.go
index 7b2eca0..f9f2f01 100644
--- a/pkg/server/server_operation_test.go
+++ b/pkg/server/server_operation_test.go
@@ -294,7 +294,10 @@ func setupTestDB(t *testing.T) storage.ServiceStorage {
 	file, err := os.CreateTemp("", "bolt")
 	require.NoError(t, err)
 	name := file.Name()
-	s, err := storage.NewStorage(storage.Bolt, name)
+	s, err := storage.NewStorage(storage.Bolt, storage.Option{
+		ID:     storage.BoltDBFilePathOption,
+		Option: name,
+	})
 	require.NoError(t, err)
 	t.Cleanup(func() {
 		_ = s.Close()
diff --git a/pkg/server/server_test.go b/pkg/server/server_test.go
index 83ea58d..f0fa455 100644
--- a/pkg/server/server_test.go
+++ b/pkg/server/server_test.go
@@ -80,7 +80,12 @@ func TestReadinessAPI(t *testing.T) {
 	shutdown := make(chan os.Signal, 1)
 	serviceConfig, err := config.LoadConfig("")
 	assert.NoError(t, err)
-	serviceConfig.Services.StorageOption = dbFile
+	serviceConfig.Services.StorageOptions = []storage.Option{
+		{
+			ID:     storage.BoltDBFilePathOption,
+			Option: dbFile,
+		},
+	}
 
 	server, err := NewSSIServer(shutdown, *serviceConfig)
 	assert.NoError(t, err)
diff --git a/pkg/service/did/storage_test.go b/pkg/service/did/storage_test.go
index a2b6c3f..7eb5e48 100644
--- a/pkg/service/did/storage_test.go
+++ b/pkg/service/did/storage_test.go
@@ -215,7 +215,10 @@ func setupTestDB(t *testing.T) storage.ServiceStorage {
 	name := file.Name()
 	err = file.Close()
 	require.NoError(t, err)
-	s, err := storage.NewStorage(storage.Bolt, name)
+	s, err := storage.NewStorage(storage.Bolt, storage.Option{
+		ID:     storage.BoltDBFilePathOption,
+		Option: name,
+	})
 	require.NoError(t, err)
 	t.Cleanup(func() {
 		_ = s.Close()
diff --git a/pkg/service/keystore/service_test.go b/pkg/service/keystore/service_test.go
index 32c7e16..92167ab 100644
--- a/pkg/service/keystore/service_test.go
+++ b/pkg/service/keystore/service_test.go
@@ -97,14 +97,17 @@ func TestStoreAndGetKey(t *testing.T) {
 	require.NoError(t, err)
 	name := file.Name()
 	assert.NoError(t, file.Close())
-	bolt, err := storage.NewStorage(storage.Bolt, name)
+	s, err := storage.NewStorage(storage.Bolt, storage.Option{
+		ID:     storage.BoltDBFilePathOption,
+		Option: name,
+	})
 	assert.NoError(t, err)
-	assert.NotEmpty(t, bolt)
+	assert.NotEmpty(t, s)
 
 	// remove the db file after the test
 	t.Cleanup(func() {
-		_ = bolt.Close()
-		_ = os.Remove(bolt.URI())
+		_ = s.Close()
+		_ = os.Remove(s.URI())
 	})
 
 	keyStore, err := NewKeyStoreService(
@@ -114,7 +117,7 @@ func TestStoreAndGetKey(t *testing.T) {
 			},
 			ServiceKeyPassword: "test-password",
 		},
-		bolt)
+		s)
 	assert.NoError(t, err)
 	assert.NotEmpty(t, keyStore)
 
diff --git a/pkg/service/operation/storage_test.go b/pkg/service/operation/storage_test.go
index bb7d74a..e3db652 100644
--- a/pkg/service/operation/storage_test.go
+++ b/pkg/service/operation/storage_test.go
@@ -9,6 +9,7 @@ import (
 	"github.com/google/go-cmp/cmp"
 	"github.com/google/go-cmp/cmp/cmpopts"
 	"github.com/stretchr/testify/require"
+
 	manifeststg "github.com/tbd54566975/ssi-service/pkg/service/manifest/storage"
 	"github.com/tbd54566975/ssi-service/pkg/service/operation/credential"
 	opstorage "github.com/tbd54566975/ssi-service/pkg/service/operation/storage"
@@ -20,7 +21,10 @@ func setupTestDB(t *testing.T) storage.ServiceStorage {
 	file, err := os.CreateTemp("", "bolt")
 	require.NoError(t, err)
 	name := file.Name()
-	s, err := storage.NewStorage(storage.Bolt, name)
+	s, err := storage.NewStorage(storage.Bolt, storage.Option{
+		ID:     storage.BoltDBFilePathOption,
+		Option: name,
+	})
 	require.NoError(t, err)
 	t.Cleanup(func() {
 		_ = s.Close()
diff --git a/pkg/storage/db_test.go b/pkg/storage/db_test.go
index 6e00806..340b4c3 100644
--- a/pkg/storage/db_test.go
+++ b/pkg/storage/db_test.go
@@ -22,24 +22,34 @@ func getDBImplementations(t *testing.T) []ServiceStorage {
 }
 
 func setupBoltDB(t *testing.T) *BoltDB {
-	db, err := NewStorage(Bolt, "test.db")
+	dbName := "test.db"
+	db, err := NewStorage(Bolt, Option{
+		ID:     BoltDBFilePathOption,
+		Option: dbName,
+	})
 	assert.NoError(t, err)
 	assert.NotEmpty(t, db)
 
 	t.Cleanup(func() {
 		_ = db.Close()
-		_ = os.Remove("test.db")
+		_ = os.Remove(dbName)
 	})
 	return db.(*BoltDB)
 }
 
 func setupRedisDB(t *testing.T) *RedisDB {
 	server := miniredis.RunT(t)
-	options := make(map[string]interface{})
-	options["address"] = server.Addr()
-	options["password"] = ""
-
-	db, err := NewStorage(Redis, options)
+	options := []Option{
+		{
+			ID:     RedisAddressOption,
+			Option: server.Addr(),
+		},
+		{
+			ID:     PasswordOption,
+			Option: "test-password",
+		},
+	}
+	db, err := NewStorage(Redis, options...)
 	assert.NoError(t, err)
 	assert.NotEmpty(t, db)
 
diff --git a/pkg/testutil/well_known_generation_test.go b/pkg/testutil/well_known_generation_test.go
index 109e7d5..038aeb2 100644
--- a/pkg/testutil/well_known_generation_test.go
+++ b/pkg/testutil/well_known_generation_test.go
@@ -185,7 +185,10 @@ func setupTestDB(t *testing.T) storage.ServiceStorage {
 	name := file.Name()
 	err = file.Close()
 	require.NoError(t, err)
-	s, err := storage.NewStorage(storage.Bolt, name)
+	s, err := storage.NewStorage(storage.Bolt, storage.Option{
+		ID:     storage.BoltDBFilePathOption,
+		Option: name,
+	})
 	require.NoError(t, err)
 	t.Cleanup(func() {
 		_ = s.Close()