Skip to content

Commit

Permalink
refactor: cloudian sdk allow callsite to pass context
Browse files Browse the repository at this point in the history
  • Loading branch information
mariatsji committed Nov 25, 2024
1 parent 27177d0 commit 15972b5
Showing 1 changed file with 167 additions and 143 deletions.
310 changes: 167 additions & 143 deletions internal/sdk/cloudian/sdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"io"
"net/http"
"strconv"
"time"
)

type Client struct {
Expand All @@ -25,9 +24,7 @@ func NewClient(baseUrl string, tokenBase64 string) *Client {
}
}

var ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)

func (client Client) newRequest(url string, method string, body *[]byte) (*http.Request, error) {
func (client Client) newRequest(ctx context.Context, url string, method string, body *[]byte) (*http.Request, error) {
req, err := http.NewRequestWithContext(ctx, method, url, bytes.NewBuffer(*body))
if err != nil {
return req, fmt.Errorf("error creating request: %w", err)
Expand Down Expand Up @@ -77,204 +74,231 @@ func unmarshalUsersJson(data []byte) ([]User, error) {
}

// List all users of a group
func (client Client) ListUsers(groupId string, offsetUserId *string) ([]User, error) {
var retVal []User

limit := 100

var offsetQueryParam = ""
if offsetUserId != nil {
offsetQueryParam = "&offset=" + *offsetUserId
}

url := client.baseURL + "/user/list?groupId=" + groupId + "&userType=all&userStatus=all&limit=" + strconv.Itoa(limit) + offsetQueryParam

// Create a context with a timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("GET error creating list request: %w", err)
}
func (client Client) ListUsers(ctx context.Context, groupId string, offsetUserId *string) ([]User, error) {
select {
case <-ctx.Done():
return nil, fmt.Errorf("ListUsers: context cancelled")
default:
var retVal []User

limit := 100

var offsetQueryParam = ""
if offsetUserId != nil {
offsetQueryParam = "&offset=" + *offsetUserId
}

resp, err := client.httpClient.Do(req)
url := client.baseURL + "/user/list?groupId=" + groupId + "&userType=all&userStatus=all&limit=" + strconv.Itoa(limit) + offsetQueryParam

if err != nil {
return nil, fmt.Errorf("GET list users failed: %w", err)
} else {
body, err := io.ReadAll(resp.Body)
defer resp.Body.Close() // nolint:errcheck
req, err := client.newRequest(ctx, http.MethodGet, url, nil)
if err != nil {
return nil, fmt.Errorf("GET reading list users response body failed: %w", err)
return nil, fmt.Errorf("GET error creating list request: %w", err)
}

users, err := unmarshalUsersJson(body)
resp, err := client.httpClient.Do(req)

if err != nil {
return nil, fmt.Errorf("GET unmarshal users response body failed: %w", err)
}
return nil, fmt.Errorf("GET list users failed: %w", err)
} else {
body, err := io.ReadAll(resp.Body)
defer resp.Body.Close() // nolint:errcheck
if err != nil {
return nil, fmt.Errorf("GET reading list users response body failed: %w", err)
}

users, err := unmarshalUsersJson(body)
if err != nil {
return nil, fmt.Errorf("GET unmarshal users response body failed: %w", err)
}

retVal = append(retVal, users...)
retVal = append(retVal, users...)

// list users is a paginated API endpoint, so we need to check the limit and use an offset to fetch more
if len(users) > limit {
// There is some ambiguity in the GET /user/list endpoint documentation, but it seems
// that UserId is the correct key for this parameter (and not CanonicalUserId)
// Fetch more results
moreUsers, err := client.ListUsers(groupId, &users[limit].UserID)
// list users is a paginated API endpoint, so we need to check the limit and use an offset to fetch more
if len(users) > limit {
// There is some ambiguity in the GET /user/list endpoint documentation, but it seems
// that UserId is the correct key for this parameter (and not CanonicalUserId)
// Fetch more results
moreUsers, err := client.ListUsers(ctx, groupId, &users[limit].UserID)

if err == nil {
retVal = append(retVal, moreUsers...)
if err == nil {
retVal = append(retVal, moreUsers...)
}
}
}

return retVal, nil
return retVal, nil
}
}

}

// Delete a single user
func (client Client) DeleteUser(user User) error {
url := client.baseURL + "/user?userId=" + user.UserID + "&groupId=" + user.GroupID + "&canonicalUserId=" + user.CanonicalUserID
func (client Client) DeleteUser(ctx context.Context, user User) error {
select {
case <-ctx.Done():
return fmt.Errorf("DeleteUser: context cancelled")
default:
url := client.baseURL + "/user?userId=" + user.UserID + "&groupId=" + user.GroupID + "&canonicalUserId=" + user.CanonicalUserID

req, err := client.newRequest(ctx, url, http.MethodDelete, nil)
if err != nil {
return err
}

req, err := client.newRequest(url, http.MethodDelete, nil)
if err != nil {
return err
}
defer cancel()
resp, err := client.httpClient.Do(req)

resp, err := client.httpClient.Do(req)
if err != nil {
return err
}
if resp != nil {
defer resp.Body.Close() // nolint:errcheck
}

if err != nil {
return err
}
if resp != nil {
defer resp.Body.Close() // nolint:errcheck
}

return err
}

// Delete a group and all its members
func (client Client) DeleteGroupRecursive(groupId string) error {
users, err := client.ListUsers(groupId, nil)
if err != nil {
return err
}

for _, user := range users {
err := client.DeleteUser(user)
func (client Client) DeleteGroupRecursive(ctx context.Context, groupId string) error {
select {
case <-ctx.Done():
return fmt.Errorf("DeleteGroupRecursive: context cancelled")
default:
users, err := client.ListUsers(ctx, groupId, nil)
if err != nil {
return fmt.Errorf("Error deleting user: %w", err)
return err
}

}
for _, user := range users {
err := client.DeleteUser(ctx, user)
if err != nil {
return fmt.Errorf("Error deleting user: %w", err)
}

return client.DeleteGroup(groupId)
}

return client.DeleteGroup(ctx, groupId)
}
}

// Deletes a group if it is without members
func (client Client) DeleteGroup(groupId string) error {
url := client.baseURL + "/group?groupId=" + groupId
func (client Client) DeleteGroup(ctx context.Context, groupId string) error {
select {
case <-ctx.Done():
return fmt.Errorf("DeleteGroup: context cancelled")
default:
url := client.baseURL + "/group?groupId=" + groupId

req, err := client.newRequest(ctx, url, http.MethodDelete, nil)
if err != nil {
return err
}

req, err := client.newRequest(url, http.MethodDelete, nil)
if err != nil {
return err
}
defer cancel()
resp, err := client.httpClient.Do(req)

resp, err := client.httpClient.Do(req)
if err != nil {
return fmt.Errorf("DELETE to cloudian /group got: %w", err)
}
defer resp.Body.Close() // nolint:errcheck

if err != nil {
return fmt.Errorf("DELETE to cloudian /group got: %w", err)
return nil
}
defer resp.Body.Close() // nolint:errcheck

return nil
}

func (client Client) CreateGroup(group Group) error {
url := client.baseURL + "/group"
func (client Client) CreateGroup(ctx context.Context, group Group) error {
select {
case <-ctx.Done():
return fmt.Errorf("CreateGroup: context cancelled")
default:
url := client.baseURL + "/group"

jsonData, err := marshalGroup(group)
if err != nil {
return fmt.Errorf("Error marshaling JSON: %w", err)
}

req, err := client.newRequest(url, http.MethodPost, &jsonData)
if err != nil {
return err
}
defer cancel()

resp, err := client.httpClient.Do(req)

if err != nil {
return fmt.Errorf("POST to cloudian /group", err)
}
defer resp.Body.Close() // nolint:errcheck
jsonData, err := marshalGroup(group)
if err != nil {
return fmt.Errorf("Error marshaling JSON: %w", err)
}

return err
}
req, err := client.newRequest(ctx, url, http.MethodPost, &jsonData)
if err != nil {
return err
}

func (client Client) UpdateGroup(group Group) error {
url := client.baseURL + "/group"
resp, err := client.httpClient.Do(req)

jsonData, err := marshalGroup(group)
if err != nil {
return fmt.Errorf("Error marshaling JSON: %w", err)
}
if err != nil {
return fmt.Errorf("POST to cloudian /group: %w", err)
}
defer resp.Body.Close() // nolint:errcheck

// Create a context with a timeout
req, err := client.newRequest(url, http.MethodPut, &jsonData)
if err != nil {
return err
}
}

defer cancel()
func (client Client) UpdateGroup(ctx context.Context, group Group) error {
select {
case <-ctx.Done():
return fmt.Errorf("UpdateGroup: context cancelled")
default:
url := client.baseURL + "/group"

resp, err := client.httpClient.Do(req)
jsonData, err := marshalGroup(group)
if err != nil {
return fmt.Errorf("Error marshaling JSON: %w", err)
}

if err != nil {
return fmt.Errorf("PUT to cloudian /group: %w", err)
}
// Create a context with a timeout
req, err := client.newRequest(ctx, url, http.MethodPut, &jsonData)
if err != nil {
return err
}

defer resp.Body.Close() // nolint:errcheck
resp, err := client.httpClient.Do(req)

return nil
}
if err != nil {
return fmt.Errorf("PUT to cloudian /group: %w", err)
}

func (client Client) GetGroup(groupId string) (*Group, error) {
url := client.baseURL + "/group?groupId=" + groupId
defer resp.Body.Close() // nolint:errcheck

req, err := client.newRequest(url, http.MethodGet, nil)
if err != nil {
return nil, err
return nil
}
defer cancel()
}

resp, err := client.httpClient.Do(req)
func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, error) {
select {
case <-ctx.Done():
return nil, fmt.Errorf("GetGroup: context cancelled")
default:
url := client.baseURL + "/group?groupId=" + groupId

if err != nil {
return nil, fmt.Errorf("GET error: %w", err)
}
req, err := client.newRequest(ctx, url, http.MethodGet, nil)
if err != nil {
return nil, err
}

if resp != nil {
defer resp.Body.Close() // nolint:errcheck
}
resp, err := client.httpClient.Do(req)

if resp.StatusCode == 200 {
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("GET reading response body failed: %w", err)
return nil, fmt.Errorf("GET error: %w", err)
}

group, err := unmarshalGroupJson(body)
if err != nil {
return nil, fmt.Errorf("GET unmarshal response body failed: %w", err)
if resp != nil {
defer resp.Body.Close() // nolint:errcheck
}

return &group, nil
}
if resp.StatusCode == 200 {
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("GET reading response body failed: %w", err)
}

// Cloudian-API returns 204 if the group does not exist
return nil, err
group, err := unmarshalGroupJson(body)
if err != nil {
return nil, fmt.Errorf("GET unmarshal response body failed: %w", err)
}

return &group, nil
}

// Cloudian-API returns 204 if the group does not exist
return nil, err
}
}

0 comments on commit 15972b5

Please sign in to comment.