Skip to content

Commit

Permalink
fix: add support for calling any endpoint from control plane (#1497)
Browse files Browse the repository at this point in the history
  • Loading branch information
wtrocki authored Mar 30, 2022
1 parent 47a5b7c commit bffe907
Show file tree
Hide file tree
Showing 6 changed files with 212 additions and 3 deletions.
106 changes: 106 additions & 0 deletions pkg/api/generic/api.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package generic

import (
"context"
"errors"
"io"
"net/http"
"strings"
)

type GenericAPI interface {
GET(ctx context.Context, path string) (interface{}, *http.Response, error)
POST(ctx context.Context, path string, body io.Reader) (interface{}, *http.Response, error)
}

// APIConfig defines the available configuration options
// to customize the API client settings
type Config struct {
// HTTPClient is a custom HTTP client
HTTPClient *http.Client
// Debug enables debug-level logging
Debug bool
// BaseURL sets a custom API server base URL
BaseURL string
}

func NewGenericAPIClient(cfg *Config) GenericAPI {
if cfg.HTTPClient == nil {
cfg.HTTPClient = http.DefaultClient
}

c := APIClient{
baseURL: cfg.BaseURL,
httpClient: cfg.HTTPClient,
}

return &c
}

type APIClient struct {
httpClient *http.Client
baseURL string
}

func (c *APIClient) GET(ctx context.Context, path string) (interface{}, *http.Response, error) {
url := c.baseURL + path

req, err := http.NewRequest("GET", url, nil)
if err != nil {
return err, nil, err
}

req = req.WithContext(ctx)

req.Header.Set("Accept", "application/json")

resp, err := c.httpClient.Do(req)
if err != nil {
return nil, resp, err
}
if resp.StatusCode > http.StatusBadRequest {
return nil, resp, errors.New(resp.Status)
}
defer resp.Body.Close()

b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, resp, err
}

return string(b), resp, err
}

func (c *APIClient) POST(ctx context.Context, path string, body io.Reader) (interface{}, *http.Response, error) {
url := c.baseURL + path
bodyBinary, err := io.ReadAll(body)

if err != nil {
return err, nil, err
}
requestBody := strings.NewReader(string(bodyBinary))
req, err := http.NewRequest("POST", url, requestBody)
if err != nil {
return nil, nil, err
}

req = req.WithContext(ctx)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Accept", "application/json")

resp, err := c.httpClient.Do(req)
if err != nil {
return nil, resp, err
}
if resp.StatusCode > http.StatusBadRequest {
return nil, resp, errors.New(resp.Status)
}
defer resp.Body.Close()

b, err := io.ReadAll(resp.Body)
if err != nil {
return nil, resp, err
}

return string(b), resp, err
}
5 changes: 2 additions & 3 deletions pkg/api/rbac/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,8 @@ func NewPrincipalAPIClient(cfg *Config) PrincipalAPI {
}

type APIClient struct {
httpClient *http.Client
baseURL *url.URL
AccessToken string
httpClient *http.Client
baseURL *url.URL
}

// GetPrincipals returns the list of user's in the current users organization/tenant
Expand Down
88 changes: 88 additions & 0 deletions pkg/cmd/request/request.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package request

import (
"context"
"errors"
"fmt"

"github.com/redhat-developer/app-services-cli/pkg/cmd/registry/artifact/util"
"github.com/redhat-developer/app-services-cli/pkg/core/ioutil/iostreams"
"github.com/redhat-developer/app-services-cli/pkg/core/localize"
"github.com/redhat-developer/app-services-cli/pkg/core/logging"
"github.com/redhat-developer/app-services-cli/pkg/shared/connection"
"github.com/redhat-developer/app-services-cli/pkg/shared/factory"
"github.com/spf13/cobra"
)

type options struct {
IO *iostreams.IOStreams
Logger logging.Logger
localizer localize.Localizer
Context context.Context
Connection factory.ConnectionFunc

urlPath string
method string
}

func NewCallCmd(f *factory.Factory) *cobra.Command {
opts := &options{
IO: f.IOStreams,
Logger: f.Logger,
localizer: f.Localizer,
Context: f.Context,
Connection: f.Connection,
}

cmd := &cobra.Command{
Use: "request",
Short: "Allows you to perform API requests against the API server",
Example: `
# Perform a GET request to the specified path
rhoas request --path /api/kafkas_mgmt/v1/kafkas
# Perform a POST request to the specified path
cat request.json | rhoas request --path "/api/kafkas_mgmt/v1/kafkas?async=true" --method post `,
Hidden: true,
Args: cobra.NoArgs,
RunE: func(cmd *cobra.Command, args []string) error {
return runCmd(opts)
},
}
cmd.Flags().StringVar(&opts.urlPath, "path", "", "Path to send request. For example /api/kafkas_mgmt/v1/kafkas?async=true")
cmd.Flags().StringVar(&opts.method, "method", "GET", "HTTP method to use. (get, post)")
return cmd
}

func runCmd(opts *options) (err error) {
if opts.urlPath == "" {
return errors.New("--path is required")
}
opts.Logger.Info("Performing request to", opts.urlPath)
conn, err := opts.Connection(connection.DefaultConfigSkipMasAuth)

if err != nil {
return err
}

var data interface{}
var response interface{}
if opts.method == "post" {
opts.Logger.Info("POST request. Reading file from standard input")
specifiedFile, err1 := util.CreateFileFromStdin()
if err1 != nil {
return err
}
data, response, err = conn.API().GenericAPI().POST(opts.Context, opts.urlPath, specifiedFile)
} else {
data, response, err = conn.API().GenericAPI().GET(opts.Context, opts.urlPath)
}

if err != nil || data == nil {
opts.Logger.Info("Fetching data failed", err, response)
return err
}

fmt.Fprint(opts.IO.Out, data)
return nil
}
2 changes: 2 additions & 0 deletions pkg/cmd/root/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/redhat-developer/app-services-cli/pkg/cmd/login"
"github.com/redhat-developer/app-services-cli/pkg/cmd/logout"
"github.com/redhat-developer/app-services-cli/pkg/cmd/registry"
"github.com/redhat-developer/app-services-cli/pkg/cmd/request"
"github.com/redhat-developer/app-services-cli/pkg/cmd/serviceaccount"
"github.com/redhat-developer/app-services-cli/pkg/cmd/status"
cliversion "github.com/redhat-developer/app-services-cli/pkg/cmd/version"
Expand Down Expand Up @@ -53,6 +54,7 @@ func NewRootCommand(f *factory.Factory, version string) *cobra.Command {
cmd.AddCommand(registry.NewServiceRegistryCommand(f))

cmd.AddCommand(docs.NewDocsCmd(f))
cmd.AddCommand(request.NewCallCmd(f))

return cmd
}
2 changes: 2 additions & 0 deletions pkg/shared/connection/api/api.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package api

import (
"github.com/redhat-developer/app-services-cli/pkg/api/generic"
"github.com/redhat-developer/app-services-cli/pkg/api/rbac"
amsclient "github.com/redhat-developer/app-services-sdk-go/accountmgmt/apiv1/client"
kafkainstanceclient "github.com/redhat-developer/app-services-sdk-go/kafkainstance/apiv1internal/client"
Expand All @@ -17,4 +18,5 @@ type API interface {
ServiceRegistryInstance(instanceID string) (*registryinstanceclient.APIClient, *registrymgmtclient.Registry, error)
AccountMgmt() amsclient.AppServicesApi
RBAC() rbac.RbacAPI
GenericAPI() generic.GenericAPI
}
12 changes: 12 additions & 0 deletions pkg/shared/connection/api/defaultapi/default_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/redhat-developer/app-services-cli/pkg/shared/kafkautil"

"github.com/redhat-developer/app-services-cli/internal/build"
"github.com/redhat-developer/app-services-cli/pkg/api/generic"
"github.com/redhat-developer/app-services-cli/pkg/api/rbac"
"github.com/redhat-developer/app-services-cli/pkg/core/logging"
"github.com/redhat-developer/app-services-cli/pkg/shared/connection/api"
Expand Down Expand Up @@ -231,6 +232,17 @@ func (a *defaultAPI) ServiceRegistryInstance(instanceID string) (*registryinstan
return client, &instance, nil
}

func (a *defaultAPI) GenericAPI() generic.GenericAPI {
tc := a.createOAuthTransport(a.AccessToken)
client := generic.NewGenericAPIClient(&generic.Config{
BaseURL: a.ApiURL.String(),
Debug: a.Logger.DebugEnabled(),
HTTPClient: tc,
})

return client
}

// AccountMgmt returns a new Account Management API client instance
func (a *defaultAPI) AccountMgmt() amsclient.AppServicesApi {
cfg := amsclient.NewConfiguration()
Expand Down

0 comments on commit bffe907

Please sign in to comment.