add output formatting control to cli (#329)
* add output formatting control to cli Adds the capacity for the CLI to output either a text table or a json blob to the terminal. Table is the default behavior, json is toggled with the --json flag.
This commit is contained in:
parent
36346548d7
commit
39c85e1a84
@ -1,14 +1,12 @@
|
||||
package backup
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/segmentio/cli"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"github.com/alcionai/corso/cli/config"
|
||||
"github.com/alcionai/corso/cli/print"
|
||||
"github.com/alcionai/corso/cli/utils"
|
||||
"github.com/alcionai/corso/pkg/logger"
|
||||
"github.com/alcionai/corso/pkg/repository"
|
||||
@ -136,15 +134,8 @@ func listExchangeCmd(cmd *cobra.Command, args []string) error {
|
||||
return errors.Wrap(err, "Failed to list backups in the repository")
|
||||
}
|
||||
|
||||
// TODO: Can be used to print in alternative forms (e.g. json)
|
||||
p, err := cli.Format("text", os.Stdout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer p.Flush()
|
||||
for _, rp := range rps {
|
||||
p.Print(*rp)
|
||||
}
|
||||
print.Backups(rps)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -185,13 +176,7 @@ func detailsExchangeCmd(cmd *cobra.Command, args []string) error {
|
||||
return errors.Wrap(err, "Failed to get backup details in the repository")
|
||||
}
|
||||
|
||||
// TODO: Can be used to print in alternative forms
|
||||
p, err := cli.Format("json", os.Stdout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer p.Flush()
|
||||
p.Print(*rpd)
|
||||
print.Entries(rpd.Entries)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/alcionai/corso/cli/backup"
|
||||
"github.com/alcionai/corso/cli/config"
|
||||
"github.com/alcionai/corso/cli/print"
|
||||
"github.com/alcionai/corso/cli/repo"
|
||||
"github.com/alcionai/corso/cli/restore"
|
||||
"github.com/alcionai/corso/pkg/logger"
|
||||
@ -57,6 +58,7 @@ func handleCorsoCmd(cmd *cobra.Command, args []string) error {
|
||||
func Handle() {
|
||||
corsoCmd.Flags().BoolP("version", "v", version, "current version info")
|
||||
corsoCmd.PersistentFlags().StringVar(&cfgFile, "config-file", "", "config file (default is $HOME/.corso)")
|
||||
print.AddOutputFlag(corsoCmd)
|
||||
|
||||
corsoCmd.CompletionOptions.DisableDefaultCmd = true
|
||||
|
||||
|
||||
87
src/cli/print/print.go
Normal file
87
src/cli/print/print.go
Normal file
@ -0,0 +1,87 @@
|
||||
package print
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/alcionai/corso/pkg/backup"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/tidwall/pretty"
|
||||
"github.com/tomlazar/table"
|
||||
)
|
||||
|
||||
var outputAsJSON bool
|
||||
|
||||
// adds the --output flag to the provided command.
|
||||
func AddOutputFlag(parent *cobra.Command) {
|
||||
parent.PersistentFlags().BoolVar(&outputAsJSON, "json", false, "output data in JSON format")
|
||||
}
|
||||
|
||||
type Printable interface {
|
||||
// should list the property names of the values surfaced in Values()
|
||||
Headers() []string
|
||||
// list of values for tabular or csv formatting
|
||||
// if the backing data is nil or otherwise missing,
|
||||
// values should provide an empty string as opposed to skipping entries
|
||||
Values() []string
|
||||
}
|
||||
|
||||
// Prints the backups to the terminal with stdout.
|
||||
func Backups(bs []*backup.Backup) {
|
||||
ps := []Printable{}
|
||||
for _, b := range bs {
|
||||
ps = append(ps, b)
|
||||
}
|
||||
printAll(ps)
|
||||
}
|
||||
|
||||
// Prints the entries to the terminal with stdout.
|
||||
func Entries(des []backup.DetailsEntry) {
|
||||
ps := []Printable{}
|
||||
for _, de := range des {
|
||||
ps = append(ps, de)
|
||||
}
|
||||
printAll(ps)
|
||||
}
|
||||
|
||||
// printAll prints the slice of printable items,
|
||||
// according to the caller's requested format.
|
||||
func printAll(ps []Printable) {
|
||||
if len(ps) == 0 {
|
||||
return
|
||||
}
|
||||
if outputAsJSON {
|
||||
outputJSON(ps)
|
||||
return
|
||||
}
|
||||
outputTable(ps)
|
||||
}
|
||||
|
||||
// output to stdout the list of printable structs as json
|
||||
func outputJSON(ps []Printable) {
|
||||
bs, err := json.Marshal(ps)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "error formatting results to json: %v\n", err)
|
||||
return
|
||||
}
|
||||
fmt.Println(string(pretty.Pretty(bs)))
|
||||
}
|
||||
|
||||
// output to stdout the list of printable structs in a table
|
||||
func outputTable(ps []Printable) {
|
||||
t := table.Table{
|
||||
Headers: ps[0].Headers(),
|
||||
Rows: [][]string{},
|
||||
}
|
||||
for _, p := range ps {
|
||||
t.Rows = append(t.Rows, p.Values())
|
||||
}
|
||||
_ = t.WriteTable(
|
||||
os.Stdout,
|
||||
&table.Config{
|
||||
ShowIndex: false,
|
||||
Color: false,
|
||||
AlternateColors: false,
|
||||
})
|
||||
}
|
||||
@ -13,11 +13,13 @@ require (
|
||||
github.com/microsoftgraph/msgraph-sdk-go v0.28.0
|
||||
github.com/microsoftgraph/msgraph-sdk-go-core v0.26.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/segmentio/cli v0.5.0
|
||||
github.com/spf13/cobra v1.4.0
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.12.0
|
||||
github.com/stretchr/testify v1.8.0
|
||||
github.com/tidwall/pretty v1.2.0
|
||||
github.com/tomlazar/table v0.1.2
|
||||
github.com/zeebo/assert v1.1.0
|
||||
go.uber.org/zap v1.21.0
|
||||
golang.org/x/tools v0.1.11
|
||||
)
|
||||
@ -60,7 +62,11 @@ require (
|
||||
github.com/klauspost/cpuid/v2 v2.0.14 // indirect
|
||||
github.com/klauspost/pgzip v1.2.5 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.13 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
||||
github.com/microsoft/kiota-http-go v0.5.2 // indirect
|
||||
github.com/microsoft/kiota-serialization-text-go v0.4.1 // indirect
|
||||
github.com/minio/md5-simd v1.1.2 // indirect
|
||||
@ -76,6 +82,7 @@ require (
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.35.0 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/rivo/uniseg v0.2.0 // indirect
|
||||
github.com/rs/xid v1.4.0 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
|
||||
|
||||
21
src/go.sum
21
src/go.sum
@ -238,8 +238,18 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
||||
github.com/microsoft/kiota-abstractions-go v0.8.1 h1:ACCwRwddJYOx+SRqfgcR8Wo8PZTd4g+JMa8lY8ABy+4=
|
||||
github.com/microsoft/kiota-abstractions-go v0.8.1/go.mod h1:05aCidCKhzer+yfhGeePaMUY3MH+wrAkQztBVEreTtc=
|
||||
github.com/microsoft/kiota-authentication-azure-go v0.3.0 h1:iLyy5qldAjBiYMGMk1r/rJkcmARA8cKboiN7/XbRxv4=
|
||||
@ -313,14 +323,14 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
|
||||
github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY=
|
||||
github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/segmentio/cli v0.5.0 h1:AssNAdZV728i8u6LWfq9pqoeQGxiyXmTt0jrCfnjcx0=
|
||||
github.com/segmentio/cli v0.5.0/go.mod h1:rktB/5TnLUnEBYdRG+jlAii0bkHWpnrb+jpXiFkoPxs=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
@ -351,6 +361,10 @@ github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PK
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/subosito/gotenv v1.3.0 h1:mjC+YW8QpAdXibNi+vNWgzmgBH4+5l5dCXv8cNysBLI=
|
||||
github.com/subosito/gotenv v1.3.0/go.mod h1:YzJjq/33h7nrwdY+iHMhEOEEbW0ovIz0tB6t6PwAXzs=
|
||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tomlazar/table v0.1.2 h1:DP8f62FzZAZk8oavepm1v/oyf4ni3/LMHWNlOinmleg=
|
||||
github.com/tomlazar/table v0.1.2/go.mod h1:IecZnpep9f/BatHacfh+++ftE+lFONN8BVPi9nx5U1w=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
|
||||
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@ -512,6 +526,7 @@ golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -540,6 +555,8 @@ golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
||||
@ -108,13 +108,12 @@ func (op *BackupOperation) Run(ctx context.Context) error {
|
||||
}
|
||||
|
||||
func (op *BackupOperation) createBackupModels(ctx context.Context, snapID string, details *backup.Details) error {
|
||||
err := op.modelStore.Put(ctx, kopia.BackupDetailsModel, details)
|
||||
err := op.modelStore.Put(ctx, kopia.BackupDetailsModel, &details.DetailsModel)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "creating backupdetails model")
|
||||
}
|
||||
|
||||
err = op.modelStore.Put(ctx, kopia.BackupModel,
|
||||
backup.New(snapID, string(details.ModelStoreID)))
|
||||
err = op.modelStore.Put(ctx, kopia.BackupModel, backup.New(snapID, string(details.ModelStoreID)))
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "creating backup model")
|
||||
}
|
||||
|
||||
@ -23,6 +23,28 @@ type Backup struct {
|
||||
// - Backup "Specification"
|
||||
}
|
||||
|
||||
// Headers returns the human-readable names of properties in a Backup
|
||||
// for printing out to a terminal in a columnar display.
|
||||
func (b Backup) Headers() []string {
|
||||
return []string{
|
||||
"Creation Time",
|
||||
"Stable ID",
|
||||
"Snapshot ID",
|
||||
"Details ID",
|
||||
}
|
||||
}
|
||||
|
||||
// Values returns the values matching the Headers list for printing
|
||||
// out to a terminal in a columnar display.
|
||||
func (b Backup) Values() []string {
|
||||
return []string{
|
||||
b.CreationTime.Format(time.RFC3339Nano),
|
||||
string(b.StableID),
|
||||
b.SnapshotID,
|
||||
b.DetailsID,
|
||||
}
|
||||
}
|
||||
|
||||
func New(snapshotID, detailsID string) *Backup {
|
||||
return &Backup{
|
||||
CreationTime: time.Now(),
|
||||
@ -31,10 +53,17 @@ func New(snapshotID, detailsID string) *Backup {
|
||||
}
|
||||
}
|
||||
|
||||
// Details describes what was stored in a Backup
|
||||
type Details struct {
|
||||
// DetailsModel describes what was stored in a Backup
|
||||
type DetailsModel struct {
|
||||
model.BaseModel
|
||||
Entries []DetailsEntry `json:"entries"`
|
||||
}
|
||||
|
||||
// Details augments the core with a mutex for processing.
|
||||
// Should be sliced back to d.DetailsModel for storage and
|
||||
// printing.
|
||||
type Details struct {
|
||||
DetailsModel
|
||||
|
||||
// internal
|
||||
mu sync.Mutex `json:"-"`
|
||||
@ -48,6 +77,31 @@ type DetailsEntry struct {
|
||||
ItemInfo
|
||||
}
|
||||
|
||||
// Headers returns the human-readable names of properties in a DetailsEntry
|
||||
// for printing out to a terminal in a columnar display.
|
||||
func (de DetailsEntry) Headers() []string {
|
||||
hs := []string{"Repo Ref"}
|
||||
if de.ItemInfo.Exchange != nil {
|
||||
hs = append(hs, de.ItemInfo.Exchange.Headers()...)
|
||||
}
|
||||
if de.ItemInfo.Sharepoint != nil {
|
||||
hs = append(hs, de.ItemInfo.Sharepoint.Headers()...)
|
||||
}
|
||||
return hs
|
||||
}
|
||||
|
||||
// Values returns the values matching the Headers list.
|
||||
func (de DetailsEntry) Values() []string {
|
||||
vs := []string{de.RepoRef}
|
||||
if de.ItemInfo.Exchange != nil {
|
||||
vs = append(vs, de.ItemInfo.Exchange.Values()...)
|
||||
}
|
||||
if de.ItemInfo.Sharepoint != nil {
|
||||
vs = append(vs, de.ItemInfo.Sharepoint.Values()...)
|
||||
}
|
||||
return vs
|
||||
}
|
||||
|
||||
// ItemInfo is a oneOf that contains service specific
|
||||
// information about the item it tracks
|
||||
type ItemInfo struct {
|
||||
@ -62,6 +116,18 @@ type ExchangeInfo struct {
|
||||
Received time.Time `json:"received"`
|
||||
}
|
||||
|
||||
// Headers returns the human-readable names of properties in an ExchangeInfo
|
||||
// for printing out to a terminal in a columnar display.
|
||||
func (e ExchangeInfo) Headers() []string {
|
||||
return []string{"Sender", "Subject", "Received"}
|
||||
}
|
||||
|
||||
// Values returns the values matching the Headers list for printing
|
||||
// out to a terminal in a columnar display.
|
||||
func (e ExchangeInfo) Values() []string {
|
||||
return []string{e.Sender, e.Subject, e.Received.Format(time.RFC3339Nano)}
|
||||
}
|
||||
|
||||
// SharepointInfo describes a sharepoint item
|
||||
// TODO: Implement this. This is currently here
|
||||
// just to illustrate usage
|
||||
@ -72,3 +138,15 @@ func (d *Details) Add(repoRef string, info ItemInfo) {
|
||||
defer d.mu.Unlock()
|
||||
d.Entries = append(d.Entries, DetailsEntry{RepoRef: repoRef, ItemInfo: info})
|
||||
}
|
||||
|
||||
// Headers returns the human-readable names of properties in a SharepointInfo
|
||||
// for printing out to a terminal in a columnar display.
|
||||
func (s SharepointInfo) Headers() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
// Values returns the values matching the Headers list for printing
|
||||
// out to a terminal in a columnar display.
|
||||
func (s SharepointInfo) Values() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
||||
108
src/pkg/backup/backup_test.go
Normal file
108
src/pkg/backup/backup_test.go
Normal file
@ -0,0 +1,108 @@
|
||||
package backup_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/zeebo/assert"
|
||||
|
||||
"github.com/alcionai/corso/internal/model"
|
||||
"github.com/alcionai/corso/pkg/backup"
|
||||
)
|
||||
|
||||
type BackupSuite struct {
|
||||
suite.Suite
|
||||
}
|
||||
|
||||
func TestBackupSuite(t *testing.T) {
|
||||
suite.Run(t, new(BackupSuite))
|
||||
}
|
||||
|
||||
func (suite *BackupSuite) TestBackup_HeadersValues() {
|
||||
t := suite.T()
|
||||
now := time.Now()
|
||||
|
||||
b := backup.Backup{
|
||||
BaseModel: model.BaseModel{
|
||||
StableID: model.ID("stable"),
|
||||
},
|
||||
CreationTime: now,
|
||||
SnapshotID: "snapshot",
|
||||
DetailsID: "details",
|
||||
}
|
||||
|
||||
expectHs := []string{
|
||||
"Creation Time",
|
||||
"Stable ID",
|
||||
"Snapshot ID",
|
||||
"Details ID",
|
||||
}
|
||||
hs := b.Headers()
|
||||
assert.DeepEqual(t, expectHs, hs)
|
||||
|
||||
expectVs := []string{
|
||||
now.Format(time.RFC3339Nano),
|
||||
"stable",
|
||||
"snapshot",
|
||||
"details",
|
||||
}
|
||||
vs := b.Values()
|
||||
assert.DeepEqual(t, expectVs, vs)
|
||||
}
|
||||
|
||||
func (suite *BackupSuite) TestDetailsEntry_HeadersValues() {
|
||||
now := time.Now()
|
||||
nowStr := now.Format(time.RFC3339Nano)
|
||||
|
||||
table := []struct {
|
||||
name string
|
||||
entry backup.DetailsEntry
|
||||
expectHs []string
|
||||
expectVs []string
|
||||
}{
|
||||
{
|
||||
name: "no info",
|
||||
entry: backup.DetailsEntry{
|
||||
RepoRef: "reporef",
|
||||
},
|
||||
expectHs: []string{"Repo Ref"},
|
||||
expectVs: []string{"reporef"},
|
||||
},
|
||||
{
|
||||
name: "exhange info",
|
||||
entry: backup.DetailsEntry{
|
||||
RepoRef: "reporef",
|
||||
ItemInfo: backup.ItemInfo{
|
||||
Exchange: &backup.ExchangeInfo{
|
||||
Sender: "sender",
|
||||
Subject: "subject",
|
||||
Received: now,
|
||||
},
|
||||
},
|
||||
},
|
||||
expectHs: []string{"Repo Ref", "Sender", "Subject", "Received"},
|
||||
expectVs: []string{"reporef", "sender", "subject", nowStr},
|
||||
},
|
||||
{
|
||||
name: "sharepoint info",
|
||||
entry: backup.DetailsEntry{
|
||||
RepoRef: "reporef",
|
||||
ItemInfo: backup.ItemInfo{
|
||||
Sharepoint: &backup.SharepointInfo{},
|
||||
},
|
||||
},
|
||||
expectHs: []string{"Repo Ref"},
|
||||
expectVs: []string{"reporef"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range table {
|
||||
suite.T().Run(test.name, func(t *testing.T) {
|
||||
hs := test.entry.Headers()
|
||||
assert.DeepEqual(t, test.expectHs, hs)
|
||||
vs := test.entry.Values()
|
||||
assert.DeepEqual(t, test.expectVs, vs)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -606,7 +606,9 @@ func (suite *ExchangeSourceSuite) TestIdPath() {
|
||||
func (suite *ExchangeSourceSuite) TestExchangeRestore_FilterDetails() {
|
||||
makeDeets := func(refs ...string) *backup.Details {
|
||||
deets := &backup.Details{
|
||||
DetailsModel: backup.DetailsModel{
|
||||
Entries: []backup.DetailsEntry{},
|
||||
},
|
||||
}
|
||||
for _, r := range refs {
|
||||
deets.Entries = append(deets.Entries, backup.DetailsEntry{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user