Skip to content

Commit

Permalink
Resource Credentials APIs (#178)
Browse files Browse the repository at this point in the history
- Create Modify Read and Delete Resource Credentials
  • Loading branch information
doriac11 authored Jan 16, 2025
1 parent daf1181 commit 4d825fa
Show file tree
Hide file tree
Showing 7 changed files with 1,608 additions and 2 deletions.
10 changes: 9 additions & 1 deletion api.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © 2019 - 2022 Dell Inc. or its subsidiaries. All Rights Reserved.
// Copyright © 2019 - 2025 Dell Inc. or its subsidiaries. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -189,6 +189,14 @@ func basicAuth(username, password string) string {
return base64.StdEncoding.EncodeToString([]byte(auth))
}

func (c *Client) xmlRequest(method, uri string, body, resp interface{}) (*http.Response, error) {
response, err := c.api.DoXMLRequest(context.Background(), method, uri, c.configConnect.Version, body, resp)
if err != nil {
doLog(logger.Error, err.Error())
}
return response, err
}

func (c *Client) getJSONWithRetry(
method, uri string,
body, resp interface{},
Expand Down
115 changes: 114 additions & 1 deletion api/api.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright © 2019 - 2022 Dell Inc. or its subsidiaries. All Rights Reserved.
// Copyright © 2019 - 2025 Dell Inc. or its subsidiaries. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
Expand All @@ -18,6 +18,7 @@ import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"io"
Expand Down Expand Up @@ -108,6 +109,13 @@ type Client interface {

// ParseJSONError parses the JSON in r into an error object
ParseJSONError(r *http.Response) error

// DoXMLRequest sends an HTTP request to the API.
DoXMLRequest(
ctx context.Context,
method, path, version string,
body, response interface{},
) (*http.Response, error)
}

type client struct {
Expand Down Expand Up @@ -432,6 +440,111 @@ func (c *client) GetToken() string {
return c.token
}

func (c *client) DoXMLRequest(
ctx context.Context,
method, path, version string,
body, resp interface{},
) (*http.Response, error) {
var (
err error
req *http.Request
res *http.Response
ubf = &bytes.Buffer{}
luri = len(path)
hostEndsWithSlash = endsWithSlash(c.host)
uriBeginsWithSlash = beginsWithSlash(path)
)
ubf.WriteString(c.host)

if !hostEndsWithSlash && (luri > 0) {
ubf.WriteString("/")
}

if luri > 0 {
if uriBeginsWithSlash {
ubf.WriteString(path[1:])
} else {
ubf.WriteString(path)
}
}

u, err := url.Parse(ubf.String())
if err != nil {
return nil, err
}
if body != nil {
xmlBody, err := xml.Marshal(body)
if err != nil {
c.doLog(logger.Error, fmt.Sprintf("Error marshaling XML: %v", err))
return nil, err
}

// Create the HTTP request
req, err = http.NewRequest(method, u.String(), bytes.NewBuffer(xmlBody))
if err != nil {
c.doLog(logger.Error, fmt.Sprintf("Error creating request: %v", err))
return nil, err
}
} else {
req, err = http.NewRequest(method, u.String(), nil)
if err != nil {
c.doLog(logger.Error, fmt.Sprintf("Error creating request: %v", err))
return nil, err
}
}

req.Header.Set("Content-Type", "application/xml")
// add headers to the request
if version != "" {
ver, err := strconv.ParseFloat(version, 64)
if err != nil {
return nil, err
}

// set the auth token
if c.token != "" {
// use Bearer Authentication if the powerflex array
// version >= 4.0
if ver >= 4.0 {
bearer := "Bearer " + c.token
req.Header.Set("Authorization", bearer)
} else {
req.SetBasicAuth("", c.token)
}
}

} else {
if c.token != "" {
req.SetBasicAuth("", c.token)
}
}

// send the request
req = req.WithContext(ctx)
if res, err = c.http.Do(req); err != nil {
return nil, err
}

// parse the response
switch {
case res == nil:
return nil, nil
case res.StatusCode >= 200 && res.StatusCode <= 299:
if resp == nil {
return nil, nil
}
dec := json.NewDecoder(res.Body)
if err = dec.Decode(resp); err != nil && err != io.EOF {
c.doLog(logger.Error, fmt.Sprintf("Error: %s Unable to decode response into %+v", err.Error(), resp))
return nil, err
}
default:
return nil, c.ParseJSONError(res)
}

return res, err
}

func (c *client) ParseJSONError(r *http.Response) error {
jsonError := &types.Error{}

Expand Down
106 changes: 106 additions & 0 deletions api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,112 @@ func TestDo(t *testing.T) {
}
}

func TestDoXMLRequest(t *testing.T) {
tests := map[string]struct {
method string
path string
body interface{}
expectedErr error
expectedBody string
}{
"successful GET request": {
method: http.MethodGet,
path: "/api/test",
body: nil,
expectedErr: nil,
expectedBody: `{"message":"success"}`,
},
"successful Post request Body not Nil": {
method: http.MethodPost,
path: "/api/test",
body: types.SwitchCredentialWrapper{
IomCredential: types.IomCredential{
Username: "test",
Password: "test",
},
},
expectedErr: nil,
expectedBody: `{"message":"success"}`,
},
"successful Post request Body without begining slash": {
method: http.MethodPost,
path: "api/test",
body: types.SwitchCredentialWrapper{
IomCredential: types.IomCredential{
Username: "test",
Password: "test",
},
},
expectedErr: nil,
expectedBody: `{"message":"success"}`,
},
"Fail Invalid Body": {
method: http.MethodPost,
path: "api/test",
body: map[string]string{"test": "test"},
expectedErr: fmt.Errorf("xml: unsupported type: map[string]string"),
expectedBody: "",
},
}

for name, tt := range tests {
t.Run(name, func(t *testing.T) {
// Create a test server to handle the request
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != tt.method {
t.Fatal(fmt.Errorf("wrong method. Expected %s; but got %s", tt.method, r.Method))
}

if tt.expectedErr != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(tt.expectedErr.Error()))
} else {
w.WriteHeader(http.StatusOK)
w.Write([]byte(tt.expectedBody))
}
}))
defer ts.Close()

// Create a new client and set the host to the test server
c, err := New(context.Background(), ts.URL, ClientOptions{}, false)
if err != nil {
t.Fatal(err)
}

// Call the DoWithHeaders function with the test parameters
var resp interface{}
_, err = c.DoXMLRequest(
context.Background(),
tt.method,
tt.path,
"3.6",
tt.body,
&resp,
)

// Check if the error matches the expected error
if tt.expectedErr != nil {
if err == nil || err.Error() != tt.expectedErr.Error() {
t.Errorf("expected error %v, got %v", tt.expectedErr, err)
}
} else if err != nil {
t.Errorf("unexpected error: %v", err)
}

// Check if the response body matches the expected body
if tt.expectedBody != "" {
b, err := json.Marshal(resp)
if err != nil {
t.Fatal(err)
}
if string(b) != tt.expectedBody {
t.Errorf("expected response body %s, got %s", tt.expectedBody, string(b))
}
}
})
}
}

func TestDoWithHeaders(t *testing.T) {
tests := map[string]struct {
method string
Expand Down
Loading

0 comments on commit 4d825fa

Please sign in to comment.