feat: invokable scripts (#387)
* basic list, create, and invoke working * all commands working * added support for create script body from file and invoke params from file * linter cleanup * update defaults to existing parameters if not provided * updated generated mock files, added mock files for scripts, added basic script create test * added mock script list * cleanup pass, fixed not using params in list call * added update mock test * fixed mock tests requiring go 1.18 * updated openapi, integrated overrides upstream, added new override to fix codegen bug * added nil check * fixed routes
This commit is contained in:
172
clients/script/script.go
Normal file
172
clients/script/script.go
Normal file
@ -0,0 +1,172 @@
|
||||
package script
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/influxdata/influx-cli/v2/api"
|
||||
"github.com/influxdata/influx-cli/v2/clients"
|
||||
"github.com/influxdata/influx-cli/v2/clients/query"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
clients.CLI
|
||||
api.InvokableScriptsApi
|
||||
}
|
||||
|
||||
func (c Client) printScripts(scripts []api.Script) error {
|
||||
if c.PrintAsJSON {
|
||||
return c.PrintJSON(scripts)
|
||||
}
|
||||
|
||||
headers := []string{
|
||||
"ID",
|
||||
"Name",
|
||||
"Description",
|
||||
"Organization ID",
|
||||
"Language",
|
||||
}
|
||||
|
||||
var rows []map[string]interface{}
|
||||
for _, s := range scripts {
|
||||
row := map[string]interface{}{
|
||||
"ID": *s.Id,
|
||||
"Name": s.Name,
|
||||
"Description": *s.Description,
|
||||
"Organization ID": s.OrgID,
|
||||
"Language": *s.Language,
|
||||
}
|
||||
|
||||
rows = append(rows, row)
|
||||
}
|
||||
|
||||
return c.PrintTable(headers, rows...)
|
||||
}
|
||||
|
||||
type ListParams struct {
|
||||
Limit int
|
||||
Offset int
|
||||
}
|
||||
|
||||
func (c Client) List(ctx context.Context, params *ListParams) error {
|
||||
req := c.GetScripts(ctx)
|
||||
if params != nil {
|
||||
req = req.Limit(int32(params.Limit)).Offset(int32(params.Offset))
|
||||
}
|
||||
res, err := req.Execute()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to list scripts: %v", err)
|
||||
}
|
||||
|
||||
return c.printScripts(*res.Scripts)
|
||||
}
|
||||
|
||||
type CreateParams struct {
|
||||
Description string
|
||||
Language string
|
||||
Name string
|
||||
Script string
|
||||
}
|
||||
|
||||
func (c Client) Create(ctx context.Context, params *CreateParams) error {
|
||||
if params == nil {
|
||||
return errors.New("failed to create script: no parameters provided")
|
||||
}
|
||||
|
||||
req := api.ScriptCreateRequest{
|
||||
Name: params.Name,
|
||||
Description: params.Description,
|
||||
Script: params.Script,
|
||||
Language: api.ScriptLanguage(params.Language),
|
||||
}
|
||||
|
||||
script, err := c.PostScripts(ctx).ScriptCreateRequest(req).Execute()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to create script: %v", err)
|
||||
}
|
||||
|
||||
return c.printScripts([]api.Script{script})
|
||||
}
|
||||
|
||||
type DeleteParams struct {
|
||||
ScriptID string
|
||||
}
|
||||
|
||||
func (c Client) Delete(ctx context.Context, params *DeleteParams) error {
|
||||
err := c.DeleteScriptsID(ctx, params.ScriptID).Execute()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to delete script: %v", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RetrieveParams struct {
|
||||
ScriptID string
|
||||
}
|
||||
|
||||
func (c Client) Retrieve(ctx context.Context, params *RetrieveParams) error {
|
||||
script, err := c.GetScriptsID(ctx, params.ScriptID).Execute()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to retrieve script: %v", err)
|
||||
}
|
||||
|
||||
return c.printScripts([]api.Script{script})
|
||||
}
|
||||
|
||||
type UpdateParams struct {
|
||||
ScriptID string
|
||||
Description string
|
||||
Name string
|
||||
Script string
|
||||
}
|
||||
|
||||
func (c Client) Update(ctx context.Context, params *UpdateParams) error {
|
||||
// Retrieve the original since we might carry over some unchanged details.
|
||||
oldScript, err := c.GetScriptsID(ctx, params.ScriptID).Execute()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update script: %v", err)
|
||||
}
|
||||
|
||||
if len(params.Description) == 0 {
|
||||
params.Description = *oldScript.Description
|
||||
}
|
||||
if len(params.Name) == 0 {
|
||||
params.Name = oldScript.Name
|
||||
}
|
||||
if len(params.Script) == 0 {
|
||||
params.Script = oldScript.Script
|
||||
}
|
||||
|
||||
req := api.ScriptUpdateRequest{
|
||||
Name: ¶ms.Name,
|
||||
Description: ¶ms.Description,
|
||||
Script: ¶ms.Script,
|
||||
}
|
||||
|
||||
script, err := c.PatchScriptsID(ctx, params.ScriptID).ScriptUpdateRequest(req).Execute()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to update script: %v", err)
|
||||
}
|
||||
|
||||
return c.printScripts([]api.Script{script})
|
||||
}
|
||||
|
||||
type InvokeParams struct {
|
||||
ScriptID string
|
||||
Params map[string]interface{}
|
||||
}
|
||||
|
||||
func (c Client) Invoke(ctx context.Context, params *InvokeParams) error {
|
||||
req := api.ScriptInvocationParams{
|
||||
Params: ¶ms.Params,
|
||||
}
|
||||
|
||||
resp, err := c.PostScriptsIDInvoke(ctx, params.ScriptID).ScriptInvocationParams(req).Execute()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to invoke script: %v", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
return query.RawResultPrinter.PrintQueryResults(resp.Body, c.StdIO)
|
||||
}
|
202
clients/script/script_test.go
Normal file
202
clients/script/script_test.go
Normal file
@ -0,0 +1,202 @@
|
||||
package script_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/influxdata/influx-cli/v2/api"
|
||||
"github.com/influxdata/influx-cli/v2/clients"
|
||||
"github.com/influxdata/influx-cli/v2/clients/script"
|
||||
"github.com/influxdata/influx-cli/v2/internal/mock"
|
||||
tmock "github.com/stretchr/testify/mock"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func Test_SimpleCreate(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
scriptsApi := mock.NewMockInvocableScriptsApi(ctrl)
|
||||
|
||||
var (
|
||||
scriptId = "123456789"
|
||||
scriptName = "simple"
|
||||
scriptDesc = "A basic script to be created"
|
||||
scriptOrgId = "1111111111111"
|
||||
scriptContent = `from(bucket: "sample_data") |> range(start: -10h)`
|
||||
scriptLanguage = api.SCRIPTLANGUAGE_FLUX
|
||||
)
|
||||
|
||||
scriptsApi.EXPECT().PostScripts(gomock.Any()).Return(api.ApiPostScriptsRequest{
|
||||
ApiService: scriptsApi,
|
||||
})
|
||||
|
||||
scriptsApi.EXPECT().PostScriptsExecute(gomock.Any()).Return(api.Script{
|
||||
Id: &scriptId,
|
||||
Name: scriptName,
|
||||
Description: &scriptDesc,
|
||||
OrgID: scriptOrgId,
|
||||
Script: scriptContent,
|
||||
Language: &scriptLanguage,
|
||||
}, nil)
|
||||
|
||||
stdio := mock.NewMockStdIO(ctrl)
|
||||
client := script.Client{
|
||||
CLI: clients.CLI{StdIO: stdio, PrintAsJSON: true},
|
||||
InvokableScriptsApi: scriptsApi,
|
||||
}
|
||||
|
||||
stdio.EXPECT().Write(tmock.MatchedBy(func(in []byte) bool {
|
||||
t.Logf("Stdio output: %s", in)
|
||||
inStr := string(in)
|
||||
// Verify we print the basic details of the script in some form.
|
||||
return strings.Contains(inStr, scriptId) &&
|
||||
strings.Contains(inStr, scriptName) &&
|
||||
strings.Contains(inStr, scriptOrgId)
|
||||
}))
|
||||
|
||||
params := script.CreateParams{
|
||||
Description: scriptDesc,
|
||||
Language: string(scriptLanguage),
|
||||
Name: scriptName,
|
||||
Script: scriptContent,
|
||||
}
|
||||
|
||||
require.NoError(t, client.Create(context.Background(), ¶ms))
|
||||
}
|
||||
|
||||
func strFactory(arg interface{}) *string {
|
||||
val := (arg.(string)) // Docker image runs Go 1.17, so we can't use generics here.
|
||||
return &val
|
||||
}
|
||||
|
||||
func Test_SimpleList(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
scriptsApi := mock.NewMockInvocableScriptsApi(ctrl)
|
||||
|
||||
language := api.SCRIPTLANGUAGE_FLUX
|
||||
scripts := []api.Script{{
|
||||
Id: strFactory("123456789"),
|
||||
Name: "simple",
|
||||
Description: strFactory("First script"),
|
||||
OrgID: "1111111111111",
|
||||
Script: `from(bucket: "sample_data") |> range(start: -10h)`,
|
||||
Language: &language,
|
||||
}, {
|
||||
Id: strFactory("000000001"),
|
||||
Name: "another",
|
||||
Description: strFactory("Second script"),
|
||||
OrgID: "9111111111119",
|
||||
Script: `from(bucket: "sample_data") |> range(start: -5h)`,
|
||||
Language: &language,
|
||||
},
|
||||
}
|
||||
|
||||
scriptsApi.EXPECT().GetScripts(gomock.Any()).Return(api.ApiGetScriptsRequest{
|
||||
ApiService: scriptsApi,
|
||||
})
|
||||
|
||||
scriptsApi.EXPECT().GetScriptsExecute(gomock.Any()).Return(api.Scripts{
|
||||
Scripts: &scripts,
|
||||
}, nil)
|
||||
|
||||
stdio := mock.NewMockStdIO(ctrl)
|
||||
client := script.Client{
|
||||
CLI: clients.CLI{StdIO: stdio, PrintAsJSON: true},
|
||||
InvokableScriptsApi: scriptsApi,
|
||||
}
|
||||
|
||||
stdio.EXPECT().Write(tmock.MatchedBy(func(in []byte) bool {
|
||||
t.Logf("Stdio output: %s", in)
|
||||
inStr := string(in)
|
||||
// Verify we print the basic details of all scripts in some form.
|
||||
success := true
|
||||
for _, script := range scripts {
|
||||
success = success && strings.Contains(inStr, *script.Id)
|
||||
success = success && strings.Contains(inStr, script.Name)
|
||||
success = success && strings.Contains(inStr, script.OrgID)
|
||||
}
|
||||
return success
|
||||
}))
|
||||
|
||||
params := script.ListParams{
|
||||
Limit: 10,
|
||||
Offset: 0,
|
||||
}
|
||||
|
||||
require.NoError(t, client.List(context.Background(), ¶ms))
|
||||
}
|
||||
|
||||
func Test_Update(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
ctrl := gomock.NewController(t)
|
||||
scriptsApi := mock.NewMockInvocableScriptsApi(ctrl)
|
||||
|
||||
var (
|
||||
scriptId = "123456789"
|
||||
scriptName = "simple"
|
||||
scriptDescOld = "A basic script to be created"
|
||||
scriptDescNew = "An updated script"
|
||||
scriptOrgId = "1111111111111"
|
||||
scriptContentOld = `from(bucket: "sample_data") |> range(start: -10h)`
|
||||
scriptContentNew = `from(bucket: "devbucket") |> range(start: -100h)`
|
||||
scriptLanguage = api.SCRIPTLANGUAGE_FLUX
|
||||
)
|
||||
|
||||
// If we change the logic for handling parameter defaulting, the GetScriptsID calls need to be removed.
|
||||
scriptsApi.EXPECT().GetScriptsID(gomock.Any(), scriptId).Return(api.ApiGetScriptsIDRequest{
|
||||
ApiService: scriptsApi,
|
||||
})
|
||||
|
||||
scriptsApi.EXPECT().GetScriptsIDExecute(gomock.Any()).Return(api.Script{
|
||||
Id: &scriptId,
|
||||
Name: scriptName,
|
||||
Description: &scriptDescOld,
|
||||
OrgID: scriptOrgId,
|
||||
Script: scriptContentOld,
|
||||
Language: &scriptLanguage,
|
||||
}, nil)
|
||||
|
||||
scriptsApi.EXPECT().PatchScriptsID(gomock.Any(), scriptId).Return(api.ApiPatchScriptsIDRequest{
|
||||
ApiService: scriptsApi,
|
||||
})
|
||||
|
||||
scriptsApi.EXPECT().PatchScriptsIDExecute(gomock.Any()).Return(api.Script{
|
||||
Id: &scriptId,
|
||||
Name: scriptName,
|
||||
Description: &scriptDescNew,
|
||||
OrgID: scriptOrgId,
|
||||
Script: scriptContentNew,
|
||||
Language: &scriptLanguage,
|
||||
}, nil)
|
||||
|
||||
stdio := mock.NewMockStdIO(ctrl)
|
||||
client := script.Client{
|
||||
CLI: clients.CLI{StdIO: stdio, PrintAsJSON: true},
|
||||
InvokableScriptsApi: scriptsApi,
|
||||
}
|
||||
|
||||
stdio.EXPECT().Write(tmock.MatchedBy(func(in []byte) bool {
|
||||
t.Logf("Stdio output: %s", in)
|
||||
inStr := string(in)
|
||||
// Verify we print the updated script's details.
|
||||
return strings.Contains(inStr, scriptId) &&
|
||||
strings.Contains(inStr, scriptName) &&
|
||||
strings.Contains(inStr, scriptDescNew) &&
|
||||
strings.Contains(inStr, scriptOrgId)
|
||||
}))
|
||||
|
||||
updateParams := script.UpdateParams{
|
||||
ScriptID: scriptId,
|
||||
Description: scriptDescNew,
|
||||
Name: scriptName,
|
||||
Script: scriptContentNew,
|
||||
}
|
||||
|
||||
require.NoError(t, client.Update(context.Background(), &updateParams))
|
||||
}
|
Reference in New Issue
Block a user