Skip to content

Commit

Permalink
adds dictionary batchmodify
Browse files Browse the repository at this point in the history
  • Loading branch information
benzvan committed Nov 27, 2020
1 parent 6cc597f commit bdd4ee4
Show file tree
Hide file tree
Showing 6 changed files with 240 additions and 5 deletions.
1 change: 1 addition & 0 deletions pkg/api/interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type Interface interface {
CreateDictionaryItem(*fastly.CreateDictionaryItemInput) (*fastly.DictionaryItem, error)
UpdateDictionaryItem(*fastly.UpdateDictionaryItemInput) (*fastly.DictionaryItem, error)
DeleteDictionaryItem(*fastly.DeleteDictionaryItemInput) error
BatchModifyDictionaryItems(*fastly.BatchModifyDictionaryItemsInput) error

GetDictionaryInfo(*fastly.GetDictionaryInfoInput) (*fastly.DictionaryInfo, error)

Expand Down
2 changes: 2 additions & 0 deletions pkg/app/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ func Run(args []string, env config.Environment, file config.File, configFilePath
dictionaryItemCreate := edgedictionaryitem.NewCreateCommand(dictionaryItemRoot.CmdClause, &globals)
dictionaryItemUpdate := edgedictionaryitem.NewUpdateCommand(dictionaryItemRoot.CmdClause, &globals)
dictionaryItemDelete := edgedictionaryitem.NewDeleteCommand(dictionaryItemRoot.CmdClause, &globals)
dictionaryItemBatchModify := edgedictionaryitem.NewBatchCommand(dictionaryItemRoot.CmdClause, &globals)

loggingRoot := logging.NewRootCommand(app, &globals)

Expand Down Expand Up @@ -410,6 +411,7 @@ func Run(args []string, env config.Environment, file config.File, configFilePath
dictionaryItemCreate,
dictionaryItemUpdate,
dictionaryItemDelete,
dictionaryItemBatchModify,

loggingRoot,

Expand Down
8 changes: 8 additions & 0 deletions pkg/app/run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,14 @@ COMMANDS
Dictionary ID
--key=KEY Dictionary item key
dictionaryitem batchmodify --dictionary-id=DICTIONARY-ID --file=FILE [<flags>]
Update multiple items in a Fastly edge dictionary
-s, --service-id=SERVICE-ID Service ID
--dictionary-id=DICTIONARY-ID
Dictionary ID
--file=FILE Batch update json file
logging bigquery create --name=NAME --version=VERSION --project-id=PROJECT-ID --dataset=DATASET --table=TABLE --user=USER --secret-key=SECRET-KEY [<flags>]
Create a BigQuery logging endpoint on a Fastly service version
Expand Down
73 changes: 73 additions & 0 deletions pkg/edgedictionaryitem/batch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package edgedictionaryitem

import (
"encoding/json"
"fmt"
"io"
"io/ioutil"
"os"

"github.com/fastly/cli/pkg/common"
"github.com/fastly/cli/pkg/compute/manifest"
"github.com/fastly/cli/pkg/config"
"github.com/fastly/cli/pkg/errors"
"github.com/fastly/cli/pkg/text"
"github.com/fastly/go-fastly/v2/fastly"
)

// BatchCommand calls the Fastly API to describe a service.
type BatchCommand struct {
common.Base
manifest manifest.Data
Input fastly.BatchModifyDictionaryItemsInput

file common.OptionalString
}

// NewBatchCommand returns a usable command registered under the parent.
func NewBatchCommand(parent common.Registerer, globals *config.Data) *BatchCommand {
var c BatchCommand
c.Globals = globals
c.manifest.File.Read(manifest.Filename)
c.CmdClause = parent.Command("batchmodify", "Update multiple items in a Fastly edge dictionary")
c.CmdClause.Flag("service-id", "Service ID").Short('s').StringVar(&c.manifest.Flag.ServiceID)
c.CmdClause.Flag("dictionary-id", "Dictionary ID").Required().StringVar(&c.Input.DictionaryID)
c.CmdClause.Flag("file", "Batch update json file").Required().StringVar(&c.file.Value)
return &c
}

// Exec invokes the application logic for the command.
func (c *BatchCommand) Exec(in io.Reader, out io.Writer) error {
serviceID, source := c.manifest.ServiceID()
if source == manifest.SourceUndefined {
return errors.ErrNoServiceID
}
c.Input.ServiceID = serviceID

jsonFile, err := os.Open(c.file.Value)
if err != nil {
return err
}

jsonBytes, err := ioutil.ReadAll(jsonFile)
if err != nil {
return err
}

err = json.Unmarshal(jsonBytes, &c.Input)
if err != nil {
return err
}

if len(c.Input.Items) == 0 {
return fmt.Errorf("item key not found in file %s", c.file.Value)
}

err = c.Globals.Client.BatchModifyDictionaryItems(&c.Input)
if err != nil {
return err
}

text.Success(out, "Made %d modifications of Dictionary %s on service %s", len(c.Input.Items), c.Input.DictionaryID, c.Input.ServiceID)
return nil
}
145 changes: 145 additions & 0 deletions pkg/edgedictionaryitem/edgedictionaryitem_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ package edgedictionaryitem_test

import (
"bytes"
"crypto/rand"
"errors"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
"testing"

Expand Down Expand Up @@ -225,6 +230,78 @@ func TestDictionaryItemDelete(t *testing.T) {
}
}

func TestDictionaryItemBatchModify(t *testing.T) {
for _, testcase := range []struct {
args []string
api mock.API
fileData string
wantError string
wantOutput string
}{
{
args: []string{"dictionaryitem", "batchmodify", "--service-id", "123"},
wantError: "error parsing arguments: required flag ",
},
{
args: []string{"dictionaryitem", "batchmodify", "--service-id", "123", "--dictionary-id", "456"},
wantError: "error parsing arguments: required flag --file not provided",
},
{
fileData: `{invalid": "json"}`,
args: []string{"dictionaryitem", "batchmodify", "--service-id", "123", "--dictionary-id", "456", "--file", "filePath"},
wantError: "invalid character 'i' looking for beginning of object key string",
},
{
fileData: `{"valid": "json"}`,
args: []string{"dictionaryitem", "batchmodify", "--service-id", "123", "--dictionary-id", "456", "--file", "filePath"},
wantError: "item key not found in file ",
},
{
args: []string{"dictionaryitem", "batchmodify", "--service-id", "123", "--dictionary-id", "456", "--file", "missingFile"},
wantError: "open missingFile: no such file or directory",
},
{
fileData: dictionaryItemBatchModifyInputOK,
args: []string{"dictionaryitem", "batchmodify", "--service-id", "123", "--dictionary-id", "456", "--file", "filePath"},
api: mock.API{BatchModifyDictionaryItemsFn: batchModifyDictionaryItemsError},
wantError: errTest.Error(),
},
{
fileData: dictionaryItemBatchModifyInputOK,
args: []string{"dictionaryitem", "batchmodify", "--service-id", "123", "--dictionary-id", "456", "--file", "filePath"},
api: mock.API{BatchModifyDictionaryItemsFn: batchModifyDictionaryItemsOK},
wantOutput: "\nSUCCESS: Made 4 modifications of Dictionary 456 on service 123\n",
},
} {
t.Run(strings.Join(testcase.args, " "), func(t *testing.T) {
filePath := tempFile(t, testcase.fileData)
defer os.RemoveAll(filePath)

// Insert temp file path into args when "filePath" is present as placeholder
for i, v := range testcase.args {
if v == "filePath" {
testcase.args[i] = filePath
}
}

var (
args = testcase.args
env = config.Environment{}
file = config.File{}
appConfigFile = "/dev/null"
clientFactory = mock.APIClient(testcase.api)
httpClient = http.DefaultClient
versioner update.Versioner = nil
in io.Reader = nil
out bytes.Buffer
)
err := app.Run(args, env, file, appConfigFile, clientFactory, httpClient, versioner, in, &out)
testutil.AssertErrorContains(t, err, testcase.wantError)
testutil.AssertString(t, testcase.wantOutput, out.String())
})
}
}

func describeDictionaryItemOK(i *fastly.GetDictionaryItemInput) (*fastly.DictionaryItem, error) {
return &fastly.DictionaryItem{
ServiceID: i.ServiceID,
Expand Down Expand Up @@ -332,3 +409,71 @@ func updateDictionaryItemOK(i *fastly.UpdateDictionaryItemInput) (*fastly.Dictio
func deleteDictionaryItemOK(i *fastly.DeleteDictionaryItemInput) error {
return nil
}

var dictionaryItemBatchModifyInputOK = `
{
"items": [
{
"op": "create",
"item_key": "some_key",
"item_value": "new_value"
},
{
"op": "update",
"item_key": "some_key",
"item_value": "new_value"
},
{
"op": "upsert",
"item_key": "some_key",
"item_value": "new_value"
},
{
"op": "delete",
"item_key": "some_key"
}
]
}`

func batchModifyDictionaryItemsOK(i *fastly.BatchModifyDictionaryItemsInput) error {
return nil
}

func batchModifyDictionaryItemsError(i *fastly.BatchModifyDictionaryItemsInput) error {
return errTest
}

var errTest = errors.New("an expected error ocurred")

func tempFile(t *testing.T, contents string) string {
t.Helper()

p := make([]byte, 32)
n, err := rand.Read(p)
if err != nil {
t.Fatal(err)
}

filename := filepath.Join(
os.TempDir(),
fmt.Sprintf("fastly-%x", p[:n]),
)

if contents != "" {
f, err := os.Create(filename)
if err != nil {
t.Fatal(err)
}
if _, err := fmt.Fprintln(f, contents); err != nil {
t.Fatal(err)
}
if err := f.Sync(); err != nil {
t.Fatal(err)
}
if err := f.Close(); err != nil {
t.Fatal(err)
}
}

return filename
}
16 changes: 11 additions & 5 deletions pkg/mock/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,12 @@ type API struct {
ListDictionariesFn func(*fastly.ListDictionariesInput) ([]*fastly.Dictionary, error)
UpdateDictionaryFn func(*fastly.UpdateDictionaryInput) (*fastly.Dictionary, error)

ListDictionaryItemsFn func(*fastly.ListDictionaryItemsInput) ([]*fastly.DictionaryItem, error)
GetDictionaryItemFn func(*fastly.GetDictionaryItemInput) (*fastly.DictionaryItem, error)
CreateDictionaryItemFn func(*fastly.CreateDictionaryItemInput) (*fastly.DictionaryItem, error)
UpdateDictionaryItemFn func(*fastly.UpdateDictionaryItemInput) (*fastly.DictionaryItem, error)
DeleteDictionaryItemFn func(*fastly.DeleteDictionaryItemInput) error
ListDictionaryItemsFn func(*fastly.ListDictionaryItemsInput) ([]*fastly.DictionaryItem, error)
GetDictionaryItemFn func(*fastly.GetDictionaryItemInput) (*fastly.DictionaryItem, error)
CreateDictionaryItemFn func(*fastly.CreateDictionaryItemInput) (*fastly.DictionaryItem, error)
UpdateDictionaryItemFn func(*fastly.UpdateDictionaryItemInput) (*fastly.DictionaryItem, error)
DeleteDictionaryItemFn func(*fastly.DeleteDictionaryItemInput) error
BatchModifyDictionaryItemsFn func(*fastly.BatchModifyDictionaryItemsInput) error

GetDictionaryInfoFn func(*fastly.GetDictionaryInfoInput) (*fastly.DictionaryInfo, error)

Expand Down Expand Up @@ -421,6 +422,11 @@ func (m API) DeleteDictionaryItem(i *fastly.DeleteDictionaryItemInput) error {
return m.DeleteDictionaryItemFn(i)
}

// BatchModifyDictionaryItems implements Interface.
func (m API) BatchModifyDictionaryItems(i *fastly.BatchModifyDictionaryItemsInput) error {
return m.BatchModifyDictionaryItemsFn(i)
}

// GetDictionaryInfo implements Interface.
func (m API) GetDictionaryInfo(i *fastly.GetDictionaryInfoInput) (*fastly.DictionaryInfo, error) {
return m.GetDictionaryInfoFn(i)
Expand Down

0 comments on commit bdd4ee4

Please sign in to comment.