-
Notifications
You must be signed in to change notification settings - Fork 69
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix: add support for calling any endpoint from control plane (#1497)
- Loading branch information
Showing
6 changed files
with
212 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters