diff --git a/Makefile b/Makefile index 87a8f3d..0db730e 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,9 @@ all: lint test build +fmt: + go fmt ./... + lint: docker run \ -t \ @@ -22,5 +25,3 @@ install: build run: go run main.go - - diff --git a/cmd/bake/bake.go b/cmd/bake/bake.go index 05fac33..8bc2407 100644 --- a/cmd/bake/bake.go +++ b/cmd/bake/bake.go @@ -11,6 +11,7 @@ import ( "net/http" "os" + "github.com/open-sauced/pizza-cli/pkg/api" "github.com/spf13/cobra" "gopkg.in/yaml.v3" @@ -19,8 +20,8 @@ import ( // Options are the options for the pizza bake command including user // defined configurations type Options struct { - // Endpoint is the service endpoint to reach out to - Endpoint string + // The API Client for the calls to bake git repos + APIClient *api.Client // URLs are the git repo URLs that will be sourced via 'pizza bake' URLs []string @@ -52,14 +53,20 @@ func NewBakeCommand() *cobra.Command { return nil }, RunE: func(cmd *cobra.Command, args []string) error { + endpoint, _ := cmd.Flags().GetString("endpoint") + useBeta, _ := cmd.Flags().GetBool("beta") + + if useBeta { + fmt.Printf("Using beta API endpoint - %s\n", api.BetaAPIEndpoint) + endpoint = api.BetaAPIEndpoint + } + opts.APIClient = api.NewClient(endpoint) + opts.URLs = append(opts.URLs, args...) return run(opts) }, } - // TODO - this will need to be a live service URL by default. - // For now, localhost is fine. - cmd.Flags().StringVarP(&opts.Endpoint, "endpoint", "e", "http://localhost:8080", "The endpoint to send requests to") cmd.Flags().BoolVarP(&opts.Wait, "wait", "w", false, "Wait for bake processing to finish") cmd.Flags().StringVarP(&opts.FilePath, "file", "f", "", "The yaml file containing a series of repos to batch to /bake") @@ -68,7 +75,7 @@ func NewBakeCommand() *cobra.Command { type bakePostRequest struct { URL string `json:"url"` - Wait bool `json:"wait,omitempty"` + Wait bool `json:"wait"` } type repos struct { @@ -110,7 +117,7 @@ func run(opts *Options) error { Wait: opts.Wait, } - err := bakeRepo(bodyPostReq, opts.Endpoint) + err := bakeRepo(bodyPostReq, opts.APIClient) if err != nil { fmt.Printf("Error: failed fetch of %s repository (%s)\n", url, err.Error()) } @@ -119,15 +126,14 @@ func run(opts *Options) error { return nil } -func bakeRepo(bodyPostReq bakePostRequest, endpoint string) error { +func bakeRepo(bodyPostReq bakePostRequest, apiClient *api.Client) error { bodyPostJSON, err := json.Marshal(bodyPostReq) if err != nil { return err } - requestBody := bytes.NewBuffer(bodyPostJSON) - resp, err := http.Post(fmt.Sprintf("%s/bake", endpoint), "application/json", requestBody) - + responseBody := bytes.NewBuffer(bodyPostJSON) + resp, err := apiClient.HTTPClient.Post(fmt.Sprintf("%s/bake", apiClient.Endpoint), "application/json", responseBody) if err != nil { return err } diff --git a/cmd/bake/bake_test.go b/cmd/bake/bake_test.go index 88ab9c5..0c5fd00 100644 --- a/cmd/bake/bake_test.go +++ b/cmd/bake/bake_test.go @@ -4,6 +4,8 @@ import ( "net/http" "net/http/httptest" "testing" + + "github.com/open-sauced/pizza-cli/pkg/api" ) func TestSendsPost(t *testing.T) { @@ -42,7 +44,7 @@ func TestSendsPost(t *testing.T) { })) defer testServer.Close() - tt.opts.Endpoint = testServer.URL + tt.opts.APIClient = api.NewClient(testServer.URL) err := run(tt.opts) if err != nil { diff --git a/cmd/repo-query/repo-query.go b/cmd/repo-query/repo-query.go index 2d7c346..b78e98b 100644 --- a/cmd/repo-query/repo-query.go +++ b/cmd/repo-query/repo-query.go @@ -12,12 +12,15 @@ import ( "os/signal" "strings" + "github.com/open-sauced/pizza-cli/pkg/api" "github.com/spf13/cobra" ) const repoQueryURL string = "https://opensauced.tools" type Options struct { + APIClient *api.Client + // URL is the git repo URL that will be indexed URL string @@ -46,6 +49,22 @@ func NewRepoQueryCommand() *cobra.Command { return nil }, RunE: func(cmd *cobra.Command, args []string) error { + endpoint, _ := cmd.Flags().GetString("endpoint") + useBeta, _ := cmd.Flags().GetBool("beta") + + if useBeta { + fmt.Printf("Warning!! Using beta API endpoint not supported for repo-query - using: %s\n", endpoint) + } + + // The repo-query is currently not deployed behind "api.opensauced.pizza" + // So, if the user has not changed the desired "endpoint", use the default + // tools URL to send SSE to the repo-query engine + if endpoint == api.APIEndpoint { + endpoint = repoQueryURL + } + + opts.APIClient = api.NewClient(endpoint) + opts.URL = args[0] return run(opts) }, @@ -56,7 +75,17 @@ func NewRepoQueryCommand() *cobra.Command { return cmd } -func getOwnerAndRepo(url string) (owner, repo string, err error) { +type repoQueryAgent struct { + client *api.Client +} + +func newRepoQueryAgent(apiClient *api.Client) *repoQueryAgent { + return &repoQueryAgent{ + client: apiClient, + } +} + +func (rq *repoQueryAgent) getOwnerAndRepo(url string) (owner, repo string, err error) { if !strings.HasPrefix(url, "https://github.com/") { return "", "", fmt.Errorf("invalid URL: %s", url) } @@ -79,14 +108,16 @@ func getOwnerAndRepo(url string) (owner, repo string, err error) { } func run(opts *Options) error { + agent := newRepoQueryAgent(opts.APIClient) + // get repo name and owner name from URL - owner, repo, err := getOwnerAndRepo(opts.URL) + owner, repo, err := agent.getOwnerAndRepo(opts.URL) if err != nil { return err } fmt.Printf("Checking if %s/%s is indexed by us...⏳\n", owner, repo) - resp, err := http.Get(fmt.Sprintf("%s/collection?owner=%s&name=%s&branch=%s", repoQueryURL, owner, repo, opts.branch)) + resp, err := agent.client.HTTPClient.Get(fmt.Sprintf("%s/collection?owner=%s&name=%s&branch=%s", agent.client.Endpoint, owner, repo, opts.branch)) if err != nil { return err } @@ -96,12 +127,12 @@ func run(opts *Options) error { // repo is not indexed fmt.Println("Repo not found❗") fmt.Println("Indexing repo...⏳") - err := indexRepo(owner, repo, opts.branch) + err := agent.indexRepo(owner, repo, opts.branch) if err != nil { return err } - err = startQnALoop(owner, repo, opts.branch) + err = agent.startQnALoop(owner, repo, opts.branch) if err != nil { return err } @@ -109,7 +140,7 @@ func run(opts *Options) error { // repo is indexed fmt.Println("Repo found ✅") - err = startQnALoop(owner, repo, opts.branch) + err = agent.startQnALoop(owner, repo, opts.branch) if err != nil { return err } @@ -126,7 +157,7 @@ func run(opts *Options) error { return nil } -func startQnALoop(owner string, repo string, branch string) error { +func (rq *repoQueryAgent) startQnALoop(owner string, repo string, branch string) error { for { // if ctrl+c is pressed, exit c := make(chan os.Signal, 1) @@ -148,7 +179,7 @@ func startQnALoop(owner string, repo string, branch string) error { fmt.Println("🍕Exiting...") os.Exit(0) } - err := askQuestion(input, owner, repo, branch) + err := rq.askQuestion(input, owner, repo, branch) if err != nil { return err } @@ -176,7 +207,7 @@ const ( ChatChunk ) -func indexRepo(owner string, repo string, branch string) error { +func (rq *repoQueryAgent) indexRepo(owner string, repo string, branch string) error { indexPostReq := &indexPostRequest{ Owner: owner, Name: repo, @@ -188,7 +219,7 @@ func indexRepo(owner string, repo string, branch string) error { return err } - resp, err := http.Post(fmt.Sprintf("%s/embed", repoQueryURL), "application/json", bytes.NewBuffer(indexPostJSON)) + resp, err := rq.client.HTTPClient.Post(fmt.Sprintf("%s/embed", rq.client.Endpoint), "application/json", bytes.NewBuffer(indexPostJSON)) if err != nil { return err } @@ -204,7 +235,7 @@ func indexRepo(owner string, repo string, branch string) error { } reader := bufio.NewReader(resp.Body) - err = listenForSSEs(reader, IndexChunk) + err = rq.listenForSSEs(reader, IndexChunk) if err != nil { return err } @@ -212,7 +243,7 @@ func indexRepo(owner string, repo string, branch string) error { return nil } -func askQuestion(question string, owner string, repo string, branch string) error { +func (rq *repoQueryAgent) askQuestion(question string, owner string, repo string, branch string) error { queryPostReq := &queryPostRequest{ Query: question, Repository: struct { @@ -231,7 +262,7 @@ func askQuestion(question string, owner string, repo string, branch string) erro return err } - resp, err := http.Post(fmt.Sprintf("%s/query", repoQueryURL), "application/json", bytes.NewBuffer(queryPostJSON)) + resp, err := rq.client.HTTPClient.Post(fmt.Sprintf("%s/query", rq.client.Endpoint), "application/json", bytes.NewBuffer(queryPostJSON)) if err != nil { return err } @@ -249,7 +280,7 @@ func askQuestion(question string, owner string, repo string, branch string) erro // listen for SSEs and send data,event pairs to processChatChunk reader := bufio.NewReader(resp.Body) - err = listenForSSEs(reader, ChatChunk) + err = rq.listenForSSEs(reader, ChatChunk) if err != nil { return err } @@ -257,7 +288,7 @@ func askQuestion(question string, owner string, repo string, branch string) erro return nil } -func listenForSSEs(reader *bufio.Reader, chunkType int) error { +func (rq *repoQueryAgent) listenForSSEs(reader *bufio.Reader, chunkType int) error { // listen for SSEs and send data, event pairs to processChunk // we send 2 lines at a time to processChunk so it can process the event and data together. // the server sends empty events sometimes, so we ignore those. @@ -295,9 +326,9 @@ func listenForSSEs(reader *bufio.Reader, chunkType int) error { switch chunkType { case IndexChunk: - err = processIndexChunk(chunk) + err = rq.processIndexChunk(chunk) case ChatChunk: - err = processChatChunk(chunk) + err = rq.processChatChunk(chunk) default: break } @@ -309,7 +340,7 @@ func listenForSSEs(reader *bufio.Reader, chunkType int) error { } } -func processIndexChunk(chunk string) error { +func (rq *repoQueryAgent) processIndexChunk(chunk string) error { // we only care about the first line of the chunk, which is the event, when indexing. // the data is irrelevant for now, but we still got it so we can process it later if we need to. // Also, for grouping the events and data together. @@ -337,7 +368,7 @@ func processIndexChunk(chunk string) error { return nil } -func processChatChunk(chunk string) error { +func (rq *repoQueryAgent) processChatChunk(chunk string) error { // The event is the first line of the chunk, and the data is the second line. chunkLines := strings.Split(chunk, "\n") eventLine := chunkLines[0] diff --git a/cmd/root/root.go b/cmd/root/root.go index 23fda04..4ad6e77 100644 --- a/cmd/root/root.go +++ b/cmd/root/root.go @@ -2,11 +2,14 @@ package root import ( + "fmt" + "github.com/spf13/cobra" "github.com/open-sauced/pizza-cli/cmd/auth" "github.com/open-sauced/pizza-cli/cmd/bake" repoquery "github.com/open-sauced/pizza-cli/cmd/repo-query" + "github.com/open-sauced/pizza-cli/pkg/api" ) // NewRootCommand bootstraps a new root cobra command for the pizza CLI @@ -18,6 +21,9 @@ func NewRootCommand() (*cobra.Command, error) { RunE: run, } + cmd.PersistentFlags().StringP("endpoint", "e", api.APIEndpoint, "The API endpoint to send requests to") + cmd.PersistentFlags().Bool("beta", false, fmt.Sprintf("Shorthand for using the beta OpenSauced API endpoint (\"%s\"). Superceds the '--endpoint' flag", api.BetaAPIEndpoint)) + cmd.AddCommand(bake.NewBakeCommand()) cmd.AddCommand(repoquery.NewRepoQueryCommand()) cmd.AddCommand(auth.NewLoginCommand()) diff --git a/pkg/api/client.go b/pkg/api/client.go new file mode 100644 index 0000000..e519936 --- /dev/null +++ b/pkg/api/client.go @@ -0,0 +1,20 @@ +package api + +import "net/http" + +type Client struct { + // The configured http client for making API requests + HTTPClient *http.Client + + // The API endpoint to use when making requests + // Example: https://api.opensauced.pizza or https://beta.api.opensauced.pizza + Endpoint string +} + +// NewClient creates a new OpenSauced API client for making http requests +func NewClient(endpoint string) *Client { + return &Client{ + HTTPClient: &http.Client{}, + Endpoint: endpoint, + } +} diff --git a/pkg/api/constants.go b/pkg/api/constants.go new file mode 100644 index 0000000..66a71e7 --- /dev/null +++ b/pkg/api/constants.go @@ -0,0 +1,6 @@ +package api + +const ( + APIEndpoint = "https://api.opensauced.pizza/v1" + BetaAPIEndpoint = "https://beta.api.opensauced.pizza/v1" +)