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

feat: pizza cli workspaces #82

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
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
35 changes: 31 additions & 4 deletions cmd/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
"os"
"os/signal"
"path"
"path/filepath"
"time"

"github.com/cli/browser"
Expand Down Expand Up @@ -79,7 +80,7 @@ func run() (string, error) {
return "", fmt.Errorf("PKCE error: %v", err.Error())
}

supabaseAuthURL := fmt.Sprintf("https://%s.supabase.co/auth/v1/authorize", supabaseID)
supabaseAuthURL := fmt.Sprintf("https://%s.supabase.co/auth/v1/authorize", supabaseBetaID)
queryParams := url.Values{
"provider": {"github"},
"code_challenge": {codeChallenge},
Expand Down Expand Up @@ -172,8 +173,8 @@ func run() (string, error) {
return username, nil
}

func getSession(authCode, codeVerifier string) (*accessTokenResponse, error) {
url := fmt.Sprintf("https://%s.supabase.co/auth/v1/token?grant_type=pkce", supabaseID)
func getSession(authCode, codeVerifier string) (*AccessTokenResponse, error) {
url := fmt.Sprintf("https://%s.supabase.co/auth/v1/token?grant_type=pkce", supabaseBetaID)

payload := map[string]string{
"auth_code": authCode,
Expand All @@ -196,7 +197,7 @@ func getSession(authCode, codeVerifier string) (*accessTokenResponse, error) {
return nil, fmt.Errorf("unexpected status: %s", res.Status)
}

var responseData accessTokenResponse
var responseData AccessTokenResponse
if err := json.NewDecoder(res.Body).Decode(&responseData); err != nil {
return nil, fmt.Errorf("could not decode JSON response: %s", err.Error())
}
Expand All @@ -223,3 +224,29 @@ func shutdown(server *http.Server) {
}
}()
}

func GetUserSession() (AccessTokenResponse, error) {
var accessToken AccessTokenResponse
homeDir, err := os.UserHomeDir()
if err != nil {
return accessToken, err
}
sessionFilePath := filepath.Join(homeDir, ".pizza", "session.json")

_, err = os.Stat(sessionFilePath)
if err != nil {
return accessToken, fmt.Errorf("Authentication is needed to perform this command. Authenticate using 'pizza login'")
}

bytes, err := os.ReadFile(sessionFilePath)
if err != nil {
return accessToken, fmt.Errorf("could not read session file")
}

err = json.Unmarshal(bytes, &accessToken)
if err != nil {
return accessToken, fmt.Errorf("could not unmarshal the token")
}

return accessToken, nil
}
5 changes: 3 additions & 2 deletions cmd/auth/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@ package auth

const (
codeChallengeLength = 87
supabaseID = "ibcwmlhcimymasokhgvn"
supabasePublicKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlhdCI6MTYyOTkzMDc3OCwiZXhwIjoxOTQ1NTA2Nzc4fQ.zcdbd7kDhk7iNSMo8SjsTaXi0wlLNNQcSZkzZ84NUDg"
supabaseBetaID = "fcqqkxwlntnrtjfbcioz"
supabaseProdID = "ibcwmlhcimymasokhgvn"
supabasePublicKey = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImZjcXFreHdsbnRucnRqZmJjaW96Iiwicm9sZSI6ImFub24iLCJpYXQiOjE2OTg0MTkyNzQsImV4cCI6MjAxMzk5NTI3NH0.ymWWYdnJC2gsnrJx4lZX2cfSOp-1xVuWFGt1Wr6zwtg"
authCallbackAddr = "localhost:3000"
)
10 changes: 5 additions & 5 deletions cmd/auth/schema.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package auth

type accessTokenResponse struct {
type AccessTokenResponse struct {
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
TokenType string `json:"token_type"`
ExpiresIn int `json:"expires_in"`
ExpiresAt int `json:"expires_at"`
User userSchema `json:"user"`
User UserSchema `json:"user"`
}

type userSchema struct {
type UserSchema struct {
ID string `json:"id"`
Aud string `json:"aud,omitempty"`
Role string `json:"role"`
Expand All @@ -28,15 +28,15 @@ type userSchema struct {
LastSignInAt string `json:"last_sign_in_at"`
AppMetadata map[string]interface{} `json:"app_metadata"`
UserMetadata map[string]interface{} `json:"user_metadata"`
Factors []mfaFactorSchema `json:"factors"`
Factors []MfaFactorSchema `json:"factors"`
Identities []interface{} `json:"identities"`
BannedUntil string `json:"banned_until"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
DeletedAt string `json:"deleted_at"`
}

type mfaFactorSchema struct {
type MfaFactorSchema struct {
ID string `json:"id"`
Status string `json:"status"`
FriendlyName string `json:"friendly_name"`
Expand Down
2 changes: 1 addition & 1 deletion cmd/bake/bake.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ func NewBakeCommand() *cobra.Command {
}

func run(opts *Options) error {
repositories, err := utils.HandleRepositoryValues(opts.Repos, opts.FilePath)
repositories, err := utils.HandleUniqueValues(opts.Repos, opts.FilePath)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/insights/contributors.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (opts *contributorsOptions) run(ctx context.Context) error {
return fmt.Errorf("invalid period: %d, accepts (7,30,90)", opts.Period)
}

repositories, err := utils.HandleRepositoryValues(opts.Repos, opts.FilePath)
repositories, err := utils.HandleUniqueValues(opts.Repos, opts.FilePath)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/insights/repositories.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ func NewRepositoriesCommand() *cobra.Command {
}

func (opts *repositoriesOptions) run(ctx context.Context) error {
repositories, err := utils.HandleRepositoryValues(opts.Repos, opts.FilePath)
repositories, err := utils.HandleUniqueValues(opts.Repos, opts.FilePath)
if err != nil {
return err
}
Expand Down
2 changes: 1 addition & 1 deletion cmd/insights/user-contributions.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ func NewUserContributionsCommand() *cobra.Command {
}

func (opts *userContributionsOptions) run(ctx context.Context) error {
repositories, err := utils.HandleRepositoryValues(opts.Repos, opts.FilePath)
repositories, err := utils.HandleUniqueValues(opts.Repos, opts.FilePath)
if err != nil {
return err
}
Expand Down
2 changes: 2 additions & 0 deletions cmd/root/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
repoquery "github.com/open-sauced/pizza-cli/cmd/repo-query"
"github.com/open-sauced/pizza-cli/cmd/show"
"github.com/open-sauced/pizza-cli/cmd/version"
"github.com/open-sauced/pizza-cli/cmd/workspaces"
"github.com/open-sauced/pizza-cli/pkg/constants"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -44,6 +45,7 @@ func NewRootCommand() (*cobra.Command, error) {
cmd.AddCommand(insights.NewInsightsCommand())
cmd.AddCommand(version.NewVersionCommand())
cmd.AddCommand(show.NewShowCommand())
cmd.AddCommand(workspaces.NewWorkspacesCommand())

return cmd, nil
}
Expand Down
96 changes: 96 additions & 0 deletions cmd/workspaces/add.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package workspaces

import (
"context"
"fmt"
"net/http"

client "github.com/open-sauced/go-api/client"
"github.com/open-sauced/pizza-cli/cmd/auth"
"github.com/open-sauced/pizza-cli/pkg/constants"
"github.com/open-sauced/pizza-cli/pkg/utils"
"github.com/spf13/cobra"
)

type AddCommand struct {
*workspacesOptions
// Repos is the array of git repository urls
Repos []string
// FilePath: the path to the yaml file
FilePath string
// TUI: terminal interface mode
TUI bool
}

func NewAddWorkSpaceCommand(workspaceOpts *workspacesOptions) *cobra.Command {
addCmd := &AddCommand{FilePath: "", workspacesOptions: workspaceOpts}
cmd := &cobra.Command{
Use: "add url... [flags]",
Short: "add repositories and contributors to a workspace",
Long: "add repositories and contributors to a workspace",
PreRunE: func(_ *cobra.Command, args []string) error {
if addCmd.Session.AccessToken == "" {
session, err := auth.GetUserSession()
if err != nil {
return err
}
addCmd.Session = session
}
return nil
},
Args: func(cmd *cobra.Command, args []string) error {
fileFlag := cmd.Flags().Lookup(constants.FlagNameFile)
if !fileFlag.Changed && len(args) == 0 && !addCmd.TUI {
return fmt.Errorf("must specify git repository url argument(s) or provide %s flag", fileFlag.Name)

}
addCmd.Repos = append(addCmd.Repos, args...)
return nil
},

RunE: func(cmd *cobra.Command, args []string) error {
return addCmd.run()
},
TraverseChildren: true,
}

cmd.Flags().StringVarP(&addCmd.FilePath, constants.FlagNameFile, "f", "", "Path to yaml file containing an array of git repository urls")
cmd.Flags().StringVarP(&addCmd.WorkspaceName, "name", "n", addCmd.WorkspaceName, "name of the workspace to be created")
cmd.Flags().BoolVar(&addCmd.TUI, "tui", addCmd.TUI, "use terminal user interface")
return cmd
}

func (a *AddCommand) run() error {
var workspaceData client.CreateWorkspaceDto
var err error
if a.TUI {
workspaceData, err = a.createView()
if err != nil {
return err
}
} else {
repos, err := utils.HandleUniqueValues(a.Repos, a.FilePath)
if err != nil {
return err
}
parsedRepos := make([]interface{}, len(repos))
i := 0
for repo := range repos {
parsedRepos[i] = repo
i++
}
workspaceData = *client.NewCreateWorkspaceDto(a.WorkspaceName, "my workspace", []interface{}{a.Session.User.UserMetadata["user_name"]}, parsedRepos)
}

authCtx := context.WithValue(context.Background(), client.ContextAccessToken, a.Session.AccessToken)
_, r, err := a.APIClient.WorkspacesServiceAPI.CreateWorkspaceForUser(authCtx).CreateWorkspaceDto(workspaceData).Execute()
if err != nil {
return err
}
if r.StatusCode != http.StatusCreated {
return fmt.Errorf("HTTP status: %d", r.StatusCode)
}

fmt.Printf("Workspace %s, has been created!", workspaceData.Name)
return nil
}
51 changes: 51 additions & 0 deletions cmd/workspaces/list.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package workspaces

import (
"context"
"fmt"
"net/http"

sw "github.com/open-sauced/go-api/client"
"github.com/open-sauced/pizza-cli/cmd/auth"
"github.com/spf13/cobra"
)

type ListCommandOpts struct {
*workspacesOptions
}

func NewListWorkSpaceCommand(workspaceOpts *workspacesOptions) *cobra.Command {
opts := &ListCommandOpts{workspaceOpts}
cmd := &cobra.Command{
Use: "list",
Short: "list all workspaces",
Long: "retrieve all the workspaces",
PreRunE: func(_ *cobra.Command, args []string) error {
if opts.Session.AccessToken == "" {
session, err := auth.GetUserSession()
if err != nil {
return err
}
opts.Session = session
}
return nil
},
RunE: func(_ *cobra.Command, args []string) error {
return opts.run()
},
}
return cmd
}

func (opts *ListCommandOpts) run() error {
authCtx := context.WithValue(context.Background(), sw.ContextAccessToken, opts.Session.AccessToken)
// here should return an array of DbWorkspace
_, r, err := opts.APIClient.WorkspacesServiceAPI.GetWorkspaceForUser(authCtx).Execute()
if err != nil {
return err
}
if r.StatusCode != http.StatusOK {
return fmt.Errorf("HTTP status: %d", r.StatusCode)
}
return nil
}
53 changes: 53 additions & 0 deletions cmd/workspaces/views.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package workspaces

import (
"fmt"

"github.com/charmbracelet/huh"
client "github.com/open-sauced/go-api/client"
"github.com/open-sauced/pizza-cli/pkg/utils"
)

func (a *AddCommand) createView() (client.CreateWorkspaceDto, error) {
formValues := client.CreateWorkspaceDto{}
var reposInput, membersInput string
form := huh.NewForm(
huh.NewGroup(
huh.NewInput().Title("Workspace Name").Value(&formValues.Name).Validate(func(s string) error {
if s == "" {
return fmt.Errorf("workspace name required")
}
return nil
}),
huh.NewText().Title("Description").Value(&formValues.Description).Lines(2),
huh.NewText().Title("Repositories").
Description("repositories to add to the workspace (yaml file, or comma separated values)").Lines(2).
Validate(func(input string) error {
if _, err := utils.ParseFileAndCSV(input); err != nil {
return err
}

return nil
}).Value(&reposInput),
huh.NewText().Title("Members").Description("members to add to the workspace (yaml file, or comma separated values)").Lines(2).
Validate(func(input string) error {
if _, err := utils.ParseFileAndCSV(input); err != nil {
return err
}
return nil
}).Value(&membersInput),
),
)

if err := form.Run(); err != nil {
return formValues, err
}

// errors are checked in form
repos, _ := utils.ParseFileAndCSV(reposInput)
members, _ := utils.ParseFileAndCSV(membersInput)
formValues.Repos = repos
formValues.Members = members

return formValues, nil
}
Loading
Loading