Add new graph status in prep for migration

Fault allows us to remove the error tracking from
the graph support statuses.  This PR is a first step
in a small refactor to slim down the status.  Following
PRs will replace the existing status with the new one.
This commit is contained in:
ryanfkeepers 2023-02-20 15:59:33 -07:00
parent 5593352226
commit 8d52f7655a
3 changed files with 220 additions and 0 deletions

View File

@ -0,0 +1,25 @@
// Code generated by "stringer -type=Operation"; DO NOT EDIT.
package status
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[OpUnknown-0]
_ = x[Backup-1]
_ = x[Restore-2]
}
const _Operation_name = "OpUnknownBackupRestore"
var _Operation_index = [...]uint8{0, 9, 15, 22}
func (i Operation) String() string {
if i < 0 || i >= Operation(len(_Operation_index)-1) {
return "Operation(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _Operation_name[_Operation_index[i]:_Operation_index[i+1]]
}

View File

@ -0,0 +1,104 @@
package status
import (
"context"
"fmt"
"github.com/dustin/go-humanize"
)
type Operation int
//go:generate stringer -type=Operation
const (
OpUnknown Operation = iota
Backup
Restore
)
// ConnectorStatus is a data type used to describe the state of the sequence of operations.
type ConnectorStatus struct {
Metrics Counts
details string
incomplete bool
op Operation
}
type Counts struct {
Bytes int64
Folders, Objects, Successes int
}
func CombineCounts(a, b Counts) Counts {
return Counts{
Bytes: a.Bytes + b.Bytes,
Folders: a.Folders + b.Folders,
Objects: a.Objects + b.Objects,
Successes: a.Successes + b.Successes,
}
}
// Constructor for ConnectorStatus. If the counts do not agree, an error is returned.
func New(
ctx context.Context,
op Operation,
cs Counts,
details string,
incomplete bool,
) ConnectorStatus {
status := ConnectorStatus{
Metrics: cs,
details: details,
incomplete: incomplete,
op: op,
}
return status
}
// Combine aggregates both ConnectorStatus value into a single status.
func Combine(one, two ConnectorStatus) ConnectorStatus {
if one.op == OpUnknown {
return two
}
if two.op == OpUnknown {
return one
}
status := ConnectorStatus{
Metrics: CombineCounts(one.Metrics, two.Metrics),
details: one.details + ", " + two.details,
incomplete: one.incomplete || two.incomplete,
op: one.op,
}
return status
}
func (cos ConnectorStatus) String() string {
var operationStatement string
switch cos.op {
case Backup:
operationStatement = "Downloaded from "
case Restore:
operationStatement = "Restored content to "
}
var incomplete string
if cos.incomplete {
incomplete = "Incomplete "
}
message := fmt.Sprintf("%sAction: %s performed on %d of %d objects (%s) within %d directories.",
incomplete,
cos.op.String(),
cos.Metrics.Successes,
cos.Metrics.Objects,
humanize.Bytes(uint64(cos.Metrics.Bytes)),
cos.Metrics.Folders)
return message + " " + operationStatement + cos.details
}

View File

@ -0,0 +1,91 @@
package status
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/suite"
"github.com/alcionai/corso/src/internal/tester"
)
type StatusUnitSuite struct {
tester.Suite
}
func TestGraphConnectorStatus(t *testing.T) {
suite.Run(t, &StatusUnitSuite{tester.NewUnitSuite(t)})
}
func (suite *StatusUnitSuite) TestNew() {
ctx, flush := tester.NewContext()
defer flush()
result := New(
ctx,
Backup,
Counts{1, 1, 1, 1},
"details",
true)
assert.True(suite.T(), result.incomplete, "status is incomplete")
}
func (suite *StatusUnitSuite) TestMergeStatus() {
ctx, flush := tester.NewContext()
defer flush()
table := []struct {
name string
one ConnectorStatus
two ConnectorStatus
expectOP Operation
expected Counts
isIncomplete assert.BoolAssertionFunc
}{
{
name: "Test: Status + unknown",
one: New(ctx, Backup, Counts{1, 1, 1, 1}, "details", false),
two: ConnectorStatus{},
expectOP: Backup,
expected: Counts{1, 1, 1, 1},
isIncomplete: assert.False,
},
{
name: "Test: unknown + Status",
one: ConnectorStatus{},
two: New(ctx, Backup, Counts{1, 1, 1, 1}, "details", false),
expectOP: Backup,
expected: Counts{1, 1, 1, 1},
isIncomplete: assert.False,
},
{
name: "Test: complete + complete",
one: New(ctx, Backup, Counts{1, 1, 3, 0}, "details", false),
two: New(ctx, Backup, Counts{3, 3, 3, 0}, "details", false),
expectOP: Backup,
expected: Counts{4, 4, 6, 0},
isIncomplete: assert.False,
},
{
name: "Test: complete + incomplete",
one: New(ctx, Restore, Counts{17, 17, 13, 0}, "details", false),
two: New(ctx, Restore, Counts{12, 9, 8, 0}, "details", true),
expectOP: Restore,
expected: Counts{29, 26, 21, 0},
isIncomplete: assert.True,
},
}
for _, test := range table {
suite.Run(test.name, func() {
t := suite.T()
result := Combine(test.one, test.two)
assert.Equal(t, result.op, test.expectOP)
assert.Equal(t, test.expected.Folders, result.Metrics.Folders)
assert.Equal(t, test.expected.Objects, result.Metrics.Objects)
assert.Equal(t, test.expected.Successes, result.Metrics.Successes)
assert.Equal(t, test.expected.Bytes, result.Metrics.Bytes)
test.isIncomplete(t, result.incomplete)
})
}
}