Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Firebase Shell Plugin #331

Closed
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions plugins/firebase/access_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package firebase

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 AccessToken() schema.CredentialType {
return schema.CredentialType{
Name: credname.AccessToken,
DocsURL: sdk.URL("https://firebase.google.com/docs/cli#cli-ci-systems"),
ManagementURL: sdk.URL("https://firebase.google.com/docs/cli#cli-ci-systems"),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove the ManagementURL because that URL is printed by the firebase login:ci command. If it's a standard link and if you are familiar with it, let's include that.

Fields: []schema.CredentialField{
{
Name: fieldname.Token,
MarkdownDescription: "Token used to authenticate to firebase.",
Secret: true,
Composition: &schema.ValueComposition{
Length: 53,
Prefix: "dummy_firebase_", // TODO: Check if this is correct
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is correct. This field is referring to the prefix like ghp_ in the case of GitHub Personal Access Tokens. Would you know if Firebase tokens have a similar prefix? If not, we can remove this field.

Charset: schema.Charset{
Lowercase: true,
Digits: true,
},
},
},
},
DefaultProvisioner: provision.EnvVars(defaultEnvVarMapping),
Importer: importer.TryAll(
importer.TryEnvVarPair(defaultEnvVarMapping),
TryfirebaseConfigFile(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we actually importing from a configuration file? If yes, could we include an example file at /test-fixtures path? Example from ngrok:

https://github.com/1Password/shell-plugins/blob/f57be66f86be90cf47450b1b0c4226c8c669c2a5/plugins/ngrok/test-fixtures/config.yml

)}
}

var defaultEnvVarMapping = map[string]sdk.FieldName{
"FIREBASE_TOKEN": fieldname.Token, // TODO: Check if this is correct
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's remove this TODO.

}

// TODO: Check if the platform stores the Access Token in a local config file, and if so,
// implement the function below to add support for importing it.
func TryfirebaseConfigFile() sdk.Importer {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The config file importer doesn't seem written. Let's make sure to do that, and let's also test it by actually running op plugin init firebase. If the importer is properly written, there'd be a prompt to import those secrets.

return importer.TryFile("~/path/to/config/file.yml", func(ctx context.Context, contents importer.FileContents, in sdk.ImportInput, out *sdk.ImportAttempt) {
// var config Config
// if err := contents.ToYAML(&config); err != nil {
// out.AddError(err)
// return
// }

// if config.Token == "" {
// return
// }

// out.AddCandidate(sdk.ImportCandidate{
// Fields: map[sdk.FieldName]string{
// fieldname.Token: config.Token,
// },
// })
})
}

// TODO: Implement the config file schema
// type Config struct {
// Token string
// }
55 changes: 55 additions & 0 deletions plugins/firebase/access_token_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package firebase

import (
"testing"

"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/plugintest"
"github.com/1Password/shell-plugins/sdk/schema/fieldname"
)

func TestAccessTokenProvisioner(t *testing.T) {
plugintest.TestProvisioner(t, AccessToken().DefaultProvisioner, map[string]plugintest.ProvisionCase{
"default": {
ItemFields: map[sdk.FieldName]string{ // TODO: Check if this is correct
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This TODO can be removed.

fieldname.Token: "dummy_firebase_3bhfuelt31a99503j251bua8rov58m2example",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefix to be fixed/removed once the correct prefix is known.

},
ExpectedOutput: sdk.ProvisionOutput{
Environment: map[string]string{
"FIREBASE_TOKEN": "dummy_firebase_3bhfuelt31a99503j251bua8rov58m2example",
},
},
},
})
}

func TestAccessTokenImporter(t *testing.T) {
plugintest.TestImporter(t, AccessToken().Importer, map[string]plugintest.ImportCase{
"environment": {
Environment: map[string]string{ // TODO: Check if this is correct
"FIREBASE_TOKEN": "dummy_firebase_3bhfuelt31a99503j251bua8rov58m2example",
},
ExpectedCandidates: []sdk.ImportCandidate{
{
Fields: map[sdk.FieldName]string{
fieldname.Token: "dummy_firebase_3bhfuelt31a99503j251bua8rov58m2example",
},
},
},
},
// TODO: If you implemented a config file importer, add a test file example in firebase/test-fixtures
// and fill the necessary details in the test template below.
"config file": {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Config file importer test to be completed once the importer is written.

Files: map[string]string{
// "~/path/to/config.yml": plugintest.LoadFixture(t, "config.yml"),
},
ExpectedCandidates: []sdk.ImportCandidate{
// {
// Fields: map[sdk.FieldName]string{
// fieldname.Token: "dummy_firebase_3bhfuelt31a99503j251bua8rov58m2example",
// },
// },
},
},
})
}
25 changes: 25 additions & 0 deletions plugins/firebase/firebase.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package firebase

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 firebaseCLI() schema.Executable {
return schema.Executable{
Name: "Firebase CLI",
Runs: []string{"firebase"},
DocsURL: sdk.URL("https://firebase.google.com/docs/cli"),
NeedsAuth: needsauth.IfAll(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's skip auth if the command contains the arg --token because it takes precedence over envvars:

image

needsauth.NotForHelpOrVersion(),
needsauth.NotWithoutArgs(),
),
Uses: []schema.CredentialUsage{
{
Name: credname.AccessToken,
},
},
}
}
22 changes: 22 additions & 0 deletions plugins/firebase/plugin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package firebase

import (
"github.com/1Password/shell-plugins/sdk"
"github.com/1Password/shell-plugins/sdk/schema"
)

func New() schema.Plugin {
return schema.Plugin{
Name: "firebase",
Platform: schema.PlatformInfo{
Name: "firebase",
Homepage: sdk.URL("https://firebase.google.com/"),
},
Credentials: []schema.CredentialType{
AccessToken(),
},
Executables: []schema.Executable{
firebaseCLI(),
},
}
}