Skip to content

Commit

Permalink
Fix value processor. Cover with tests. (#14)
Browse files Browse the repository at this point in the history
Fix value processor. Cover with tests.
  • Loading branch information
lexbritvin authored Jan 21, 2025
1 parent 28e3457 commit 9ab631f
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 35 deletions.
13 changes: 8 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ toolchain go1.23.4

require (
filippo.io/age v1.2.1
github.com/launchrctl/launchr v0.17.1
github.com/launchrctl/launchr v0.17.2
github.com/stretchr/testify v1.10.0
golang.org/x/term v0.28.0
gopkg.in/yaml.v3 v3.0.1
)
Expand All @@ -20,6 +21,7 @@ require (
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/containerd/console v1.0.4 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/docker/docker v27.5.0+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
Expand Down Expand Up @@ -49,6 +51,7 @@ require (
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/pterm/pterm v0.12.80 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect
Expand All @@ -57,11 +60,11 @@ require (
github.com/spf13/pflag v1.0.5 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 // indirect
go.opentelemetry.io/otel v1.33.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 // indirect
go.opentelemetry.io/otel v1.34.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 // indirect
go.opentelemetry.io/otel/metric v1.33.0 // indirect
go.opentelemetry.io/otel/trace v1.33.0 // indirect
go.opentelemetry.io/otel/metric v1.34.0 // indirect
go.opentelemetry.io/otel/trace v1.34.0 // indirect
go.opentelemetry.io/proto/otlp v1.3.1 // indirect
go.uber.org/mock v0.5.0 // indirect
golang.org/x/crypto v0.32.0 // indirect
Expand Down
20 changes: 10 additions & 10 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -227,8 +227,8 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/launchrctl/launchr v0.17.1 h1:0mzOBE2M776MVQf62IuT686u/cZKQJDruMlVRq6RQw0=
github.com/launchrctl/launchr v0.17.1/go.mod h1:aI/DfDXJLk7xya2jowNAAykWeRoe1Tp6qI2NGGJwmj8=
github.com/launchrctl/launchr v0.17.2 h1:3wFUxdIvoWERREy+xgi2N69VXyy2Vkq2Lh75nw0fhBw=
github.com/launchrctl/launchr v0.17.2/go.mod h1:aI/DfDXJLk7xya2jowNAAykWeRoe1Tp6qI2NGGJwmj8=
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
Expand Down Expand Up @@ -374,20 +374,20 @@ go.etcd.io/etcd/client/pkg/v3 v3.5.4/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3
go.etcd.io/etcd/client/v3 v3.5.4/go.mod h1:ZaRkVgBZC+L+dLCjTcF1hRXpgZXQPOvnA/Ak/gq3kiY=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q=
go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw=
go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0 h1:CV7UdSGJt/Ao6Gp4CXckLxVRRsRgDHoI8XjbL3PDl8s=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.59.0/go.mod h1:FRmFuRJfag1IZ2dPkHnEoSFVgTVPUd2qf5Vi69hLb8I=
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0 h1:K0XaT3DwHAcV4nKLzcQvwAgSyisUghWoY20I7huthMk=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.31.0/go.mod h1:B5Ki776z/MBnVha1Nzwp5arlzBbE3+1jk+pGmaP5HME=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0 h1:lUsI2TYsQw2r1IASwoROaCnjdj2cvC2+Jbxvk6nHnWU=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.31.0/go.mod h1:2HpZxxQurfGxJlJDblybejHB6RX6pmExPNe517hREw4=
go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ=
go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M=
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s=
go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck=
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
Expand Down
48 changes: 28 additions & 20 deletions plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import (
)

const (
getByKeyProc = "keyring.GetKeyValue"
procGetKeyValue = "keyring.GetKeyValue"
errTplNotFoundURL = "%s not found in keyring. Use `%s keyring:login` to add it."
errTplNotFoundKey = "%s not found in keyring. Use `%s keyring:set` to add it."
)
Expand Down Expand Up @@ -62,43 +62,51 @@ func (p *Plugin) OnAppInit(app launchr.App) error {
var m action.Manager
app.GetService(&m)

AddValueProcessors(m, p.k)
addValueProcessors(m, p.k)
return nil
}

// AddValueProcessors adds a keyring [action.ValueProcessor] to [action.Manager].
func AddValueProcessors(m action.Manager, keyring Keyring) {
getByKey := func(value any, options map[string]any) (any, error) {
return getByKeyProcessor(value, options, keyring)
// GetKeyValueProcessorOptions is a [action.ValueProcessorOptions] struct.
type GetKeyValueProcessorOptions struct {
Key string `yaml:"key"`
}

// Validate implements [action.ValueProcessorOptions] interface.
func (o *GetKeyValueProcessorOptions) Validate() error {
if o.Key == "" {
return fmt.Errorf(`option "key" is required for %q processor`, procGetKeyValue)
}
return nil
}

proc := action.NewFuncProcessor([]jsonschema.Type{jsonschema.String}, getByKey)
m.AddValueProcessor(getByKeyProc, proc)
// addValueProcessors adds a keyring [action.ValueProcessor] to [action.Manager].
func addValueProcessors(m action.Manager, keyring Keyring) {
m.AddValueProcessor(procGetKeyValue, action.GenericValueProcessor[*GetKeyValueProcessorOptions]{
Types: []jsonschema.Type{jsonschema.String},
Fn: func(v any, opts *GetKeyValueProcessorOptions, ctx action.ValueProcessorContext) (any, error) {
return processGetByKey(v, opts, ctx, keyring)
},
})
}

func getByKeyProcessor(value any, options map[string]any, k Keyring) (any, error) {
func processGetByKey(value any, opts *GetKeyValueProcessorOptions, ctx action.ValueProcessorContext, k Keyring) (any, error) {
val, ok := value.(string)
if !ok && value != nil {
return val, fmt.Errorf(
"string type is expected for %q processor. Change value type or remove the processor",
getByKeyProc,
procGetKeyValue,
)
}

if val != "" {
launchr.Term().Warning().Printfln("Skipping processor %q, value is not empty. Value will remain unchanged", getByKeyProc)
launchr.Log().Warn("skipping processor, value is not empty", "processor", getByKeyProc)
if ctx.IsChanged {
launchr.Term().Warning().Printfln("Skipping processor %q, value is not empty. Value will remain unchanged", procGetKeyValue)
launchr.Log().Warn("skipping processor, value is not empty", "processor", procGetKeyValue)
return value, nil
}

key, ok := options["key"].(string)
if !ok {
return value, fmt.Errorf(`option "key" is required for %q processor`, getByKeyProc)
}

v, err := k.GetForKey(key)
v, err := k.GetForKey(opts.Key)
if err != nil {
return value, buildNotFoundError(key, errTplNotFoundKey, err)
return value, buildNotFoundError(opts.Key, errTplNotFoundKey, err)
}

return v.Value, nil
Expand Down
80 changes: 80 additions & 0 deletions plugin_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package keyring

import (
"fmt"
"testing"

"github.com/stretchr/testify/require"

"github.com/launchrctl/launchr/pkg/action"
)

const testActionYaml = `
runtime: plugin
action:
title: test keyring
options:
- name: secret
process:
- processor: keyring.GetKeyValue
options:
key: storedsecret
`

const testActionYamlWithDefault = `
runtime: plugin
action:
title: test keyring
options:
- name: secret
default: "mydefault"
process:
- processor: keyring.GetKeyValue
options:
key: storedsecret
`

const testActionYamlWrongOptions = `
runtime: plugin
action:
title: test keyring
options:
- name: secret
process:
- processor: keyring.GetKeyValue
`

func Test_KeyringProcessor(t *testing.T) {
// Prepare services.
k := &keyringService{
store: &dataStoreYaml{file: &plainFile{fname: "teststorage.yaml"}},
}
am := action.NewManager()
addValueProcessors(am, k)

// Prepare test data.
expected := "my_secret"
err := k.AddItem(KeyValueItem{Key: "storedsecret", Value: expected})
require.NoError(t, err)

expConfig := action.InputParams{
"secret": expected,
}
expGiven := action.InputParams{
"secret": "my_user_secret",
}
errOpts := fmt.Errorf("option %q is required for %q processor", "key", procGetKeyValue)
tt := []action.TestCaseValueProcessor{
{Name: "get keyring keyvalue - no input given", Yaml: testActionYaml, ExpOpts: expConfig},
{Name: "get keyring keyvalue - default and no input given", Yaml: testActionYamlWithDefault, ExpOpts: expConfig},
{Name: "get keyring keyvalue - input given", Yaml: testActionYaml, Opts: expGiven, ExpOpts: expGiven},
{Name: "get keyring keyvalue - wrong options", Yaml: testActionYamlWrongOptions, ErrInit: errOpts},
}
for _, tt := range tt {
tt := tt
t.Run(tt.Name, func(t *testing.T) {
t.Parallel()
tt.Test(t, am)
})
}
}

0 comments on commit 9ab631f

Please sign in to comment.