Repository: coinbase/rosetta-cli
Pull request #303 (2022-04-14 03:56:11)

error categorization
Fixes # .

### Motivation
<!--
Does this solve a bug? Enable a new use-case? Improve an existing behavior? Concrete examples are helpful here.
-->
Clean some customized errors and unnecessary fatal logs 
### Solution
<!--
What is the solution here from a high level. What are the key technical decisions and why were they made?
-->

### Open questions
<!--
(optional) Any open questions or feedback on design desired?
-->



Patch:
diff --git a/cmd/check_construction.go b/cmd/check_construction.go
index 2a04aa7..617a4a0 100644
--- a/cmd/check_construction.go
+++ b/cmd/check_construction.go
@@ -16,8 +16,8 @@ package cmd
 
 import (
 	"context"
-	"errors"
 	"fmt"
+	"github.com/coinbase/rosetta-cli/pkg/errors"
 	"time"
 
 	"github.com/coinbase/rosetta-cli/pkg/results"
@@ -59,7 +59,7 @@ func runCheckConstructionCmd(_ *cobra.Command, _ []string) error {
 			Config,
 			nil,
 			nil,
-			errors.New("construction configuration is missing"),
+			errors.ErrConstructionConfigMissing,
 		)
 	}
 
diff --git a/cmd/utils_asserter_configuration.go b/cmd/utils_asserter_configuration.go
index 3f4b4e8..39afbdb 100644
--- a/cmd/utils_asserter_configuration.go
+++ b/cmd/utils_asserter_configuration.go
@@ -81,4 +81,4 @@ func sortArrayFieldsOnConfiguration(configuration *asserter.Configuration) {
 	sort.Slice(configuration.AllowedErrors, func(i, j int) bool {
 		return configuration.AllowedErrors[i].Code < configuration.AllowedErrors[j].Code
 	})
-}
\ No newline at end of file
+}
diff --git a/cmd/validate_asserter_config.go b/cmd/validate_asserter_config.go
index 068deba..ba5afe3 100644
--- a/cmd/validate_asserter_config.go
+++ b/cmd/validate_asserter_config.go
@@ -16,8 +16,8 @@ package cmd
 
 import (
 	"context"
-	"errors"
 	"fmt"
+	"github.com/coinbase/rosetta-cli/pkg/errors"
 	"github.com/coinbase/rosetta-sdk-go/asserter"
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
 	"github.com/coinbase/rosetta-sdk-go/types"
@@ -52,10 +52,10 @@ func validateNetworkAndAsserterAllowMatch(
 	networkAllow *types.Allow, asserterConfiguration *asserter.Configuration,
 ) error {
 	if networkAllow == nil {
-		return errors.New("/network/options object's Allow is nil")
+		return fmt.Errorf("%w: /network/options object's Allow is nil", errors.ErrAsserterConfigError)
 	}
 	if asserterConfiguration == nil {
-		return errors.New("asserter-configuration-file object is nil")
+		return fmt.Errorf("%w: asserter-configuration-file object is nil", errors.ErrAsserterConfigError)
 	}
 
 	if err := verifyTimestampStartIndex(
@@ -111,7 +111,7 @@ func verifyOperationTypes(networkOt, asserterOt []string) error {
 		if networkOperationType != asserterOperationType {
 			return fmt.Errorf(
 				"/network/options / asserter-configuration-file operation type mismatch %+v "+
-				"%+v\nnetwork operation types: %+v\nasserter operation types: %+v",
+					"%+v\nnetwork operation types: %+v\nasserter operation types: %+v",
 				networkOperationType, asserterOperationType, networkOt, asserterOt,
 			)
 		}
diff --git a/cmd/view_block.go b/cmd/view_block.go
index 597d34b..be2de14 100644
--- a/cmd/view_block.go
+++ b/cmd/view_block.go
@@ -15,8 +15,8 @@
 package cmd
 
 import (
-	"errors"
 	"fmt"
+	"github.com/coinbase/rosetta-cli/pkg/errors"
 	"strconv"
 	"time"
 
@@ -125,7 +125,7 @@ func runViewBlockCmd(_ *cobra.Command, args []string) error {
 	}
 	// It's valid for a block to be omitted without triggering an error
 	if block == nil {
-		return errors.New("block not found, it might be omitted")
+		return fmt.Errorf("%w: block not found, it might be omitted", errors.ErrBlockNotFound)
 	}
 
 	fmt.Printf("\n")
diff --git a/cmd/view_networks.go b/cmd/view_networks.go
index eda06a5..c2d0a47 100644
--- a/cmd/view_networks.go
+++ b/cmd/view_networks.go
@@ -15,8 +15,8 @@
 package cmd
 
 import (
-	"errors"
 	"fmt"
+	"github.com/coinbase/rosetta-cli/pkg/errors"
 	"log"
 	"time"
 
@@ -63,7 +63,7 @@ func runViewNetworksCmd(cmd *cobra.Command, args []string) error {
 	}
 
 	if len(networkList.NetworkIdentifiers) == 0 {
-		return errors.New("no networks available")
+		return errors.ErrNoAvailableNetwork
 	}
 
 	for _, network := range networkList.NetworkIdentifiers {
diff --git a/configuration/configuration.go b/configuration/configuration.go
index 1dc1a2f..c92c2cd 100644
--- a/configuration/configuration.go
+++ b/configuration/configuration.go
@@ -24,6 +24,7 @@ import (
 	"runtime"
 	"strings"
 
+	customerrors "github.com/coinbase/rosetta-cli/pkg/errors"
 	"github.com/coinbase/rosetta-sdk-go/asserter"
 	"github.com/coinbase/rosetta-sdk-go/constructor/dsl"
 	"github.com/coinbase/rosetta-sdk-go/constructor/job"
@@ -180,11 +181,11 @@ func assertConstructionConfiguration(ctx context.Context, config *ConstructionCo
 	}
 
 	if len(config.Workflows) > 0 && len(config.ConstructorDSLFile) > 0 {
-		return errors.New("cannot populate both workflows and DSL file path")
+		return fmt.Errorf("%w: cannot populate both workflows and DSL file path", customerrors.ErrParseFileFailed)
 	}
 
 	if len(config.Workflows) == 0 && len(config.ConstructorDSLFile) == 0 {
-		return errors.New("both workflows and DSL file path are empty")
+		return fmt.Errorf("%w: both workflows and DSL file path are empty", customerrors.ErrParseFileFailed)
 	}
 
 	// Compile ConstructorDSLFile and save to Workflows
@@ -203,7 +204,8 @@ func assertConstructionConfiguration(ctx context.Context, config *ConstructionCo
 		if workflow.Name == string(job.CreateAccount) || workflow.Name == string(job.RequestFunds) {
 			if workflow.Concurrency != job.ReservedWorkflowConcurrency {
 				return fmt.Errorf(
-					"reserved workflow %s must have concurrency %d",
+					"%w: reserved workflow %s must have concurrency %d",
+					customerrors.ErrParseWorkflowFailed,
 					workflow.Name,
 					job.ReservedWorkflowConcurrency,
 				)
diff --git a/pkg/errors/errors.go b/pkg/errors/errors.go
new file mode 100644
index 0000000..cac440e
--- /dev/null
+++ b/pkg/errors/errors.go
@@ -0,0 +1,45 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package errors
+
+import (
+	"errors"
+)
+
+// Configuration Errors
+
+var (
+	// Construction Configuration Errors
+
+	ErrParseFileFailed           = errors.New("unable to parse config files")
+	ErrBalanceTrackingDisabled   = errors.New("balance tracking disabled")
+	ErrReconciliationConfig      = errors.New("invalid reconciliation error")
+	ErrCompileDSLFileFailed      = errors.New("unable to compile DSL file")
+	ErrParseWorkflowFailed       = errors.New("unable to parse workflow")
+	ErrConstructionConfigMissing = errors.New("construction configuration is missing")
+
+	// TODO: Data Configuration Errors
+
+	// Construction check errors
+
+	ErrConstructionCheckHalt = errors.New("construction check halted")
+
+	ErrAsserterConfigError = errors.New("asserter configuration validation failed")
+
+	// Bad Command Errors
+
+	ErrBlockNotFound      = errors.New("block not found")
+	ErrNoAvailableNetwork = errors.New("no networks available")
+)


Test patch:
diff --git a/pkg/tester/construction.go b/pkg/tester/construction.go
index dd2f19d..537890c 100644
--- a/pkg/tester/construction.go
+++ b/pkg/tester/construction.go
@@ -19,6 +19,7 @@ import (
 	"encoding/json"
 	"errors"
 	"fmt"
+	customErrs "github.com/coinbase/rosetta-cli/pkg/errors"
 	"log"
 	"net/http"
 	"time"
@@ -83,7 +84,7 @@ func InitializeConstruction(
 ) (*ConstructionTester, error) {
 	dataPath, err := utils.CreateCommandPath(config.DataDirectory, constructionCmdName, network)
 	if err != nil {
-		log.Fatalf("%s: cannot create command path", err.Error())
+		return nil, fmt.Errorf("%s: cannot create command path", err.Error())
 	}
 
 	opts := []database.BadgerOption{}
@@ -99,17 +100,17 @@ func InitializeConstruction(
 
 	localStore, err := database.NewBadgerDatabase(ctx, dataPath, opts...)
 	if err != nil {
-		log.Fatalf("%s: unable to initialize database", err.Error())
+		return nil, fmt.Errorf("%s: unable to initialize database", err.Error())
 	}
 
 	networkOptions, fetchErr := onlineFetcher.NetworkOptionsRetry(ctx, network, nil)
 	if err != nil {
-		log.Fatalf("%s: unable to get network options", fetchErr.Err.Error())
+		return nil, fmt.Errorf("%s: unable to get network options", fetchErr.Err.Error())
 	}
 
 	if len(networkOptions.Allow.BalanceExemptions) > 0 &&
 		config.Construction.InitialBalanceFetchDisabled {
-		log.Fatal("found balance exemptions but initial balance fetch disabled")
+		return nil, fmt.Errorf("found balance exemptions but initial balance fetch disabled")
 	}
 
 	counterStorage := modules.NewCounterStorage(localStore)
@@ -578,7 +579,7 @@ func (t *ConstructionTester) HandleErr(
 			t.config,
 			t.counterStorage,
 			t.jobStorage,
-			errors.New("check halted"),
+			fmt.Errorf("%w: %v", customErrs.ErrConstructionCheckHalt, err.Error()),
 		)
 	}