From 14fa41c8a9b84faa6be91701f53b0a9e3e1c75ea Mon Sep 17 00:00:00 2001 From: Maniraja Date: Fri, 30 Jun 2023 22:04:40 -0400 Subject: [PATCH 1/4] Add support for Pipedream CLI --- plugins/pipedream/api_key.go | 100 +++++++++++++++++++++++++ plugins/pipedream/api_key_test.go | 45 +++++++++++ plugins/pipedream/pd.go | 25 +++++++ plugins/pipedream/plugin.go | 22 ++++++ plugins/pipedream/test-fixtures/config | 2 + sdk/schema/fieldname/names.go | 2 + 6 files changed, 196 insertions(+) create mode 100644 plugins/pipedream/api_key.go create mode 100644 plugins/pipedream/api_key_test.go create mode 100644 plugins/pipedream/pd.go create mode 100644 plugins/pipedream/plugin.go create mode 100644 plugins/pipedream/test-fixtures/config diff --git a/plugins/pipedream/api_key.go b/plugins/pipedream/api_key.go new file mode 100644 index 000000000..1e5b8c0dd --- /dev/null +++ b/plugins/pipedream/api_key.go @@ -0,0 +1,100 @@ +package pipedream + +import ( + "context" + + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/importer" + "github.com/1Password/shell-plugins/sdk/provision" + "github.com/1Password/shell-plugins/sdk/schema" + "github.com/1Password/shell-plugins/sdk/schema/credname" + "github.com/1Password/shell-plugins/sdk/schema/fieldname" +) + +func APIKey() schema.CredentialType { + return schema.CredentialType{ + Name: credname.APIKey, + DocsURL: sdk.URL("https://pipedream.com/docs/api_key"), // TODO: Replace with actual URL + ManagementURL: sdk.URL("https://console.pipedream.com/user/security/tokens"), // TODO: Replace with actual URL + Fields: []schema.CredentialField{ + { + Name: fieldname.APIKey, + MarkdownDescription: "API Key used to authenticate to Pipedream.", + Secret: true, + Composition: &schema.ValueComposition{ + Length: 32, + Charset: schema.Charset{ + Lowercase: true, + Digits: true, + }, + }, + }, + { + Name: fieldname.OrgID, + MarkdownDescription: "OrgId for the Pipedream organization.", + Secret: true, + Composition: &schema.ValueComposition{ + Length: 9, + Charset: schema.Charset{ + Lowercase: true, + Uppercase: true, + Digits: true, + Symbols: true, + }, + }, + }, + }, + DefaultProvisioner: provision.TempFile( + pipedreamConfig, + provision.AtFixedPath("~/.config/pipedream/config"), + ), + Importer: importer.TryAll( + TryPipedreamConfigFile(), + )} +} + +func TryPipedreamConfigFile() sdk.Importer { + return importer.TryFile("~/.config/pipedream/config", func(ctx context.Context, contents importer.FileContents, in sdk.ImportInput, out *sdk.ImportAttempt) { + configFile, err := contents.ToINI() + + if err != nil { + out.AddError(err) + return + } + + for _, section := range configFile.Sections() { + fields := make(map[sdk.FieldName]string) + if section.HasKey("api_key") && section.Key("api_key").Value() != "" { + fields[fieldname.APIKey] = section.Key("api_key").Value() + } + if section.HasKey("org_id") && section.Key("org_id").Value() != "" { + fields[fieldname.OrgID] = section.Key("org_id").Value() + } + + if fields[fieldname.APIKey] != "" && fields[fieldname.OrgID] != "" { + out.AddCandidate(sdk.ImportCandidate{ + Fields: fields, + }) + } + } + }) +} + +type Config struct { + APIKey string + OrgId string +} + +func pipedreamConfig(in sdk.ProvisionInput) ([]byte, error) { + contents := "" + + if apikey, ok := in.ItemFields[fieldname.APIKey]; ok { + contents += "api_key = " + apikey + "\n" + } + + if orgid, ok := in.ItemFields[fieldname.OrgID]; ok { + contents += "org_id = " + orgid + "\n" + } + + return []byte(contents), nil +} diff --git a/plugins/pipedream/api_key_test.go b/plugins/pipedream/api_key_test.go new file mode 100644 index 000000000..d1caa3194 --- /dev/null +++ b/plugins/pipedream/api_key_test.go @@ -0,0 +1,45 @@ +package pipedream + +import ( + "testing" + + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/plugintest" + "github.com/1Password/shell-plugins/sdk/schema/fieldname" +) + +func TestAPIKeyProvisioner(t *testing.T) { + plugintest.TestProvisioner(t, APIKey().DefaultProvisioner, map[string]plugintest.ProvisionCase{ + "config file": { + ItemFields: map[sdk.FieldName]string{ + fieldname.Key: "9cfvvd7bp6099paodoua5shfoexample", + fieldname.OrgID: "b_EXAMPLE", + }, + ExpectedOutput: sdk.ProvisionOutput{ + Files: map[string]sdk.OutputFile{ + "~/.config/pipedream/config": { + Contents: []byte(plugintest.LoadFixture(t, "config")), + }, + }, + }, + }, + }) +} + +func TestAPIKeyImporter(t *testing.T) { + plugintest.TestImporter(t, APIKey().Importer, map[string]plugintest.ImportCase{ + "config file": { + Files: map[string]string{ + "~/.config/pipedream/config": plugintest.LoadFixture(t, "config"), + }, + ExpectedCandidates: []sdk.ImportCandidate{ + { + Fields: map[sdk.FieldName]string{ + fieldname.APIKey: "9cfvvd7bp6099paodoua5shfoexample", + fieldname.OrgID: "b_EXAMPLE", + }, + }, + }, + }, + }) +} diff --git a/plugins/pipedream/pd.go b/plugins/pipedream/pd.go new file mode 100644 index 000000000..674511799 --- /dev/null +++ b/plugins/pipedream/pd.go @@ -0,0 +1,25 @@ +package pipedream + +import ( + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/needsauth" + "github.com/1Password/shell-plugins/sdk/schema" + "github.com/1Password/shell-plugins/sdk/schema/credname" +) + +func PipedreamCLI() schema.Executable { + return schema.Executable{ + Name: "Pipedream CLI", + Runs: []string{"pd"}, + DocsURL: sdk.URL("https://pipedream.com/docs/cli/reference/"), + NeedsAuth: needsauth.IfAll( + needsauth.NotForHelpOrVersion(), + needsauth.NotWithoutArgs(), + ), + Uses: []schema.CredentialUsage{ + { + Name: credname.APIKey, + }, + }, + } +} diff --git a/plugins/pipedream/plugin.go b/plugins/pipedream/plugin.go new file mode 100644 index 000000000..b2dfb7983 --- /dev/null +++ b/plugins/pipedream/plugin.go @@ -0,0 +1,22 @@ +package pipedream + +import ( + "github.com/1Password/shell-plugins/sdk" + "github.com/1Password/shell-plugins/sdk/schema" +) + +func New() schema.Plugin { + return schema.Plugin{ + Name: "pipedream", + Platform: schema.PlatformInfo{ + Name: "Pipedream", + Homepage: sdk.URL("https://pipedream.com"), + }, + Credentials: []schema.CredentialType{ + APIKey(), + }, + Executables: []schema.Executable{ + PipedreamCLI(), + }, + } +} diff --git a/plugins/pipedream/test-fixtures/config b/plugins/pipedream/test-fixtures/config new file mode 100644 index 000000000..dc194585c --- /dev/null +++ b/plugins/pipedream/test-fixtures/config @@ -0,0 +1,2 @@ +api_key = 9cfvvd7bp6099paodoua5shfoexample +org_id = b_EXAMPLE \ No newline at end of file diff --git a/sdk/schema/fieldname/names.go b/sdk/schema/fieldname/names.go index d227bc1a8..ef5a9c70f 100644 --- a/sdk/schema/fieldname/names.go +++ b/sdk/schema/fieldname/names.go @@ -36,6 +36,7 @@ const ( Mode = sdk.FieldName("Mode") Namespace = sdk.FieldName("Namespace") OneTimePassword = sdk.FieldName("One-Time Password") + OrgID = sdk.FieldName("Org ID") OrgURL = sdk.FieldName("Org URL") Organization = sdk.FieldName("Organization") Password = sdk.FieldName("Password") @@ -86,6 +87,7 @@ func ListAll() []sdk.FieldName { Mode, Namespace, OneTimePassword, + OrgID, OrgURL, Organization, Password, From 29033741531c218633c4cc2bac6d1bb925e88a91 Mon Sep 17 00:00:00 2001 From: Maniraja Date: Mon, 17 Jul 2023 14:33:59 -0400 Subject: [PATCH 2/4] Address feedback --- plugins/pipedream/api_key.go | 4 ++-- plugins/pipedream/pd.go | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/plugins/pipedream/api_key.go b/plugins/pipedream/api_key.go index 1e5b8c0dd..07d2eb3ba 100644 --- a/plugins/pipedream/api_key.go +++ b/plugins/pipedream/api_key.go @@ -14,8 +14,8 @@ import ( func APIKey() schema.CredentialType { return schema.CredentialType{ Name: credname.APIKey, - DocsURL: sdk.URL("https://pipedream.com/docs/api_key"), // TODO: Replace with actual URL - ManagementURL: sdk.URL("https://console.pipedream.com/user/security/tokens"), // TODO: Replace with actual URL + DocsURL: sdk.URL("https://pipedream.com/docs/api_key"), + ManagementURL: sdk.URL("https://console.pipedream.com/user/security/tokens"), Fields: []schema.CredentialField{ { Name: fieldname.APIKey, diff --git a/plugins/pipedream/pd.go b/plugins/pipedream/pd.go index 674511799..1090f5743 100644 --- a/plugins/pipedream/pd.go +++ b/plugins/pipedream/pd.go @@ -15,6 +15,8 @@ func PipedreamCLI() schema.Executable { NeedsAuth: needsauth.IfAll( needsauth.NotForHelpOrVersion(), needsauth.NotWithoutArgs(), + needsauth.NotWhenContainsArgs("-p"), + needsauth.NotWhenContainsArgs("--profile"), ), Uses: []schema.CredentialUsage{ { From f95dee3659acbe831da4777ae3db816883158639 Mon Sep 17 00:00:00 2001 From: Maniraja Date: Mon, 17 Jul 2023 20:15:12 -0400 Subject: [PATCH 3/4] NameHint support and mark OrgId as optional field --- plugins/pipedream/api_key.go | 6 ++++-- plugins/pipedream/api_key_test.go | 26 +++++++++++++++++------ plugins/pipedream/test-fixtures/config | 2 -- plugins/pipedream/test-fixtures/import | 9 ++++++++ plugins/pipedream/test-fixtures/provision | 2 ++ 5 files changed, 35 insertions(+), 10 deletions(-) delete mode 100644 plugins/pipedream/test-fixtures/config create mode 100644 plugins/pipedream/test-fixtures/import create mode 100644 plugins/pipedream/test-fixtures/provision diff --git a/plugins/pipedream/api_key.go b/plugins/pipedream/api_key.go index 07d2eb3ba..f111fef79 100644 --- a/plugins/pipedream/api_key.go +++ b/plugins/pipedream/api_key.go @@ -33,6 +33,7 @@ func APIKey() schema.CredentialType { Name: fieldname.OrgID, MarkdownDescription: "OrgId for the Pipedream organization.", Secret: true, + Optional: true, Composition: &schema.ValueComposition{ Length: 9, Charset: schema.Charset{ @@ -71,9 +72,10 @@ func TryPipedreamConfigFile() sdk.Importer { fields[fieldname.OrgID] = section.Key("org_id").Value() } - if fields[fieldname.APIKey] != "" && fields[fieldname.OrgID] != "" { + if fields[fieldname.APIKey] != "" { out.AddCandidate(sdk.ImportCandidate{ - Fields: fields, + Fields: fields, + NameHint: importer.SanitizeNameHint(section.Name()), }) } } diff --git a/plugins/pipedream/api_key_test.go b/plugins/pipedream/api_key_test.go index d1caa3194..3104c3a9a 100644 --- a/plugins/pipedream/api_key_test.go +++ b/plugins/pipedream/api_key_test.go @@ -12,13 +12,13 @@ func TestAPIKeyProvisioner(t *testing.T) { plugintest.TestProvisioner(t, APIKey().DefaultProvisioner, map[string]plugintest.ProvisionCase{ "config file": { ItemFields: map[sdk.FieldName]string{ - fieldname.Key: "9cfvvd7bp6099paodoua5shfoexample", - fieldname.OrgID: "b_EXAMPLE", + fieldname.APIKey: "ugvfxesz62ycsl42z49c0t1hjexample", + fieldname.OrgID: "YbEXAMPLE", }, ExpectedOutput: sdk.ProvisionOutput{ Files: map[string]sdk.OutputFile{ "~/.config/pipedream/config": { - Contents: []byte(plugintest.LoadFixture(t, "config")), + Contents: []byte(plugintest.LoadFixture(t, "provision")), }, }, }, @@ -30,14 +30,28 @@ func TestAPIKeyImporter(t *testing.T) { plugintest.TestImporter(t, APIKey().Importer, map[string]plugintest.ImportCase{ "config file": { Files: map[string]string{ - "~/.config/pipedream/config": plugintest.LoadFixture(t, "config"), + "~/.config/pipedream/config": plugintest.LoadFixture(t, "import"), }, ExpectedCandidates: []sdk.ImportCandidate{ { Fields: map[sdk.FieldName]string{ - fieldname.APIKey: "9cfvvd7bp6099paodoua5shfoexample", - fieldname.OrgID: "b_EXAMPLE", + fieldname.APIKey: "ugvfxesz62ycsl42z49c0t1hjexample", + fieldname.OrgID: "YbEXAMPLE", }, + NameHint: "DEFAULT", + }, + { + Fields: map[sdk.FieldName]string{ + fieldname.APIKey: "5puf32rvhkz83c6oj4wpxvaniexample", + fieldname.OrgID: "KVEXAMPLE", + }, + NameHint: "first", + }, + { + Fields: map[sdk.FieldName]string{ + fieldname.APIKey: "lgx1amb0qf7mjy6y7nkgfc3x9example", + }, + NameHint: "second", }, }, }, diff --git a/plugins/pipedream/test-fixtures/config b/plugins/pipedream/test-fixtures/config deleted file mode 100644 index dc194585c..000000000 --- a/plugins/pipedream/test-fixtures/config +++ /dev/null @@ -1,2 +0,0 @@ -api_key = 9cfvvd7bp6099paodoua5shfoexample -org_id = b_EXAMPLE \ No newline at end of file diff --git a/plugins/pipedream/test-fixtures/import b/plugins/pipedream/test-fixtures/import new file mode 100644 index 000000000..7e2347f2d --- /dev/null +++ b/plugins/pipedream/test-fixtures/import @@ -0,0 +1,9 @@ +api_key = ugvfxesz62ycsl42z49c0t1hjexample +org_id = YbEXAMPLE + +[first] +api_key = 5puf32rvhkz83c6oj4wpxvaniexample +org_id = KVEXAMPLE + +[second] +api_key = lgx1amb0qf7mjy6y7nkgfc3x9example diff --git a/plugins/pipedream/test-fixtures/provision b/plugins/pipedream/test-fixtures/provision new file mode 100644 index 000000000..4dd5e1c26 --- /dev/null +++ b/plugins/pipedream/test-fixtures/provision @@ -0,0 +1,2 @@ +api_key = ugvfxesz62ycsl42z49c0t1hjexample +org_id = YbEXAMPLE From 396867dedf5545256e0e968c808d2bd34a1dec5d Mon Sep 17 00:00:00 2001 From: Arun Sathiya Date: Tue, 25 Jul 2023 07:51:15 -0700 Subject: [PATCH 4/4] Update Org ID field as it is not a secret Co-authored-by: Simon Barendse --- plugins/pipedream/api_key.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/pipedream/api_key.go b/plugins/pipedream/api_key.go index f111fef79..7a1a23dad 100644 --- a/plugins/pipedream/api_key.go +++ b/plugins/pipedream/api_key.go @@ -32,7 +32,7 @@ func APIKey() schema.CredentialType { { Name: fieldname.OrgID, MarkdownDescription: "OrgId for the Pipedream organization.", - Secret: true, + Secret: false, Optional: true, Composition: &schema.ValueComposition{ Length: 9,