From 47bc77dc62b58bcb8a63339ce61c7118f4429dc4 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Thu, 21 Nov 2024 10:29:15 +0100 Subject: [PATCH 01/52] feat: add cloudian-sdk with CRUD on group --- internal/sdk/cloudian/sdk.go | 303 ++++++++++++++++++++++++++++++ internal/sdk/cloudian/sdk_test.go | 86 +++++++++ 2 files changed, 389 insertions(+) create mode 100644 internal/sdk/cloudian/sdk.go create mode 100644 internal/sdk/cloudian/sdk_test.go diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go new file mode 100644 index 00000000..e3113ddf --- /dev/null +++ b/internal/sdk/cloudian/sdk.go @@ -0,0 +1,303 @@ +package cloudian + +import ( + "bytes" + "encoding/base64" + "encoding/json" + "fmt" + "io" + "net/http" + "strconv" +) + +type Group struct { + Active string + GroupId string + GroupName string + LdapEnabled bool + LdapGroup string + LdapMatchAttribute string + LdapSearch string + LdapSearchUserBase string + LdapServerURL string + LdapUserDNTemplate string + S3endpointshttp []string + S3endpointshttps []string + S3websiteendpoints []string +} + +type User struct { + UserId string + GroupId string + CanonicalUserId string +} + +func marshalGroup(group Group) ([]byte, error) { + return json.Marshal(group) +} + +func unmarshalGroupJson(data []byte) (Group, error) { + var group Group + err := json.Unmarshal(data, &group) + return group, err +} + +func unmarshalUsersJson(data []byte) ([]User, error) { + var users []User + err := json.Unmarshal(data, &users) + return users, err +} + +const baseUrl = "https://s3-admin.statnett.no:19443" + +// checks if a string is properly base64 encoded +func decode(base64Encoded string) ([]byte, error) { + decoded, err := base64.StdEncoding.DecodeString(base64Encoded) + if err != nil { + fmt.Println("The provided token does not decode as base64 - is it correctly encoded? ", err) + return nil, err + } + return decoded, nil +} + +// List all users of a group +func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User, error) { + var retVal []User + + limit := 100 + + var offsetQueryParam string + if offsetUserId == nil { + offsetQueryParam = "" + } else { + offsetQueryParam = "&offset=" + *offsetUserId + } + + url := baseUrl + "/user/list?groupId=" + groupId + "&userType=all&userStatus=all&limit=" + strconv.Itoa(limit) + offsetQueryParam + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + fmt.Println("GET error creating list request: ", err) + return nil, err + } + + client := &http.Client{} + resp, err := client.Do(req) + + if err == nil { + body, err := io.ReadAll(resp.Body) + if err != nil { + fmt.Println("GET reading list users response body failed: ", err) + return nil, err + } + + users, err := unmarshalUsersJson(body) + if err != nil { + fmt.Println("GET unmarshal users response body failed: ", err) + return nil, err + } + + 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 := ListUsers(groupId, &users[limit].UserId, tokenBase64) + + if err == nil { + retVal = append(retVal, moreUsers...) + } + } + + return retVal, nil + } else { + fmt.Println("GET list users failed: ", err) + return nil, err + } + +} + +// Delete a single user +func DeleteUser(user User, tokenBase64 string) (*User, error) { + url := baseUrl + "/user?userId=" + user.UserId + "&groupId=" + user.GroupId + "&canonicalUserId=" + user.CanonicalUserId + + decode(tokenBase64) + + req, err := http.NewRequest("DELETE", url, nil) + if err != nil { + fmt.Println("DELETE error creating request: ", err) + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Basic " + tokenBase64) + + client := &http.Client{} + resp, err := client.Do(req) + + if resp != nil && err != nil { + //Cloudian does not return a payload for this DELETE, but we can echo it to the callsite if all went well + return &user, nil + } + return nil, err +} + +// Delete a group and all its members +func DeleteGroupRecursive(groupId string, tokenBase64 string) (*string, error) { + users, err := ListUsers(groupId, nil, tokenBase64) + + if err != nil { + for _, user := range users { + _, err := DeleteUser(user, tokenBase64) + if err != nil { + fmt.Println("Error deleting user: ", err) + return nil, err + } + } + + retVal, err := DeleteGroup(groupId, tokenBase64) + + return retVal, err + } + + return nil, err +} + +// Deletes a group if it is without members +func DeleteGroup(groupId string, tokenBase64 string) (*string, error) { + url := baseUrl + "/group?groupId=" + groupId + + decode(tokenBase64) + + req, err := http.NewRequest("DELETE", url, nil) + if err != nil { + fmt.Println("DELETE error creating request: ", err) + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Basic "+tokenBase64) + + client := &http.Client{} + resp, err := client.Do(req) + + statusErrStr := strconv.Itoa(resp.StatusCode) + if err != nil { + fmt.Println("DELETE to cloudian /group got status code ["+statusErrStr+"]", err) + return nil, err + } + + //Cloudian does not return a payload for this DELETE, but we can echo it to the callsite if all went well + return &groupId, nil +} + +func CreateGroup(group Group, tokenBase64 string) (*Group, error) { + url := baseUrl + "/group" + + decode(tokenBase64) + + jsonData, err := marshalGroup(group) + if err != nil { + fmt.Println("Error marshaling JSON:", err) + return nil, err + } + + req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) + if err != nil { + fmt.Println("POST error creating request: ", err) + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Basic "+tokenBase64) + + client := &http.Client{} + resp, err := client.Do(req) + + statusErrStr := strconv.FormatInt(int64(resp.StatusCode), 10) + if err != nil { + fmt.Println("POST to cloudian /group got status code ["+statusErrStr+"]", err) + return nil, err + } + + //Cloudian does not return a payload for this POST, but we can echo it to the callsite if all went well + return &group, nil +} + +func UpdateGroup(group Group, tokenBase64 string) (*Group, error) { + url := baseUrl + "/group" + + decode(tokenBase64) + + jsonData, err := marshalGroup(group) + if err != nil { + fmt.Println("Error marshaling JSON:", err) + return nil, err + } + + req, err := http.NewRequest("PUT", url, bytes.NewBuffer(jsonData)) + if err != nil { + fmt.Println("POST error creating request: ", err) + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Basic "+tokenBase64) + + client := &http.Client{} + resp, err := client.Do(req) + + statusErrStr := strconv.FormatInt(int64(resp.StatusCode), 10) + if err != nil { + fmt.Println("PUT to cloudian /group got status code ["+statusErrStr+"]", err) + } + + //Cloudian does not return a payload for this PUT, but we can echo it to the callsite if all went well + return &group, nil +} + +func GetGroup(groupId string, tokenBase64 string) (*Group, error) { + url := baseUrl + "/group?groupId=" + groupId + + decode(tokenBase64) + + req, err := http.NewRequest("GET", url, nil) + if err != nil { + fmt.Println("GET error creating request: ", err) + return nil, err + } + + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Basic "+tokenBase64) + + client := &http.Client{} + resp, err := client.Do(req) + + if err != nil { + fmt.Println("GET errored towards Cloudian /group: ", err) + return nil, err + } + + defer resp.Body.Close() + + if resp.StatusCode == 200 { + body, err := io.ReadAll(resp.Body) + if err != nil { + fmt.Println("GET reading response body failed: ", err) + return nil, err + } + + group, err := unmarshalGroupJson(body) + if err != nil { + fmt.Println("GET unmarshal response body failed: ", err) + return nil, err + } + + return &group, nil + } + + // Cloudian-API returns 204 if the group does not exist + return nil, nil +} diff --git a/internal/sdk/cloudian/sdk_test.go b/internal/sdk/cloudian/sdk_test.go new file mode 100644 index 00000000..4c7f02ed --- /dev/null +++ b/internal/sdk/cloudian/sdk_test.go @@ -0,0 +1,86 @@ +package cloudian + +import ( + "testing" +) + +func TestRealisticGroupSerialization(t *testing.T) { + jsonString := `{ + "active": "true", + "groupId": "QA", + "groupName": "Quality Assurance Group", + "ldapEnabled": false, + "ldapGroup": "", + "ldapMatchAttribute": "", + "ldapSearch": "", + "ldapSearchUserBase": "", + "ldapServerURL": "", + "ldapUserDNTemplate": "", + "s3endpointshttp": ["ALL"], + "s3endpointshttps": ["ALL"], + "s3websiteendpoints": ["ALL"] + }` + + group, err := unmarshalGroupJson([]byte(jsonString)) + if err != nil { + t.Errorf("Error deserializing from JSON: %v", err) + } + + if group.GroupId != "QA" { + t.Errorf("Expected QA, got %v", group.GroupId) + } +} + +func TestUnmarshalUsers(t *testing.T) { + jsonString := `[ + { + "active": "true", + "address1": "", + "address2": "", + "canonicalUserId": "fd221552ff4ddc857d7a9ca316bb8344", + "city": "", + "country": "", + "emailAddr": "", + "fullName": "Glory Bee", + "groupId": "QA", + "ldapEnabled": false, + "phone": "", + "state": "", + "userId": "Glory", + "userType": "User", + "website": "", + "zip": "" + }, + { + "active": "true", + "address1": "", + "address2": "", + "canonicalUserId": "bd0796cd9746ef9cc4ef656ddaacfac4", + "city": "", + "country": "", + "emailAddr": "", + "fullName": "John Thompson", + "groupId": "QA", + "ldapEnabled": false, + "phone": "", + "state": "", + "userId": "John", + "userType": "User", + "website": "", + "zip": "" + }]` + + users, err := unmarshalUsersJson([]byte(jsonString)) + if err != nil { + t.Errorf("Error deserializing users from JSON: %v", err) + } + + if users[0].UserId != "Glory" { + t.Errorf("Expected Glory as the userId of first user, got %v", users[0].UserId) + } + + if users[1].UserId != "John" { + t.Errorf("Expected John as the userId of second user, got %v", users[1].UserId) + } + +} From 57cf3f65af681c06e021efcf5a176fb73813a1f5 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Thu, 21 Nov 2024 13:04:24 +0100 Subject: [PATCH 02/52] refactor: reuse http.Client in sdk.go --- internal/sdk/cloudian/sdk.go | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index e3113ddf..95d589b4 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -60,6 +60,8 @@ func decode(base64Encoded string) ([]byte, error) { return decoded, nil } +var client = &http.Client{} + // List all users of a group func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User, error) { var retVal []User @@ -81,7 +83,6 @@ func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User return nil, err } - client := &http.Client{} resp, err := client.Do(req) if err == nil { @@ -134,7 +135,6 @@ func DeleteUser(user User, tokenBase64 string) (*User, error) { req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Basic " + tokenBase64) - client := &http.Client{} resp, err := client.Do(req) if resp != nil && err != nil { @@ -180,7 +180,6 @@ func DeleteGroup(groupId string, tokenBase64 string) (*string, error) { req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Basic "+tokenBase64) - client := &http.Client{} resp, err := client.Do(req) statusErrStr := strconv.Itoa(resp.StatusCode) @@ -213,7 +212,6 @@ func CreateGroup(group Group, tokenBase64 string) (*Group, error) { req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Basic "+tokenBase64) - client := &http.Client{} resp, err := client.Do(req) statusErrStr := strconv.FormatInt(int64(resp.StatusCode), 10) @@ -246,7 +244,6 @@ func UpdateGroup(group Group, tokenBase64 string) (*Group, error) { req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Basic "+tokenBase64) - client := &http.Client{} resp, err := client.Do(req) statusErrStr := strconv.FormatInt(int64(resp.StatusCode), 10) @@ -272,7 +269,6 @@ func GetGroup(groupId string, tokenBase64 string) (*Group, error) { req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Basic "+tokenBase64) - client := &http.Client{} resp, err := client.Do(req) if err != nil { From 6c876a67694a95de06bd5a072e5ee2e108023049 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Thu, 21 Nov 2024 13:17:16 +0100 Subject: [PATCH 03/52] format: gofmt -s sdk.go --- internal/sdk/cloudian/sdk.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 95d589b4..d630dbeb 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -133,7 +133,7 @@ func DeleteUser(user User, tokenBase64 string) (*User, error) { } req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Basic " + tokenBase64) + req.Header.Set("Authorization", "Basic "+tokenBase64) resp, err := client.Do(req) From f4632df6a793229cc4edb263d338dc2e84e95006 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Thu, 21 Nov 2024 13:21:26 +0100 Subject: [PATCH 04/52] lint: remove decode-method, trust user --- internal/sdk/cloudian/sdk.go | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index d630dbeb..adca0ef9 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -2,7 +2,6 @@ package cloudian import ( "bytes" - "encoding/base64" "encoding/json" "fmt" "io" @@ -50,16 +49,6 @@ func unmarshalUsersJson(data []byte) ([]User, error) { const baseUrl = "https://s3-admin.statnett.no:19443" -// checks if a string is properly base64 encoded -func decode(base64Encoded string) ([]byte, error) { - decoded, err := base64.StdEncoding.DecodeString(base64Encoded) - if err != nil { - fmt.Println("The provided token does not decode as base64 - is it correctly encoded? ", err) - return nil, err - } - return decoded, nil -} - var client = &http.Client{} // List all users of a group @@ -124,8 +113,6 @@ func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User func DeleteUser(user User, tokenBase64 string) (*User, error) { url := baseUrl + "/user?userId=" + user.UserId + "&groupId=" + user.GroupId + "&canonicalUserId=" + user.CanonicalUserId - decode(tokenBase64) - req, err := http.NewRequest("DELETE", url, nil) if err != nil { fmt.Println("DELETE error creating request: ", err) @@ -169,8 +156,6 @@ func DeleteGroupRecursive(groupId string, tokenBase64 string) (*string, error) { func DeleteGroup(groupId string, tokenBase64 string) (*string, error) { url := baseUrl + "/group?groupId=" + groupId - decode(tokenBase64) - req, err := http.NewRequest("DELETE", url, nil) if err != nil { fmt.Println("DELETE error creating request: ", err) @@ -195,8 +180,6 @@ func DeleteGroup(groupId string, tokenBase64 string) (*string, error) { func CreateGroup(group Group, tokenBase64 string) (*Group, error) { url := baseUrl + "/group" - decode(tokenBase64) - jsonData, err := marshalGroup(group) if err != nil { fmt.Println("Error marshaling JSON:", err) @@ -227,8 +210,6 @@ func CreateGroup(group Group, tokenBase64 string) (*Group, error) { func UpdateGroup(group Group, tokenBase64 string) (*Group, error) { url := baseUrl + "/group" - decode(tokenBase64) - jsonData, err := marshalGroup(group) if err != nil { fmt.Println("Error marshaling JSON:", err) @@ -258,8 +239,6 @@ func UpdateGroup(group Group, tokenBase64 string) (*Group, error) { func GetGroup(groupId string, tokenBase64 string) (*Group, error) { url := baseUrl + "/group?groupId=" + groupId - decode(tokenBase64) - req, err := http.NewRequest("GET", url, nil) if err != nil { fmt.Println("GET error creating request: ", err) From f815199baab06bbd03d965e7f52f29cc0c64ab5f Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Thu, 21 Nov 2024 13:23:29 +0100 Subject: [PATCH 05/52] lint: cloudian sdk inline status code check --- internal/sdk/cloudian/sdk.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index adca0ef9..03e78371 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -197,8 +197,8 @@ func CreateGroup(group Group, tokenBase64 string) (*Group, error) { resp, err := client.Do(req) - statusErrStr := strconv.FormatInt(int64(resp.StatusCode), 10) if err != nil { + statusErrStr := strconv.FormatInt(int64(resp.StatusCode), 10) fmt.Println("POST to cloudian /group got status code ["+statusErrStr+"]", err) return nil, err } @@ -227,8 +227,8 @@ func UpdateGroup(group Group, tokenBase64 string) (*Group, error) { resp, err := client.Do(req) - statusErrStr := strconv.FormatInt(int64(resp.StatusCode), 10) if err != nil { + statusErrStr := strconv.FormatInt(int64(resp.StatusCode), 10) fmt.Println("PUT to cloudian /group got status code ["+statusErrStr+"]", err) } From 05284afc018b03472e88c9170ae4185e3867666f Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Thu, 21 Nov 2024 13:24:51 +0100 Subject: [PATCH 06/52] lint: cloudian sdk insert space after comment slashes --- internal/sdk/cloudian/sdk.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 03e78371..d8d70809 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -125,7 +125,7 @@ func DeleteUser(user User, tokenBase64 string) (*User, error) { resp, err := client.Do(req) if resp != nil && err != nil { - //Cloudian does not return a payload for this DELETE, but we can echo it to the callsite if all went well + // Cloudian does not return a payload for this DELETE, but we can echo it to the callsite if all went well return &user, nil } return nil, err @@ -173,7 +173,7 @@ func DeleteGroup(groupId string, tokenBase64 string) (*string, error) { return nil, err } - //Cloudian does not return a payload for this DELETE, but we can echo it to the callsite if all went well + // Cloudian does not return a payload for this DELETE, but we can echo it to the callsite if all went well return &groupId, nil } @@ -203,7 +203,7 @@ func CreateGroup(group Group, tokenBase64 string) (*Group, error) { return nil, err } - //Cloudian does not return a payload for this POST, but we can echo it to the callsite if all went well + // Cloudian does not return a payload for this POST, but we can echo it to the callsite if all went well return &group, nil } @@ -232,7 +232,7 @@ func UpdateGroup(group Group, tokenBase64 string) (*Group, error) { fmt.Println("PUT to cloudian /group got status code ["+statusErrStr+"]", err) } - //Cloudian does not return a payload for this PUT, but we can echo it to the callsite if all went well + // Cloudian does not return a payload for this PUT, but we can echo it to the callsite if all went well return &group, nil } From c80b2dc88d88121c35ab15e750f954fbb5fd8f47 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Thu, 21 Nov 2024 13:29:49 +0100 Subject: [PATCH 07/52] lint: cloudian sdk close all response bodys --- internal/sdk/cloudian/sdk.go | 40 +++++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index d8d70809..432445b9 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -87,6 +87,12 @@ func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User return nil, err } + err = resp.Body.Close() + if err != nil { + fmt.Println("Closing response body failed: ", err) + return nil, err + } + 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 @@ -126,6 +132,12 @@ func DeleteUser(user User, tokenBase64 string) (*User, error) { if resp != nil && err != nil { // Cloudian does not return a payload for this DELETE, but we can echo it to the callsite if all went well + err = resp.Body.Close() + if err != nil { + fmt.Println("Closing response body failed: ", err) + return nil, err + } + return &user, nil } return nil, err @@ -167,12 +179,18 @@ func DeleteGroup(groupId string, tokenBase64 string) (*string, error) { resp, err := client.Do(req) - statusErrStr := strconv.Itoa(resp.StatusCode) if err != nil { + statusErrStr := strconv.Itoa(resp.StatusCode) fmt.Println("DELETE to cloudian /group got status code ["+statusErrStr+"]", err) return nil, err } + err = resp.Body.Close() + if err != nil { + fmt.Println("Closing response body failed: ", err) + return nil, err + } + // Cloudian does not return a payload for this DELETE, but we can echo it to the callsite if all went well return &groupId, nil } @@ -203,6 +221,12 @@ func CreateGroup(group Group, tokenBase64 string) (*Group, error) { return nil, err } + err = resp.Body.Close() + if err != nil { + fmt.Println("Closing response body failed: ", err) + return nil, err + } + // Cloudian does not return a payload for this POST, but we can echo it to the callsite if all went well return &group, nil } @@ -232,6 +256,12 @@ func UpdateGroup(group Group, tokenBase64 string) (*Group, error) { fmt.Println("PUT to cloudian /group got status code ["+statusErrStr+"]", err) } + err = resp.Body.Close() + if err != nil { + fmt.Println("Closing response body failed: ", err) + return nil, err + } + // Cloudian does not return a payload for this PUT, but we can echo it to the callsite if all went well return &group, nil } @@ -255,8 +285,6 @@ func GetGroup(groupId string, tokenBase64 string) (*Group, error) { return nil, err } - defer resp.Body.Close() - if resp.StatusCode == 200 { body, err := io.ReadAll(resp.Body) if err != nil { @@ -273,6 +301,12 @@ func GetGroup(groupId string, tokenBase64 string) (*Group, error) { return &group, nil } + err = resp.Body.Close() + + if err != nil { + fmt.Println("Closing response body failed: ", err) + return nil, err + } // Cloudian-API returns 204 if the group does not exist return nil, nil } From 774bd095897532d711fe84f06f6e36636c9fce13 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Thu, 21 Nov 2024 13:32:22 +0100 Subject: [PATCH 08/52] lint: cloudian sdk annotate json structs --- internal/sdk/cloudian/sdk.go | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 432445b9..5a3f6b13 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -10,25 +10,25 @@ import ( ) type Group struct { - Active string - GroupId string - GroupName string - LdapEnabled bool - LdapGroup string - LdapMatchAttribute string - LdapSearch string - LdapSearchUserBase string - LdapServerURL string - LdapUserDNTemplate string - S3endpointshttp []string - S3endpointshttps []string - S3websiteendpoints []string + Active string `json:"active"` + GroupId string `json:"groupId"` + GroupName string `json:"groupName"` + LdapEnabled bool `json:"ldapEnabled"` + LdapGroup string `json:"ldapGroup"` + LdapMatchAttribute string `json:"ldapMatchAttribute"` + LdapSearch string `json:"ldapSearch"` + LdapSearchUserBase string `json:"ldapSearchUserBase"` + LdapServerURL string `json:"ldapServerURL"` + LdapUserDNTemplate string `json:"ldapUserDNTemplate"` + S3endpointshttp []string `json:"s3endpointshttp"` + S3endpointshttps []string `json:"s3endpointshttps"` + S3websiteendpoints []string `json:"s3websiteendpoints"` } type User struct { - UserId string - GroupId string - CanonicalUserId string + UserId string `json:"userId"` + GroupId string `json:"groupId"` + CanonicalUserId string `json:"canonicalUserId"` } func marshalGroup(group Group) ([]byte, error) { From ee23dca90869851b8dab9864c55226802802672a Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Thu, 21 Nov 2024 13:36:00 +0100 Subject: [PATCH 09/52] refactor: cloudian sdk refer to http package verbs --- internal/sdk/cloudian/sdk.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 5a3f6b13..f5f1c7b5 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -66,7 +66,7 @@ func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User url := baseUrl + "/user/list?groupId=" + groupId + "&userType=all&userStatus=all&limit=" + strconv.Itoa(limit) + offsetQueryParam - req, err := http.NewRequest("GET", url, nil) + req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { fmt.Println("GET error creating list request: ", err) return nil, err @@ -119,7 +119,7 @@ func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User func DeleteUser(user User, tokenBase64 string) (*User, error) { url := baseUrl + "/user?userId=" + user.UserId + "&groupId=" + user.GroupId + "&canonicalUserId=" + user.CanonicalUserId - req, err := http.NewRequest("DELETE", url, nil) + req, err := http.NewRequest(http.MethodDelete, url, nil) if err != nil { fmt.Println("DELETE error creating request: ", err) return nil, err @@ -168,7 +168,7 @@ func DeleteGroupRecursive(groupId string, tokenBase64 string) (*string, error) { func DeleteGroup(groupId string, tokenBase64 string) (*string, error) { url := baseUrl + "/group?groupId=" + groupId - req, err := http.NewRequest("DELETE", url, nil) + req, err := http.NewRequest(http.MethodDelete, url, nil) if err != nil { fmt.Println("DELETE error creating request: ", err) return nil, err @@ -204,7 +204,7 @@ func CreateGroup(group Group, tokenBase64 string) (*Group, error) { return nil, err } - req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData)) + req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(jsonData)) if err != nil { fmt.Println("POST error creating request: ", err) return nil, err @@ -240,7 +240,7 @@ func UpdateGroup(group Group, tokenBase64 string) (*Group, error) { return nil, err } - req, err := http.NewRequest("PUT", url, bytes.NewBuffer(jsonData)) + req, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer(jsonData)) if err != nil { fmt.Println("POST error creating request: ", err) return nil, err @@ -269,7 +269,7 @@ func UpdateGroup(group Group, tokenBase64 string) (*Group, error) { func GetGroup(groupId string, tokenBase64 string) (*Group, error) { url := baseUrl + "/group?groupId=" + groupId - req, err := http.NewRequest("GET", url, nil) + req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { fmt.Println("GET error creating request: ", err) return nil, err From e130ccb2141ca10ae482c6b7d67c621d3e925a8c Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Thu, 21 Nov 2024 13:41:16 +0100 Subject: [PATCH 10/52] lint: cloudian sdk add explicit http context --- internal/sdk/cloudian/sdk.go | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index f5f1c7b5..5a15d26a 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -2,11 +2,13 @@ package cloudian import ( "bytes" + "context" "encoding/json" "fmt" "io" "net/http" "strconv" + "time" ) type Group struct { @@ -66,7 +68,10 @@ func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User url := baseUrl + "/user/list?groupId=" + groupId + "&userType=all&userStatus=all&limit=" + strconv.Itoa(limit) + offsetQueryParam - req, err := http.NewRequest(http.MethodGet, url, nil) + // 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 { fmt.Println("GET error creating list request: ", err) return nil, err @@ -119,7 +124,10 @@ func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User func DeleteUser(user User, tokenBase64 string) (*User, error) { url := baseUrl + "/user?userId=" + user.UserId + "&groupId=" + user.GroupId + "&canonicalUserId=" + user.CanonicalUserId - req, err := http.NewRequest(http.MethodDelete, url, nil) + // Create a context with a timeout + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil) if err != nil { fmt.Println("DELETE error creating request: ", err) return nil, err @@ -168,7 +176,10 @@ func DeleteGroupRecursive(groupId string, tokenBase64 string) (*string, error) { func DeleteGroup(groupId string, tokenBase64 string) (*string, error) { url := baseUrl + "/group?groupId=" + groupId - req, err := http.NewRequest(http.MethodDelete, url, nil) + // Create a context with a timeout + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil) if err != nil { fmt.Println("DELETE error creating request: ", err) return nil, err @@ -204,7 +215,10 @@ func CreateGroup(group Group, tokenBase64 string) (*Group, error) { return nil, err } - req, err := http.NewRequest(http.MethodPost, url, bytes.NewBuffer(jsonData)) + // Create a context with a timeout + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(jsonData)) if err != nil { fmt.Println("POST error creating request: ", err) return nil, err @@ -240,7 +254,10 @@ func UpdateGroup(group Group, tokenBase64 string) (*Group, error) { return nil, err } - req, err := http.NewRequest(http.MethodPut, url, bytes.NewBuffer(jsonData)) + // Create a context with a timeout + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + req, err := http.NewRequestWithContext(ctx, http.MethodPut, url, bytes.NewBuffer(jsonData)) if err != nil { fmt.Println("POST error creating request: ", err) return nil, err @@ -269,7 +286,10 @@ func UpdateGroup(group Group, tokenBase64 string) (*Group, error) { func GetGroup(groupId string, tokenBase64 string) (*Group, error) { url := baseUrl + "/group?groupId=" + groupId - req, err := http.NewRequest(http.MethodGet, url, nil) + // 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 { fmt.Println("GET error creating request: ", err) return nil, err From ea5cd14726832e3c20948b391c622e49d235b86a Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Fri, 22 Nov 2024 08:49:53 +0100 Subject: [PATCH 11/52] refactor: adhere golang convension of capitalized abbreviations --- internal/sdk/cloudian/sdk.go | 26 +++++++++++++------------- internal/sdk/cloudian/sdk_test.go | 12 ++++++------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 5a15d26a..581e7734 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -13,24 +13,24 @@ import ( type Group struct { Active string `json:"active"` - GroupId string `json:"groupId"` + GroupID string `json:"groupId"` GroupName string `json:"groupName"` - LdapEnabled bool `json:"ldapEnabled"` - LdapGroup string `json:"ldapGroup"` - LdapMatchAttribute string `json:"ldapMatchAttribute"` - LdapSearch string `json:"ldapSearch"` - LdapSearchUserBase string `json:"ldapSearchUserBase"` - LdapServerURL string `json:"ldapServerURL"` - LdapUserDNTemplate string `json:"ldapUserDNTemplate"` + LDAPEnabled bool `json:"ldapEnabled"` + LDAPGroup string `json:"ldapGroup"` + LDAPMatchAttribute string `json:"ldapMatchAttribute"` + LDAPSearch string `json:"ldapSearch"` + LDAPSearchUserBase string `json:"ldapSearchUserBase"` + LDAPServerURL string `json:"ldapServerURL"` + LDAPUserDNTemplate string `json:"ldapUserDNTemplate"` S3endpointshttp []string `json:"s3endpointshttp"` S3endpointshttps []string `json:"s3endpointshttps"` S3websiteendpoints []string `json:"s3websiteendpoints"` } type User struct { - UserId string `json:"userId"` - GroupId string `json:"groupId"` - CanonicalUserId string `json:"canonicalUserId"` + UserID string `json:"userId"` + GroupID string `json:"groupId"` + CanonicalUserID string `json:"canonicalUserId"` } func marshalGroup(group Group) ([]byte, error) { @@ -105,7 +105,7 @@ func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User // 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 := ListUsers(groupId, &users[limit].UserId, tokenBase64) + moreUsers, err := ListUsers(groupId, &users[limit].UserID, tokenBase64) if err == nil { retVal = append(retVal, moreUsers...) @@ -122,7 +122,7 @@ func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User // Delete a single user func DeleteUser(user User, tokenBase64 string) (*User, error) { - url := baseUrl + "/user?userId=" + user.UserId + "&groupId=" + user.GroupId + "&canonicalUserId=" + user.CanonicalUserId + url := baseUrl + "/user?userId=" + user.UserID + "&groupId=" + user.GroupID + "&canonicalUserId=" + user.CanonicalUserID // Create a context with a timeout ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) diff --git a/internal/sdk/cloudian/sdk_test.go b/internal/sdk/cloudian/sdk_test.go index 4c7f02ed..e24c1a80 100644 --- a/internal/sdk/cloudian/sdk_test.go +++ b/internal/sdk/cloudian/sdk_test.go @@ -26,8 +26,8 @@ func TestRealisticGroupSerialization(t *testing.T) { t.Errorf("Error deserializing from JSON: %v", err) } - if group.GroupId != "QA" { - t.Errorf("Expected QA, got %v", group.GroupId) + if group.GroupID != "QA" { + t.Errorf("Expected QA, got %v", group.GroupID) } } @@ -75,12 +75,12 @@ func TestUnmarshalUsers(t *testing.T) { t.Errorf("Error deserializing users from JSON: %v", err) } - if users[0].UserId != "Glory" { - t.Errorf("Expected Glory as the userId of first user, got %v", users[0].UserId) + if users[0].UserID != "Glory" { + t.Errorf("Expected Glory as the userId of first user, got %v", users[0].UserID) } - if users[1].UserId != "John" { - t.Errorf("Expected John as the userId of second user, got %v", users[1].UserId) + if users[1].UserID != "John" { + t.Errorf("Expected John as the userId of second user, got %v", users[1].UserID) } } From 982451d08176d8227a0bbb37e0c868ed3b0abbcc Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Fri, 22 Nov 2024 08:57:02 +0100 Subject: [PATCH 12/52] fix: add cloudian base url required env variable --- internal/sdk/cloudian/sdk.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 581e7734..fc13141a 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -7,6 +7,7 @@ import ( "fmt" "io" "net/http" + "os" "strconv" "time" ) @@ -49,7 +50,7 @@ func unmarshalUsersJson(data []byte) ([]User, error) { return users, err } -const baseUrl = "https://s3-admin.statnett.no:19443" +var baseUrl, _ = os.LookupEnv("CLOUDIAN_BASE_URL") var client = &http.Client{} From 171e4f6f6b171edbfe7bf12d9722a21cf8b76626 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Fri, 22 Nov 2024 09:26:46 +0100 Subject: [PATCH 13/52] refactor: sdk cloudian, shorten offset var assignment --- internal/sdk/cloudian/sdk.go | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index fc13141a..8b0c426f 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -60,10 +60,8 @@ func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User limit := 100 - var offsetQueryParam string - if offsetUserId == nil { - offsetQueryParam = "" - } else { + var offsetQueryParam = "" + if offsetUserId != nil { offsetQueryParam = "&offset=" + *offsetUserId } From fbfdbf52fcec491f6113056538de58c22075d8b4 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Fri, 22 Nov 2024 09:55:33 +0100 Subject: [PATCH 14/52] refactor: cloudian sdk defer body close and ignore any failure doing so --- internal/sdk/cloudian/sdk.go | 41 ++++++------------------------------ 1 file changed, 7 insertions(+), 34 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 8b0c426f..069d07b0 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -80,6 +80,7 @@ func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User if err == nil { body, err := io.ReadAll(resp.Body) + defer resp.Body.Close() if err != nil { fmt.Println("GET reading list users response body failed: ", err) return nil, err @@ -91,12 +92,6 @@ func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User return nil, err } - err = resp.Body.Close() - if err != nil { - fmt.Println("Closing response body failed: ", err) - return nil, err - } - 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 @@ -139,11 +134,7 @@ func DeleteUser(user User, tokenBase64 string) (*User, error) { if resp != nil && err != nil { // Cloudian does not return a payload for this DELETE, but we can echo it to the callsite if all went well - err = resp.Body.Close() - if err != nil { - fmt.Println("Closing response body failed: ", err) - return nil, err - } + defer resp.Body.Close() return &user, nil } @@ -194,12 +185,7 @@ func DeleteGroup(groupId string, tokenBase64 string) (*string, error) { fmt.Println("DELETE to cloudian /group got status code ["+statusErrStr+"]", err) return nil, err } - - err = resp.Body.Close() - if err != nil { - fmt.Println("Closing response body failed: ", err) - return nil, err - } + defer resp.Body.Close() // Cloudian does not return a payload for this DELETE, but we can echo it to the callsite if all went well return &groupId, nil @@ -233,12 +219,7 @@ func CreateGroup(group Group, tokenBase64 string) (*Group, error) { fmt.Println("POST to cloudian /group got status code ["+statusErrStr+"]", err) return nil, err } - - err = resp.Body.Close() - if err != nil { - fmt.Println("Closing response body failed: ", err) - return nil, err - } + defer resp.Body.Close() // Cloudian does not return a payload for this POST, but we can echo it to the callsite if all went well return &group, nil @@ -272,11 +253,7 @@ func UpdateGroup(group Group, tokenBase64 string) (*Group, error) { fmt.Println("PUT to cloudian /group got status code ["+statusErrStr+"]", err) } - err = resp.Body.Close() - if err != nil { - fmt.Println("Closing response body failed: ", err) - return nil, err - } + defer resp.Body.Close() // Cloudian does not return a payload for this PUT, but we can echo it to the callsite if all went well return &group, nil @@ -304,6 +281,8 @@ func GetGroup(groupId string, tokenBase64 string) (*Group, error) { return nil, err } + defer resp.Body.Close() + if resp.StatusCode == 200 { body, err := io.ReadAll(resp.Body) if err != nil { @@ -320,12 +299,6 @@ func GetGroup(groupId string, tokenBase64 string) (*Group, error) { return &group, nil } - err = resp.Body.Close() - - if err != nil { - fmt.Println("Closing response body failed: ", err) - return nil, err - } // Cloudian-API returns 204 if the group does not exist return nil, nil } From 36bc3ba8114b4adee26487fc07157b6bc7512711 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Fri, 22 Nov 2024 09:59:29 +0100 Subject: [PATCH 15/52] fix: provider-cloudian fix delete user logic failure --- internal/sdk/cloudian/sdk.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 069d07b0..13cf4b06 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -132,7 +132,7 @@ func DeleteUser(user User, tokenBase64 string) (*User, error) { resp, err := client.Do(req) - if resp != nil && err != nil { + if resp != nil && err == nil { // Cloudian does not return a payload for this DELETE, but we can echo it to the callsite if all went well defer resp.Body.Close() From 3a36ae8688ec522e96053bdd6c1b9ea34b9b0ecc Mon Sep 17 00:00:00 2001 From: Sjur Date: Fri, 22 Nov 2024 10:09:08 +0100 Subject: [PATCH 16/52] Update internal/sdk/cloudian/sdk.go refactor: shorten expression Co-authored-by: Amund Tenstad --- internal/sdk/cloudian/sdk.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 13cf4b06..c7a8bf01 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -154,9 +154,7 @@ func DeleteGroupRecursive(groupId string, tokenBase64 string) (*string, error) { } } - retVal, err := DeleteGroup(groupId, tokenBase64) - - return retVal, err + return DeleteGroup(groupId, tokenBase64) } return nil, err From 9ab98f61e6fb838135ea1345a9065103aaaca02d Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Fri, 22 Nov 2024 10:20:51 +0100 Subject: [PATCH 17/52] cloudian sdk ignore error check of body close --- internal/sdk/cloudian/sdk.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index c7a8bf01..a7690e1e 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -80,7 +80,7 @@ func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User if err == nil { body, err := io.ReadAll(resp.Body) - defer resp.Body.Close() + defer resp.Body.Close() // nolint:errcheck if err != nil { fmt.Println("GET reading list users response body failed: ", err) return nil, err @@ -134,7 +134,7 @@ func DeleteUser(user User, tokenBase64 string) (*User, error) { if resp != nil && err == nil { // Cloudian does not return a payload for this DELETE, but we can echo it to the callsite if all went well - defer resp.Body.Close() + defer resp.Body.Close() // nolint:errcheck return &user, nil } @@ -183,7 +183,7 @@ func DeleteGroup(groupId string, tokenBase64 string) (*string, error) { fmt.Println("DELETE to cloudian /group got status code ["+statusErrStr+"]", err) return nil, err } - defer resp.Body.Close() + defer resp.Body.Close() // nolint:errcheck // Cloudian does not return a payload for this DELETE, but we can echo it to the callsite if all went well return &groupId, nil @@ -217,7 +217,7 @@ func CreateGroup(group Group, tokenBase64 string) (*Group, error) { fmt.Println("POST to cloudian /group got status code ["+statusErrStr+"]", err) return nil, err } - defer resp.Body.Close() + defer resp.Body.Close() // nolint:errcheck // Cloudian does not return a payload for this POST, but we can echo it to the callsite if all went well return &group, nil @@ -251,7 +251,7 @@ func UpdateGroup(group Group, tokenBase64 string) (*Group, error) { fmt.Println("PUT to cloudian /group got status code ["+statusErrStr+"]", err) } - defer resp.Body.Close() + defer resp.Body.Close() // nolint:errcheck // Cloudian does not return a payload for this PUT, but we can echo it to the callsite if all went well return &group, nil @@ -279,7 +279,7 @@ func GetGroup(groupId string, tokenBase64 string) (*Group, error) { return nil, err } - defer resp.Body.Close() + defer resp.Body.Close() // nolint:errcheck if resp.StatusCode == 200 { body, err := io.ReadAll(resp.Body) From 074b7f8fcb27888b3282ce0db8049b208911fc6a Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Fri, 22 Nov 2024 10:27:55 +0100 Subject: [PATCH 18/52] refactor: cloudian sdk strip away return types for non-fetching operations --- internal/sdk/cloudian/sdk.go | 45 +++++++++++++++++------------------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index a7690e1e..0682d0ee 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -115,7 +115,7 @@ func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User } // Delete a single user -func DeleteUser(user User, tokenBase64 string) (*User, error) { +func DeleteUser(user User, tokenBase64 string) error { url := baseUrl + "/user?userId=" + user.UserID + "&groupId=" + user.GroupID + "&canonicalUserId=" + user.CanonicalUserID // Create a context with a timeout @@ -124,7 +124,7 @@ func DeleteUser(user User, tokenBase64 string) (*User, error) { req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil) if err != nil { fmt.Println("DELETE error creating request: ", err) - return nil, err + return err } req.Header.Set("Content-Type", "application/json") @@ -136,32 +136,32 @@ func DeleteUser(user User, tokenBase64 string) (*User, error) { // Cloudian does not return a payload for this DELETE, but we can echo it to the callsite if all went well defer resp.Body.Close() // nolint:errcheck - return &user, nil + return nil } - return nil, err + return err } // Delete a group and all its members -func DeleteGroupRecursive(groupId string, tokenBase64 string) (*string, error) { +func DeleteGroupRecursive(groupId string, tokenBase64 string) error { users, err := ListUsers(groupId, nil, tokenBase64) if err != nil { for _, user := range users { - _, err := DeleteUser(user, tokenBase64) + err := DeleteUser(user, tokenBase64) if err != nil { fmt.Println("Error deleting user: ", err) - return nil, err + return err } } return DeleteGroup(groupId, tokenBase64) } - return nil, err + return err } // Deletes a group if it is without members -func DeleteGroup(groupId string, tokenBase64 string) (*string, error) { +func DeleteGroup(groupId string, tokenBase64 string) error { url := baseUrl + "/group?groupId=" + groupId // Create a context with a timeout @@ -170,7 +170,7 @@ func DeleteGroup(groupId string, tokenBase64 string) (*string, error) { req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil) if err != nil { fmt.Println("DELETE error creating request: ", err) - return nil, err + return err } req.Header.Set("Content-Type", "application/json") @@ -181,21 +181,20 @@ func DeleteGroup(groupId string, tokenBase64 string) (*string, error) { if err != nil { statusErrStr := strconv.Itoa(resp.StatusCode) fmt.Println("DELETE to cloudian /group got status code ["+statusErrStr+"]", err) - return nil, err + return err } defer resp.Body.Close() // nolint:errcheck - // Cloudian does not return a payload for this DELETE, but we can echo it to the callsite if all went well - return &groupId, nil + return nil } -func CreateGroup(group Group, tokenBase64 string) (*Group, error) { +func CreateGroup(group Group, tokenBase64 string) error { url := baseUrl + "/group" jsonData, err := marshalGroup(group) if err != nil { fmt.Println("Error marshaling JSON:", err) - return nil, err + return err } // Create a context with a timeout @@ -204,7 +203,7 @@ func CreateGroup(group Group, tokenBase64 string) (*Group, error) { req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(jsonData)) if err != nil { fmt.Println("POST error creating request: ", err) - return nil, err + return err } req.Header.Set("Content-Type", "application/json") @@ -215,21 +214,20 @@ func CreateGroup(group Group, tokenBase64 string) (*Group, error) { if err != nil { statusErrStr := strconv.FormatInt(int64(resp.StatusCode), 10) fmt.Println("POST to cloudian /group got status code ["+statusErrStr+"]", err) - return nil, err + return err } defer resp.Body.Close() // nolint:errcheck - // Cloudian does not return a payload for this POST, but we can echo it to the callsite if all went well - return &group, nil + return err } -func UpdateGroup(group Group, tokenBase64 string) (*Group, error) { +func UpdateGroup(group Group, tokenBase64 string) error { url := baseUrl + "/group" jsonData, err := marshalGroup(group) if err != nil { fmt.Println("Error marshaling JSON:", err) - return nil, err + return err } // Create a context with a timeout @@ -238,7 +236,7 @@ func UpdateGroup(group Group, tokenBase64 string) (*Group, error) { req, err := http.NewRequestWithContext(ctx, http.MethodPut, url, bytes.NewBuffer(jsonData)) if err != nil { fmt.Println("POST error creating request: ", err) - return nil, err + return err } req.Header.Set("Content-Type", "application/json") @@ -253,8 +251,7 @@ func UpdateGroup(group Group, tokenBase64 string) (*Group, error) { defer resp.Body.Close() // nolint:errcheck - // Cloudian does not return a payload for this PUT, but we can echo it to the callsite if all went well - return &group, nil + return nil } func GetGroup(groupId string, tokenBase64 string) (*Group, error) { From 768565d89f514731ab76d0e65137f1a484bc677a Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Fri, 22 Nov 2024 11:14:29 +0100 Subject: [PATCH 19/52] refactor: introduce cludian sdk client --- internal/sdk/cloudian/sdk.go | 75 ++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 0682d0ee..27d44962 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -7,11 +7,24 @@ import ( "fmt" "io" "net/http" - "os" "strconv" "time" ) +type Client struct { + baseURL string + httpClient *http.Client + token string +} + +func MkClient(baseUrl string, tokenBase64 string) *Client { + return &Client{ + baseURL: baseUrl, + httpClient: &http.Client{}, + token: tokenBase64, + } +} + type Group struct { Active string `json:"active"` GroupID string `json:"groupId"` @@ -50,12 +63,8 @@ func unmarshalUsersJson(data []byte) ([]User, error) { return users, err } -var baseUrl, _ = os.LookupEnv("CLOUDIAN_BASE_URL") - -var client = &http.Client{} - // List all users of a group -func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User, error) { +func (client Client) ListUsers(groupId string, offsetUserId *string) ([]User, error) { var retVal []User limit := 100 @@ -65,7 +74,7 @@ func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User offsetQueryParam = "&offset=" + *offsetUserId } - url := baseUrl + "/user/list?groupId=" + groupId + "&userType=all&userStatus=all&limit=" + strconv.Itoa(limit) + offsetQueryParam + 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) @@ -76,7 +85,7 @@ func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User return nil, err } - resp, err := client.Do(req) + resp, err := client.httpClient.Do(req) if err == nil { body, err := io.ReadAll(resp.Body) @@ -99,7 +108,7 @@ func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User // 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 := ListUsers(groupId, &users[limit].UserID, tokenBase64) + moreUsers, err := client.ListUsers(groupId, &users[limit].UserID) if err == nil { retVal = append(retVal, moreUsers...) @@ -115,8 +124,8 @@ func ListUsers(groupId string, offsetUserId *string, tokenBase64 string) ([]User } // Delete a single user -func DeleteUser(user User, tokenBase64 string) error { - url := baseUrl + "/user?userId=" + user.UserID + "&groupId=" + user.GroupID + "&canonicalUserId=" + user.CanonicalUserID +func (client Client) DeleteUser(user User) error { + url := client.baseURL + "/user?userId=" + user.UserID + "&groupId=" + user.GroupID + "&canonicalUserId=" + user.CanonicalUserID // Create a context with a timeout ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) @@ -128,9 +137,9 @@ func DeleteUser(user User, tokenBase64 string) error { } req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Basic "+tokenBase64) + req.Header.Set("Authorization", "Basic "+client.token) - resp, err := client.Do(req) + resp, err := client.httpClient.Do(req) if resp != nil && err == nil { // Cloudian does not return a payload for this DELETE, but we can echo it to the callsite if all went well @@ -142,27 +151,27 @@ func DeleteUser(user User, tokenBase64 string) error { } // Delete a group and all its members -func DeleteGroupRecursive(groupId string, tokenBase64 string) error { - users, err := ListUsers(groupId, nil, tokenBase64) +func (client Client) DeleteGroupRecursive(groupId string) error { + users, err := client.ListUsers(groupId, nil) if err != nil { for _, user := range users { - err := DeleteUser(user, tokenBase64) + err := client.DeleteUser(user) if err != nil { fmt.Println("Error deleting user: ", err) return err } } - return DeleteGroup(groupId, tokenBase64) + return client.DeleteGroup(groupId) } return err } // Deletes a group if it is without members -func DeleteGroup(groupId string, tokenBase64 string) error { - url := baseUrl + "/group?groupId=" + groupId +func (client Client) DeleteGroup(groupId string) error { + url := client.baseURL + "/group?groupId=" + groupId // Create a context with a timeout ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) @@ -174,9 +183,9 @@ func DeleteGroup(groupId string, tokenBase64 string) error { } req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Basic "+tokenBase64) + req.Header.Set("Authorization", "Basic "+client.token) - resp, err := client.Do(req) + resp, err := client.httpClient.Do(req) if err != nil { statusErrStr := strconv.Itoa(resp.StatusCode) @@ -188,8 +197,8 @@ func DeleteGroup(groupId string, tokenBase64 string) error { return nil } -func CreateGroup(group Group, tokenBase64 string) error { - url := baseUrl + "/group" +func (client Client) CreateGroup(group Group) error { + url := client.baseURL + "/group" jsonData, err := marshalGroup(group) if err != nil { @@ -207,9 +216,9 @@ func CreateGroup(group Group, tokenBase64 string) error { } req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Basic "+tokenBase64) + req.Header.Set("Authorization", "Basic "+client.token) - resp, err := client.Do(req) + resp, err := client.httpClient.Do(req) if err != nil { statusErrStr := strconv.FormatInt(int64(resp.StatusCode), 10) @@ -221,8 +230,8 @@ func CreateGroup(group Group, tokenBase64 string) error { return err } -func UpdateGroup(group Group, tokenBase64 string) error { - url := baseUrl + "/group" +func (client Client) UpdateGroup(group Group) error { + url := client.baseURL + "/group" jsonData, err := marshalGroup(group) if err != nil { @@ -240,9 +249,9 @@ func UpdateGroup(group Group, tokenBase64 string) error { } req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Basic "+tokenBase64) + req.Header.Set("Authorization", "Basic "+client.token) - resp, err := client.Do(req) + resp, err := client.httpClient.Do(req) if err != nil { statusErrStr := strconv.FormatInt(int64(resp.StatusCode), 10) @@ -254,8 +263,8 @@ func UpdateGroup(group Group, tokenBase64 string) error { return nil } -func GetGroup(groupId string, tokenBase64 string) (*Group, error) { - url := baseUrl + "/group?groupId=" + groupId +func (client Client) GetGroup(groupId string) (*Group, error) { + url := client.baseURL + "/group?groupId=" + groupId // Create a context with a timeout ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) @@ -267,9 +276,9 @@ func GetGroup(groupId string, tokenBase64 string) (*Group, error) { } req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Basic "+tokenBase64) + req.Header.Set("Authorization", "Basic "+client.token) - resp, err := client.Do(req) + resp, err := client.httpClient.Do(req) if err != nil { fmt.Println("GET errored towards Cloudian /group: ", err) From a8f26e2881b6bfe3c9f891c2a2b918df87b2fac2 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Fri, 22 Nov 2024 11:22:05 +0100 Subject: [PATCH 20/52] fix: cloudian sdk should not log --- internal/sdk/cloudian/sdk.go | 53 ++++++++++++------------------------ 1 file changed, 18 insertions(+), 35 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 27d44962..04f5fedb 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -81,8 +81,7 @@ func (client Client) ListUsers(groupId string, offsetUserId *string) ([]User, er defer cancel() req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { - fmt.Println("GET error creating list request: ", err) - return nil, err + return nil, fmt.Errorf("GET error creating list request: %w", err) } resp, err := client.httpClient.Do(req) @@ -91,14 +90,12 @@ func (client Client) ListUsers(groupId string, offsetUserId *string) ([]User, er body, err := io.ReadAll(resp.Body) defer resp.Body.Close() // nolint:errcheck if err != nil { - fmt.Println("GET reading list users response body failed: ", err) - return nil, err + return nil, fmt.Errorf("GET reading list users response body failed: %w", err) } users, err := unmarshalUsersJson(body) if err != nil { - fmt.Println("GET unmarshal users response body failed: ", err) - return nil, err + return nil, fmt.Errorf("GET unmarshal users response body failed: %w", err) } retVal = append(retVal, users...) @@ -117,8 +114,7 @@ func (client Client) ListUsers(groupId string, offsetUserId *string) ([]User, er return retVal, nil } else { - fmt.Println("GET list users failed: ", err) - return nil, err + return nil, fmt.Errorf("GET list users failed: %w", err) } } @@ -132,8 +128,7 @@ func (client Client) DeleteUser(user User) error { defer cancel() req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil) if err != nil { - fmt.Println("DELETE error creating request: ", err) - return err + return fmt.Errorf("DELETE error creating request: %w", err) } req.Header.Set("Content-Type", "application/json") @@ -158,8 +153,7 @@ func (client Client) DeleteGroupRecursive(groupId string) error { for _, user := range users { err := client.DeleteUser(user) if err != nil { - fmt.Println("Error deleting user: ", err) - return err + return fmt.Errorf("Error deleting user: %w", err) } } @@ -178,8 +172,7 @@ func (client Client) DeleteGroup(groupId string) error { defer cancel() req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil) if err != nil { - fmt.Println("DELETE error creating request: ", err) - return err + return fmt.Errorf("DELETE error creating request: %w", err) } req.Header.Set("Content-Type", "application/json") @@ -189,8 +182,7 @@ func (client Client) DeleteGroup(groupId string) error { if err != nil { statusErrStr := strconv.Itoa(resp.StatusCode) - fmt.Println("DELETE to cloudian /group got status code ["+statusErrStr+"]", err) - return err + return fmt.Errorf("DELETE to cloudian /group got status code [%s]: %w", statusErrStr, err) } defer resp.Body.Close() // nolint:errcheck @@ -202,8 +194,7 @@ func (client Client) CreateGroup(group Group) error { jsonData, err := marshalGroup(group) if err != nil { - fmt.Println("Error marshaling JSON:", err) - return err + return fmt.Errorf("Error marshaling JSON: %w", err) } // Create a context with a timeout @@ -211,8 +202,7 @@ func (client Client) CreateGroup(group Group) error { defer cancel() req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(jsonData)) if err != nil { - fmt.Println("POST error creating request: ", err) - return err + return fmt.Errorf("POST error creating request: %w", err) } req.Header.Set("Content-Type", "application/json") @@ -222,8 +212,7 @@ func (client Client) CreateGroup(group Group) error { if err != nil { statusErrStr := strconv.FormatInt(int64(resp.StatusCode), 10) - fmt.Println("POST to cloudian /group got status code ["+statusErrStr+"]", err) - return err + return fmt.Errorf("POST to cloudian /group got status code [%s]: %w", statusErrStr, err) } defer resp.Body.Close() // nolint:errcheck @@ -235,8 +224,7 @@ func (client Client) UpdateGroup(group Group) error { jsonData, err := marshalGroup(group) if err != nil { - fmt.Println("Error marshaling JSON:", err) - return err + return fmt.Errorf("Error marshaling JSON: %w", err) } // Create a context with a timeout @@ -244,8 +232,7 @@ func (client Client) UpdateGroup(group Group) error { defer cancel() req, err := http.NewRequestWithContext(ctx, http.MethodPut, url, bytes.NewBuffer(jsonData)) if err != nil { - fmt.Println("POST error creating request: ", err) - return err + return fmt.Errorf("PUT error creating request: %w", err) } req.Header.Set("Content-Type", "application/json") @@ -255,7 +242,7 @@ func (client Client) UpdateGroup(group Group) error { if err != nil { statusErrStr := strconv.FormatInt(int64(resp.StatusCode), 10) - fmt.Println("PUT to cloudian /group got status code ["+statusErrStr+"]", err) + return fmt.Errorf("PUT to cloudian /group got status code [%s]: %w", statusErrStr, err) } defer resp.Body.Close() // nolint:errcheck @@ -271,8 +258,7 @@ func (client Client) GetGroup(groupId string) (*Group, error) { defer cancel() req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) if err != nil { - fmt.Println("GET error creating request: ", err) - return nil, err + return nil, fmt.Errorf("GET error creating request: %w", err) } req.Header.Set("Content-Type", "application/json") @@ -281,8 +267,7 @@ func (client Client) GetGroup(groupId string) (*Group, error) { resp, err := client.httpClient.Do(req) if err != nil { - fmt.Println("GET errored towards Cloudian /group: ", err) - return nil, err + return nil, fmt.Errorf("GET error: %w", err) } defer resp.Body.Close() // nolint:errcheck @@ -290,14 +275,12 @@ func (client Client) GetGroup(groupId string) (*Group, error) { if resp.StatusCode == 200 { body, err := io.ReadAll(resp.Body) if err != nil { - fmt.Println("GET reading response body failed: ", err) - return nil, err + return nil, fmt.Errorf("GET reading response body failed: %w", err) } group, err := unmarshalGroupJson(body) if err != nil { - fmt.Println("GET unmarshal response body failed: ", err) - return nil, err + return nil, fmt.Errorf("GET unmarshal response body failed: %w", err) } return &group, nil From 52cdc12267db635d8a78139e3ba4e45b20f10849 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Fri, 22 Nov 2024 11:30:03 +0100 Subject: [PATCH 21/52] refactor: cloudian sdk common headers function --- internal/sdk/cloudian/sdk.go | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 04f5fedb..32aad75f 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -25,6 +25,11 @@ func MkClient(baseUrl string, tokenBase64 string) *Client { } } +func (client Client) headerModifier(req *http.Request) { + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Basic "+client.token) +} + type Group struct { Active string `json:"active"` GroupID string `json:"groupId"` @@ -131,8 +136,7 @@ func (client Client) DeleteUser(user User) error { return fmt.Errorf("DELETE error creating request: %w", err) } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Basic "+client.token) + client.headerModifier(req) resp, err := client.httpClient.Do(req) @@ -175,8 +179,7 @@ func (client Client) DeleteGroup(groupId string) error { return fmt.Errorf("DELETE error creating request: %w", err) } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Basic "+client.token) + client.headerModifier(req) resp, err := client.httpClient.Do(req) @@ -205,8 +208,7 @@ func (client Client) CreateGroup(group Group) error { return fmt.Errorf("POST error creating request: %w", err) } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Basic "+client.token) + client.headerModifier(req) resp, err := client.httpClient.Do(req) @@ -235,8 +237,7 @@ func (client Client) UpdateGroup(group Group) error { return fmt.Errorf("PUT error creating request: %w", err) } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Basic "+client.token) + client.headerModifier(req) resp, err := client.httpClient.Do(req) @@ -261,8 +262,7 @@ func (client Client) GetGroup(groupId string) (*Group, error) { return nil, fmt.Errorf("GET error creating request: %w", err) } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Basic "+client.token) + client.headerModifier(req) resp, err := client.httpClient.Do(req) From ddd5b785df0eba99a7d1cb4c9a86fd2e34a666eb Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Fri, 22 Nov 2024 11:39:15 +0100 Subject: [PATCH 22/52] test: add property test for json marshall roundtrip just for fun --- internal/sdk/cloudian/sdk_test.go | 52 +++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/internal/sdk/cloudian/sdk_test.go b/internal/sdk/cloudian/sdk_test.go index e24c1a80..13d9896e 100644 --- a/internal/sdk/cloudian/sdk_test.go +++ b/internal/sdk/cloudian/sdk_test.go @@ -1,9 +1,23 @@ package cloudian import ( + "math/rand" + "reflect" + "strings" "testing" + "testing/quick" ) +const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-. " + +func randomString(length int) string { + var sb strings.Builder + for i := 0; i < length; i++ { + sb.WriteByte(charset[rand.Intn(len(charset))]) + } + return sb.String() +} + func TestRealisticGroupSerialization(t *testing.T) { jsonString := `{ "active": "true", @@ -84,3 +98,41 @@ func TestUnmarshalUsers(t *testing.T) { } } + +func (group Group) Generate(rand *rand.Rand, size int) reflect.Value { + return reflect.ValueOf(Group{ + Active: "true", + GroupID: randomString(16), + GroupName: randomString(32), + LDAPEnabled: false, + LDAPGroup: randomString(8), + LDAPMatchAttribute: "", + LDAPSearch: "", + LDAPSearchUserBase: "", + LDAPServerURL: "", + LDAPUserDNTemplate: "", + S3endpointshttp: []string{"ALL"}, + S3endpointshttps: []string{"ALL"}, + S3websiteendpoints: []string{"ALL"}, + }) +} + +func TestGroupSerialization(t *testing.T) { + f := func(group Group) bool { + data, err := marshalGroup(group) + if err != nil { + return false + } + + deserialized, err := unmarshalGroupJson(data) + if err != nil { + return false + } + + return reflect.DeepEqual(group, deserialized) + } + + if err := quick.Check(f, nil); err != nil { + t.Error(err) + } +} From 1c25a64683f275db8b6b6c6a1594c8101aeafedb Mon Sep 17 00:00:00 2001 From: Sjur Date: Fri, 22 Nov 2024 14:26:57 +0100 Subject: [PATCH 23/52] Update internal/sdk/cloudian/sdk.go refactor: rename MkClient to NewClient Co-authored-by: Amund Tenstad --- internal/sdk/cloudian/sdk.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 32aad75f..f0972fd9 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -17,7 +17,7 @@ type Client struct { token string } -func MkClient(baseUrl string, tokenBase64 string) *Client { +func NewClient(baseUrl string, tokenBase64 string) *Client { return &Client{ baseURL: baseUrl, httpClient: &http.Client{}, From b93b115f9fc865868e9bf805cf522ceb244661ef Mon Sep 17 00:00:00 2001 From: Sjur Date: Fri, 22 Nov 2024 14:27:51 +0100 Subject: [PATCH 24/52] Update internal/sdk/cloudian/sdk.go fix: error check in list users Co-authored-by: Amund Tenstad --- internal/sdk/cloudian/sdk.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index f0972fd9..1fe25ab3 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -91,7 +91,9 @@ func (client Client) ListUsers(groupId string, offsetUserId *string) ([]User, er resp, err := client.httpClient.Do(req) - if err == nil { + if err != nil { + return fmt.Errorf("GET list users failed: %w", err) + } body, err := io.ReadAll(resp.Body) defer resp.Body.Close() // nolint:errcheck if err != nil { From fbf08f7572820285016e51b79baa2fce5937f008 Mon Sep 17 00:00:00 2001 From: Sjur Date: Fri, 22 Nov 2024 14:29:02 +0100 Subject: [PATCH 25/52] Update internal/sdk/cloudian/sdk.go fix: handle users errors logic Co-authored-by: Amund Tenstad --- internal/sdk/cloudian/sdk.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 1fe25ab3..4561e384 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -154,9 +154,11 @@ func (client Client) DeleteUser(user User) error { // Delete a group and all its members func (client Client) DeleteGroupRecursive(groupId string) error { users, err := client.ListUsers(groupId, nil) - if err != nil { - for _, user := range users { + return err + } + + for _, user := range users { err := client.DeleteUser(user) if err != nil { return fmt.Errorf("Error deleting user: %w", err) From e8db770dd3c383fcf43f40d67d6262e18740c147 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Fri, 22 Nov 2024 14:59:33 +0100 Subject: [PATCH 26/52] fix: compilation errors after updates --- internal/sdk/cloudian/sdk.go | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 4561e384..ed4c3f4c 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -92,8 +92,8 @@ func (client Client) ListUsers(groupId string, offsetUserId *string) ([]User, er resp, err := client.httpClient.Do(req) if err != nil { - return fmt.Errorf("GET list users 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 { @@ -120,10 +120,7 @@ func (client Client) ListUsers(groupId string, offsetUserId *string) ([]User, er } return retVal, nil - } else { - return nil, fmt.Errorf("GET list users failed: %w", err) } - } // Delete a single user @@ -159,16 +156,14 @@ func (client Client) DeleteGroupRecursive(groupId string) error { } for _, user := range users { - err := client.DeleteUser(user) - if err != nil { - return fmt.Errorf("Error deleting user: %w", err) - } + err := client.DeleteUser(user) + if err != nil { + return fmt.Errorf("Error deleting user: %w", err) } - return client.DeleteGroup(groupId) } - return err + return client.DeleteGroup(groupId) } // Deletes a group if it is without members From 5c7ca3fa5df35f2d0b58011e746e4406b852d9bb Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Fri, 22 Nov 2024 15:13:24 +0100 Subject: [PATCH 27/52] refactor: cloudian sdk centralize request creation --- internal/sdk/cloudian/sdk.go | 58 +++++++++++++++--------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index ed4c3f4c..af56b7d8 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -25,9 +25,17 @@ func NewClient(baseUrl string, tokenBase64 string) *Client { } } -func (client Client) headerModifier(req *http.Request) { +var ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second) + +func (client Client) newRequest(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) + } req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Basic "+client.token) + + return req, nil } type Group struct { @@ -127,15 +135,11 @@ func (client Client) ListUsers(groupId string, offsetUserId *string) ([]User, er func (client Client) DeleteUser(user User) error { url := client.baseURL + "/user?userId=" + user.UserID + "&groupId=" + user.GroupID + "&canonicalUserId=" + user.CanonicalUserID - // Create a context with a timeout - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil) + req, err := client.newRequest(url, http.MethodDelete, nil) if err != nil { - return fmt.Errorf("DELETE error creating request: %w", err) + return err } - - client.headerModifier(req) + defer cancel() resp, err := client.httpClient.Do(req) @@ -170,15 +174,11 @@ func (client Client) DeleteGroupRecursive(groupId string) error { func (client Client) DeleteGroup(groupId string) error { url := client.baseURL + "/group?groupId=" + groupId - // Create a context with a timeout - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - req, err := http.NewRequestWithContext(ctx, http.MethodDelete, url, nil) + req, err := client.newRequest(url, http.MethodDelete, nil) if err != nil { - return fmt.Errorf("DELETE error creating request: %w", err) + return err } - - client.headerModifier(req) + defer cancel() resp, err := client.httpClient.Do(req) @@ -199,15 +199,11 @@ func (client Client) CreateGroup(group Group) error { return fmt.Errorf("Error marshaling JSON: %w", err) } - // Create a context with a timeout - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(jsonData)) + req, err := client.newRequest(url, http.MethodPost, &jsonData) if err != nil { - return fmt.Errorf("POST error creating request: %w", err) + return err } - - client.headerModifier(req) + defer cancel() resp, err := client.httpClient.Do(req) @@ -229,14 +225,12 @@ func (client Client) UpdateGroup(group Group) error { } // Create a context with a timeout - ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) - defer cancel() - req, err := http.NewRequestWithContext(ctx, http.MethodPut, url, bytes.NewBuffer(jsonData)) + req, err := client.newRequest(url, http.MethodPut, &jsonData) if err != nil { - return fmt.Errorf("PUT error creating request: %w", err) + return err } - client.headerModifier(req) + defer cancel() resp, err := client.httpClient.Do(req) @@ -253,15 +247,11 @@ func (client Client) UpdateGroup(group Group) error { func (client Client) GetGroup(groupId string) (*Group, error) { url := client.baseURL + "/group?groupId=" + groupId - // 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) + req, err := client.newRequest(url, http.MethodGet, nil) if err != nil { - return nil, fmt.Errorf("GET error creating request: %w", err) + return nil, err } - - client.headerModifier(req) + defer cancel() resp, err := client.httpClient.Do(req) From 17a8b274eb1db474875267d8207f46549efd0f09 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Fri, 22 Nov 2024 15:36:13 +0100 Subject: [PATCH 28/52] fix: cludian sdk delete user error logic --- internal/sdk/cloudian/sdk.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index af56b7d8..941f3dff 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -143,12 +143,13 @@ func (client Client) DeleteUser(user User) error { resp, err := client.httpClient.Do(req) - if resp != nil && err == nil { - // Cloudian does not return a payload for this DELETE, but we can echo it to the callsite if all went well + if err != nil { + return err + } + if resp != nil { defer resp.Body.Close() // nolint:errcheck - - return nil } + return err } From 27177d0dc383cfd1c9863ba239eb80af56f39ecb Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Fri, 22 Nov 2024 15:41:40 +0100 Subject: [PATCH 29/52] fix: cloudian sdk errors --- internal/sdk/cloudian/sdk.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 941f3dff..5da5227e 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -184,8 +184,7 @@ func (client Client) DeleteGroup(groupId string) error { resp, err := client.httpClient.Do(req) if err != nil { - statusErrStr := strconv.Itoa(resp.StatusCode) - return fmt.Errorf("DELETE to cloudian /group got status code [%s]: %w", statusErrStr, err) + return fmt.Errorf("DELETE to cloudian /group got: %w", err) } defer resp.Body.Close() // nolint:errcheck @@ -209,8 +208,7 @@ func (client Client) CreateGroup(group Group) error { resp, err := client.httpClient.Do(req) if err != nil { - statusErrStr := strconv.FormatInt(int64(resp.StatusCode), 10) - return fmt.Errorf("POST to cloudian /group got status code [%s]: %w", statusErrStr, err) + return fmt.Errorf("POST to cloudian /group", err) } defer resp.Body.Close() // nolint:errcheck @@ -236,8 +234,7 @@ func (client Client) UpdateGroup(group Group) error { resp, err := client.httpClient.Do(req) if err != nil { - statusErrStr := strconv.FormatInt(int64(resp.StatusCode), 10) - return fmt.Errorf("PUT to cloudian /group got status code [%s]: %w", statusErrStr, err) + return fmt.Errorf("PUT to cloudian /group: %w", err) } defer resp.Body.Close() // nolint:errcheck @@ -260,7 +257,9 @@ func (client Client) GetGroup(groupId string) (*Group, error) { return nil, fmt.Errorf("GET error: %w", err) } - defer resp.Body.Close() // nolint:errcheck + if resp != nil { + defer resp.Body.Close() // nolint:errcheck + } if resp.StatusCode == 200 { body, err := io.ReadAll(resp.Body) @@ -277,5 +276,5 @@ func (client Client) GetGroup(groupId string) (*Group, error) { } // Cloudian-API returns 204 if the group does not exist - return nil, nil + return nil, err } From 15972b5a5f6781c762b7e1228c0988def7f35a15 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Mon, 25 Nov 2024 09:40:48 +0100 Subject: [PATCH 30/52] refactor: cloudian sdk allow callsite to pass context --- internal/sdk/cloudian/sdk.go | 310 +++++++++++++++++++---------------- 1 file changed, 167 insertions(+), 143 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 5da5227e..7cb5bf0b 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -8,7 +8,6 @@ import ( "io" "net/http" "strconv" - "time" ) type Client struct { @@ -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) @@ -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 + } } From be4322aa8f8db09327420a41648fdf5ed072a2ca Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Mon, 25 Nov 2024 09:58:17 +0100 Subject: [PATCH 31/52] fix: cloudian sdk no need to check ctx --- internal/sdk/cloudian/sdk.go | 280 +++++++++++++++-------------------- 1 file changed, 123 insertions(+), 157 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 7cb5bf0b..6d634b4a 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -75,230 +75,196 @@ func unmarshalUsersJson(data []byte) ([]User, error) { // List all users of a group 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 + var retVal []User - limit := 100 + limit := 100 - var offsetQueryParam = "" - if offsetUserId != nil { - offsetQueryParam = "&offset=" + *offsetUserId - } + var offsetQueryParam = "" + if offsetUserId != nil { + offsetQueryParam = "&offset=" + *offsetUserId + } - url := client.baseURL + "/user/list?groupId=" + groupId + "&userType=all&userStatus=all&limit=" + strconv.Itoa(limit) + offsetQueryParam + url := client.baseURL + "/user/list?groupId=" + groupId + "&userType=all&userStatus=all&limit=" + strconv.Itoa(limit) + offsetQueryParam - req, err := client.newRequest(ctx, http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("GET error creating list request: %w", err) - } + req, err := client.newRequest(ctx, http.MethodGet, url, nil) + if err != nil { + return nil, fmt.Errorf("GET error creating list request: %w", err) + } - resp, err := client.httpClient.Do(req) + resp, err := client.httpClient.Do(req) + 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 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 - if err != nil { - return nil, fmt.Errorf("GET reading list users response body failed: %w", err) - } + 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) - } + 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(ctx, 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(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 - } + url := client.baseURL + "/user?userId=" + user.UserID + "&groupId=" + user.GroupID + "&canonicalUserId=" + user.CanonicalUserID - resp, err := client.httpClient.Do(req) + req, err := client.newRequest(ctx, url, http.MethodDelete, nil) + if err != nil { + return err + } - if err != nil { - return err - } - if resp != nil { - defer resp.Body.Close() // nolint:errcheck - } + resp, err := client.httpClient.Do(req) + 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(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 err - } - - for _, user := range users { - err := client.DeleteUser(ctx, user) - if err != nil { - return fmt.Errorf("Error deleting user: %w", err) - } + users, err := client.ListUsers(ctx, groupId, nil) + if err != nil { + 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(ctx, groupId) } + + return client.DeleteGroup(ctx, groupId) } // Deletes a group if it is without members 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 + url := client.baseURL + "/group?groupId=" + groupId - req, err := client.newRequest(ctx, url, http.MethodDelete, nil) - if err != nil { - return err - } - - resp, err := client.httpClient.Do(req) + req, err := client.newRequest(ctx, url, http.MethodDelete, nil) + if err != nil { + return err + } - if err != nil { - return fmt.Errorf("DELETE to cloudian /group got: %w", err) - } - defer resp.Body.Close() // nolint:errcheck + resp, err := client.httpClient.Do(req) - return nil + if err != nil { + return fmt.Errorf("DELETE to cloudian /group got: %w", err) } + defer resp.Body.Close() // nolint:errcheck + + return nil } 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) - } + url := client.baseURL + "/group" - req, err := client.newRequest(ctx, url, http.MethodPost, &jsonData) - if err != nil { - return err - } + jsonData, err := marshalGroup(group) + if err != nil { + return fmt.Errorf("Error marshaling JSON: %w", err) + } - resp, err := client.httpClient.Do(req) + req, err := client.newRequest(ctx, url, http.MethodPost, &jsonData) + if err != nil { + return err + } - if err != nil { - return fmt.Errorf("POST to cloudian /group: %w", err) - } - defer resp.Body.Close() // nolint:errcheck + resp, err := client.httpClient.Do(req) - return err + if err != nil { + return fmt.Errorf("POST to cloudian /group: %w", err) } + defer resp.Body.Close() // nolint:errcheck + + return err } 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" - - jsonData, err := marshalGroup(group) - if err != nil { - return fmt.Errorf("Error marshaling JSON: %w", err) - } - // Create a context with a timeout - req, err := client.newRequest(ctx, url, http.MethodPut, &jsonData) - if err != nil { - return err - } + 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) } + + defer resp.Body.Close() // nolint:errcheck + + return nil } 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 + url := client.baseURL + "/group?groupId=" + groupId - req, err := client.newRequest(ctx, url, http.MethodGet, nil) - if err != nil { - return nil, err - } - - resp, err := client.httpClient.Do(req) + req, err := client.newRequest(ctx, url, http.MethodGet, nil) + if err != nil { + return nil, err + } - if err != nil { - return nil, fmt.Errorf("GET error: %w", err) - } + resp, err := client.httpClient.Do(req) - if resp != nil { - defer resp.Body.Close() // nolint:errcheck - } + if err != nil { + return nil, fmt.Errorf("GET error: %w", err) + } - if resp.StatusCode == 200 { - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("GET reading response body failed: %w", err) - } + if resp != nil { + defer resp.Body.Close() // nolint:errcheck + } - group, err := unmarshalGroupJson(body) - if err != nil { - return nil, fmt.Errorf("GET unmarshal response body failed: %w", err) - } + 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 &group, nil + group, err := unmarshalGroupJson(body) + if err != nil { + return nil, fmt.Errorf("GET unmarshal response body failed: %w", err) } - // Cloudian-API returns 204 if the group does not exist - return nil, err + return &group, nil } + + // Cloudian-API returns 204 if the group does not exist + return nil, err } From 2d6abbd6eb9a6d30edcbf7895b156f8133866103 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Mon, 25 Nov 2024 10:00:30 +0100 Subject: [PATCH 32/52] refactor: cloudian sdk move util functions to the bottom --- internal/sdk/cloudian/sdk.go | 99 ++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 49 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 6d634b4a..c6559289 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -57,65 +57,50 @@ type User struct { CanonicalUserID string `json:"canonicalUserId"` } -func marshalGroup(group Group) ([]byte, error) { - return json.Marshal(group) -} - -func unmarshalGroupJson(data []byte) (Group, error) { - var group Group - err := json.Unmarshal(data, &group) - return group, err -} - -func unmarshalUsersJson(data []byte) ([]User, error) { - var users []User - err := json.Unmarshal(data, &users) - return users, err -} // List all users of a group func (client Client) ListUsers(ctx context.Context, 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 req, err := client.newRequest(ctx, http.MethodGet, url, nil) if err != nil { return nil, fmt.Errorf("GET error creating list request: %w", err) } - + resp, err := client.httpClient.Do(req) - + if err != nil { return nil, fmt.Errorf("GET list users failed: %w", err) - } else { - body, err := io.ReadAll(resp.Body) + } 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...) - + // 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...) } @@ -129,21 +114,21 @@ func (client Client) ListUsers(ctx context.Context, groupId string, offsetUserId // Delete a single user func (client Client) DeleteUser(ctx context.Context, user User) error { 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 } - + resp, err := client.httpClient.Do(req) - + if err != nil { return err } if resp != nil { defer resp.Body.Close() // nolint:errcheck } - + return err } @@ -159,34 +144,34 @@ func (client Client) DeleteGroupRecursive(ctx context.Context, groupId string) e if err != nil { return fmt.Errorf("Error deleting user: %w", err) } - + } - + return client.DeleteGroup(ctx, groupId) } // Deletes a group if it is without members func (client Client) DeleteGroup(ctx context.Context, groupId string) error { url := client.baseURL + "/group?groupId=" + groupId - + req, err := client.newRequest(ctx, url, http.MethodDelete, nil) if err != nil { return err } - + 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 - + return nil } func (client Client) CreateGroup(ctx context.Context, group Group) error { url := client.baseURL + "/group" - + jsonData, err := marshalGroup(group) if err != nil { return fmt.Errorf("Error marshaling JSON: %w", err) @@ -196,26 +181,26 @@ func (client Client) CreateGroup(ctx context.Context, group Group) error { if err != nil { return err } - + resp, err := client.httpClient.Do(req) - + if err != nil { return fmt.Errorf("POST to cloudian /group: %w", err) } defer resp.Body.Close() // nolint:errcheck - + return err } func (client Client) UpdateGroup(ctx context.Context, group Group) error { - + url := client.baseURL + "/group" - + jsonData, err := marshalGroup(group) if err != nil { return fmt.Errorf("Error marshaling JSON: %w", err) } - + // Create a context with a timeout req, err := client.newRequest(ctx, url, http.MethodPut, &jsonData) if err != nil { @@ -223,19 +208,19 @@ func (client Client) UpdateGroup(ctx context.Context, group Group) error { } resp, err := client.httpClient.Do(req) - + if err != nil { return fmt.Errorf("PUT to cloudian /group: %w", err) } - + defer resp.Body.Close() // nolint:errcheck - + return nil } func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, error) { url := client.baseURL + "/group?groupId=" + groupId - + req, err := client.newRequest(ctx, url, http.MethodGet, nil) if err != nil { return nil, err @@ -246,11 +231,11 @@ func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, erro if err != nil { return nil, fmt.Errorf("GET error: %w", err) } - + if resp != nil { defer resp.Body.Close() // nolint:errcheck } - + if resp.StatusCode == 200 { body, err := io.ReadAll(resp.Body) if err != nil { @@ -261,10 +246,26 @@ func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, erro 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 } + +func marshalGroup(group Group) ([]byte, error) { + return json.Marshal(group) +} + +func unmarshalGroupJson(data []byte) (Group, error) { + var group Group + err := json.Unmarshal(data, &group) + return group, err +} + +func unmarshalUsersJson(data []byte) ([]User, error) { + var users []User + err := json.Unmarshal(data, &users) + return users, err +} \ No newline at end of file From 8a908c929cc55094967000d3a08e548fafe4dd10 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Mon, 25 Nov 2024 10:03:20 +0100 Subject: [PATCH 33/52] refactor: cloudian sdk gofmt --- internal/sdk/cloudian/sdk.go | 71 ++++++++++++++++++------------------ 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index c6559289..ec60b222 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -57,50 +57,49 @@ type User struct { CanonicalUserID string `json:"canonicalUserId"` } - // List all users of a group func (client Client) ListUsers(ctx context.Context, 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 req, err := client.newRequest(ctx, http.MethodGet, url, nil) if err != nil { return nil, fmt.Errorf("GET error creating list request: %w", err) } - + resp, err := client.httpClient.Do(req) - + if err != nil { return nil, fmt.Errorf("GET list users failed: %w", err) - } else { - body, err := io.ReadAll(resp.Body) + } 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...) - + // 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...) } @@ -114,21 +113,21 @@ func (client Client) ListUsers(ctx context.Context, groupId string, offsetUserId // Delete a single user func (client Client) DeleteUser(ctx context.Context, user User) error { 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 } - + resp, err := client.httpClient.Do(req) - + if err != nil { return err } if resp != nil { defer resp.Body.Close() // nolint:errcheck } - + return err } @@ -144,34 +143,34 @@ func (client Client) DeleteGroupRecursive(ctx context.Context, groupId string) e if err != nil { return fmt.Errorf("Error deleting user: %w", err) } - + } - + return client.DeleteGroup(ctx, groupId) } // Deletes a group if it is without members func (client Client) DeleteGroup(ctx context.Context, groupId string) error { url := client.baseURL + "/group?groupId=" + groupId - + req, err := client.newRequest(ctx, url, http.MethodDelete, nil) if err != nil { return err } - + 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 - + return nil } func (client Client) CreateGroup(ctx context.Context, group Group) error { url := client.baseURL + "/group" - + jsonData, err := marshalGroup(group) if err != nil { return fmt.Errorf("Error marshaling JSON: %w", err) @@ -181,26 +180,26 @@ func (client Client) CreateGroup(ctx context.Context, group Group) error { if err != nil { return err } - + resp, err := client.httpClient.Do(req) - + if err != nil { return fmt.Errorf("POST to cloudian /group: %w", err) } defer resp.Body.Close() // nolint:errcheck - + return err } func (client Client) UpdateGroup(ctx context.Context, group Group) error { - + url := client.baseURL + "/group" - + jsonData, err := marshalGroup(group) if err != nil { return fmt.Errorf("Error marshaling JSON: %w", err) } - + // Create a context with a timeout req, err := client.newRequest(ctx, url, http.MethodPut, &jsonData) if err != nil { @@ -208,19 +207,19 @@ func (client Client) UpdateGroup(ctx context.Context, group Group) error { } resp, err := client.httpClient.Do(req) - + if err != nil { return fmt.Errorf("PUT to cloudian /group: %w", err) } - + defer resp.Body.Close() // nolint:errcheck - + return nil } func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, error) { url := client.baseURL + "/group?groupId=" + groupId - + req, err := client.newRequest(ctx, url, http.MethodGet, nil) if err != nil { return nil, err @@ -231,11 +230,11 @@ func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, erro if err != nil { return nil, fmt.Errorf("GET error: %w", err) } - + if resp != nil { defer resp.Body.Close() // nolint:errcheck } - + if resp.StatusCode == 200 { body, err := io.ReadAll(resp.Body) if err != nil { @@ -246,7 +245,7 @@ func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, erro if err != nil { return nil, fmt.Errorf("GET unmarshal response body failed: %w", err) } - + return &group, nil } @@ -268,4 +267,4 @@ func unmarshalUsersJson(data []byte) ([]User, error) { var users []User err := json.Unmarshal(data, &users) return users, err -} \ No newline at end of file +} From 63adcedf3ff385e2d12e01c6006c975c9fc34710 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Mon, 25 Nov 2024 10:07:25 +0100 Subject: [PATCH 34/52] refactor: cloudian sdk no need to else here --- internal/sdk/cloudian/sdk.go | 43 ++++++++++++++++++------------------ 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index ec60b222..fa827ca9 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -79,35 +79,34 @@ func (client Client) ListUsers(ctx context.Context, groupId string, offsetUserId 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 - if err != nil { - return nil, fmt.Errorf("GET reading list users response body failed: %w", err) - } + } + 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) - } + 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(ctx, 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 From 3855f66bc7b5554261eaf10326e9b11041a7f998 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Mon, 25 Nov 2024 10:07:25 +0100 Subject: [PATCH 35/52] refactor: cloudian sdk no need to else here --- internal/sdk/cloudian/sdk.go | 38 ++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index fa827ca9..53ecf4ab 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -16,25 +16,6 @@ type Client struct { token string } -func NewClient(baseUrl string, tokenBase64 string) *Client { - return &Client{ - baseURL: baseUrl, - httpClient: &http.Client{}, - token: tokenBase64, - } -} - -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) - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("Authorization", "Basic "+client.token) - - return req, nil -} - type Group struct { Active string `json:"active"` GroupID string `json:"groupId"` @@ -57,6 +38,14 @@ type User struct { CanonicalUserID string `json:"canonicalUserId"` } +func NewClient(baseUrl string, tokenBase64 string) *Client { + return &Client{ + baseURL: baseUrl, + httpClient: &http.Client{}, + token: tokenBase64, + } +} + // List all users of a group func (client Client) ListUsers(ctx context.Context, groupId string, offsetUserId *string) ([]User, error) { var retVal []User @@ -252,6 +241,17 @@ func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, erro return nil, err } +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) + } + req.Header.Set("Content-Type", "application/json") + req.Header.Set("Authorization", "Basic "+client.token) + + return req, nil +} + func marshalGroup(group Group) ([]byte, error) { return json.Marshal(group) } From d1b09cb298d2a85cc83f33d5cd12f849ee8c3cf4 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Mon, 25 Nov 2024 10:22:03 +0100 Subject: [PATCH 36/52] refactor: cloudian sdk rewrite getgroup logic to switch on resp.statuscode --- internal/sdk/cloudian/sdk.go | 37 ++++++++++++++++++++---------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 53ecf4ab..bf9564be 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -89,9 +89,10 @@ func (client Client) ListUsers(ctx context.Context, groupId string, offsetUserId // Fetch more results moreUsers, err := client.ListUsers(ctx, groupId, &users[limit].UserID) - if err == nil { - retVal = append(retVal, moreUsers...) + if err != nil { + return nil, fmt.Errorf("GET list users failed: %w", err) } + retVal = append(retVal, moreUsers...) } return retVal, nil @@ -221,23 +222,27 @@ func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, erro if resp != nil { defer resp.Body.Close() // nolint:errcheck - } - - if resp.StatusCode == 200 { - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("GET reading response body failed: %w", err) + switch resp.StatusCode { + case 200: + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("GET reading response body failed: %w", err) + } + + group, err := unmarshalGroupJson(body) + if err != nil { + return nil, fmt.Errorf("GET unmarshal response body failed: %w", err) + } + + return &group, nil + case 204: + // Cloudian-API returns 204 if the group does not exist + return nil, nil + default: + return nil, fmt.Errorf("GET unexpected status code: %d", resp.StatusCode) } - - 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 } From 4ca796e5fd2b00ee0ffcb5062a7d3afe36bcad3e Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Mon, 25 Nov 2024 10:26:18 +0100 Subject: [PATCH 37/52] refactor: cloudian sdk gofmt --- internal/sdk/cloudian/sdk.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index bf9564be..a8af9ae9 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -228,17 +228,17 @@ func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, erro if err != nil { return nil, fmt.Errorf("GET reading response body failed: %w", err) } - + group, err := unmarshalGroupJson(body) if err != nil { return nil, fmt.Errorf("GET unmarshal response body failed: %w", err) } - + return &group, nil case 204: // Cloudian-API returns 204 if the group does not exist return nil, nil - default: + default: return nil, fmt.Errorf("GET unexpected status code: %d", resp.StatusCode) } } From f33b22d46b3af74e15732cb3aa3dc83b780bc492 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Mon, 25 Nov 2024 10:51:13 +0100 Subject: [PATCH 38/52] refactor: consistent use of error wrapping and defer body close --- internal/sdk/cloudian/sdk.go | 69 +++++++++++++++++------------------- 1 file changed, 32 insertions(+), 37 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index a8af9ae9..3b0cfd53 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -69,8 +69,9 @@ func (client Client) ListUsers(ctx context.Context, groupId string, offsetUserId if err != nil { return nil, fmt.Errorf("GET list users failed: %w", err) } - body, err := io.ReadAll(resp.Body) defer resp.Body.Close() // nolint:errcheck + + body, err := io.ReadAll(resp.Body) if err != nil { return nil, fmt.Errorf("GET reading list users response body failed: %w", err) } @@ -105,32 +106,30 @@ func (client Client) DeleteUser(ctx context.Context, user User) error { req, err := client.newRequest(ctx, url, http.MethodDelete, nil) if err != nil { - return err + return fmt.Errorf("DELETE error creating request: %w", err) } resp, err := client.httpClient.Do(req) if err != nil { - return err - } - if resp != nil { - defer resp.Body.Close() // nolint:errcheck + return fmt.Errorf("DELETE to cloudian /user got: %w", err) } + defer resp.Body.Close() // nolint:errcheck - return err + return nil } // Delete a group and all its members func (client Client) DeleteGroupRecursive(ctx context.Context, groupId string) error { users, err := client.ListUsers(ctx, groupId, nil) if err != nil { - return err + return fmt.Errorf("error listing users: %w", err) } for _, user := range users { err := client.DeleteUser(ctx, user) if err != nil { - return fmt.Errorf("Error deleting user: %w", err) + return fmt.Errorf("error deleting user: %w", err) } } @@ -144,7 +143,7 @@ func (client Client) DeleteGroup(ctx context.Context, groupId string) error { req, err := client.newRequest(ctx, url, http.MethodDelete, nil) if err != nil { - return err + return fmt.Errorf("error creating request: %w", err) } resp, err := client.httpClient.Do(req) @@ -162,12 +161,12 @@ func (client Client) CreateGroup(ctx context.Context, group Group) error { jsonData, err := marshalGroup(group) if err != nil { - return fmt.Errorf("Error marshaling JSON: %w", err) + return fmt.Errorf("error marshaling JSON: %w", err) } req, err := client.newRequest(ctx, url, http.MethodPost, &jsonData) if err != nil { - return err + return fmt.Errorf("error creating request: %w", err) } resp, err := client.httpClient.Do(req) @@ -177,7 +176,7 @@ func (client Client) CreateGroup(ctx context.Context, group Group) error { } defer resp.Body.Close() // nolint:errcheck - return err + return nil } func (client Client) UpdateGroup(ctx context.Context, group Group) error { @@ -186,13 +185,13 @@ func (client Client) UpdateGroup(ctx context.Context, group Group) error { jsonData, err := marshalGroup(group) if err != nil { - return fmt.Errorf("Error marshaling JSON: %w", err) + return fmt.Errorf("error marshaling JSON: %w", err) } // Create a context with a timeout req, err := client.newRequest(ctx, url, http.MethodPut, &jsonData) if err != nil { - return err + return fmt.Errorf("error creating request: %w", err) } resp, err := client.httpClient.Do(req) @@ -200,7 +199,6 @@ func (client Client) UpdateGroup(ctx context.Context, group Group) error { if err != nil { return fmt.Errorf("PUT to cloudian /group: %w", err) } - defer resp.Body.Close() // nolint:errcheck return nil @@ -219,31 +217,28 @@ func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, erro if err != nil { return nil, fmt.Errorf("GET error: %w", err) } + defer resp.Body.Close() // nolint:errcheck - if resp != nil { - defer resp.Body.Close() // nolint:errcheck - switch resp.StatusCode { - case 200: - body, err := io.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("GET reading response body failed: %w", err) - } - - group, err := unmarshalGroupJson(body) - if err != nil { - return nil, fmt.Errorf("GET unmarshal response body failed: %w", err) - } - - return &group, nil - case 204: - // Cloudian-API returns 204 if the group does not exist - return nil, nil - default: - return nil, fmt.Errorf("GET unexpected status code: %d", resp.StatusCode) + switch resp.StatusCode { + case 200: + body, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("GET reading response body failed: %w", err) } + + group, err := unmarshalGroupJson(body) + if err != nil { + return nil, fmt.Errorf("GET unmarshal response body failed: %w", err) + } + + return &group, nil + case 204: + // Cloudian-API returns 204 if the group does not exist + return nil, nil + default: + return nil, fmt.Errorf("GET unexpected status. Failure: %w", err) } - return nil, err } func (client Client) newRequest(ctx context.Context, url string, method string, body *[]byte) (*http.Request, error) { From 0996284320d49f9dea9a736447dd36137dc0cc5b Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Mon, 25 Nov 2024 11:05:12 +0100 Subject: [PATCH 39/52] doc: cloudian sdk minimal golang doc --- internal/sdk/cloudian/sdk.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 3b0cfd53..0aae027b 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -46,7 +46,7 @@ func NewClient(baseUrl string, tokenBase64 string) *Client { } } -// List all users of a group +// List all users of a group. func (client Client) ListUsers(ctx context.Context, groupId string, offsetUserId *string) ([]User, error) { var retVal []User @@ -100,7 +100,7 @@ func (client Client) ListUsers(ctx context.Context, groupId string, offsetUserId } -// Delete a single user +// Delete a single user. Errors if the user does not exist. func (client Client) DeleteUser(ctx context.Context, user User) error { url := client.baseURL + "/user?userId=" + user.UserID + "&groupId=" + user.GroupID + "&canonicalUserId=" + user.CanonicalUserID @@ -119,7 +119,7 @@ func (client Client) DeleteUser(ctx context.Context, user User) error { return nil } -// Delete a group and all its members +// Delete a group and all its members. func (client Client) DeleteGroupRecursive(ctx context.Context, groupId string) error { users, err := client.ListUsers(ctx, groupId, nil) if err != nil { @@ -137,7 +137,7 @@ func (client Client) DeleteGroupRecursive(ctx context.Context, groupId string) e return client.DeleteGroup(ctx, groupId) } -// Deletes a group if it is without members +// Deletes a group if it is without members. func (client Client) DeleteGroup(ctx context.Context, groupId string) error { url := client.baseURL + "/group?groupId=" + groupId @@ -156,6 +156,7 @@ func (client Client) DeleteGroup(ctx context.Context, groupId string) error { return nil } +// Creates a group. func (client Client) CreateGroup(ctx context.Context, group Group) error { url := client.baseURL + "/group" @@ -179,6 +180,7 @@ func (client Client) CreateGroup(ctx context.Context, group Group) error { return nil } +// Updates a group if it does not exists. func (client Client) UpdateGroup(ctx context.Context, group Group) error { url := client.baseURL + "/group" @@ -204,6 +206,7 @@ func (client Client) UpdateGroup(ctx context.Context, group Group) error { return nil } +// Get a group. Returns nil if the group does not exist. func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, error) { url := client.baseURL + "/group?groupId=" + groupId From 7dc35de4fe44bf6f79cd8dc08dc7e5ae27f57821 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Mon, 25 Nov 2024 11:08:49 +0100 Subject: [PATCH 40/52] refactor: Initialisms ref go.dev wiki --- internal/sdk/cloudian/sdk.go | 20 ++++++++++---------- internal/sdk/cloudian/sdk_test.go | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 0aae027b..0b3256a0 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -12,7 +12,7 @@ import ( type Client struct { baseURL string - httpClient *http.Client + HTTPClient *http.Client token string } @@ -27,8 +27,8 @@ type Group struct { LDAPSearchUserBase string `json:"ldapSearchUserBase"` LDAPServerURL string `json:"ldapServerURL"` LDAPUserDNTemplate string `json:"ldapUserDNTemplate"` - S3endpointshttp []string `json:"s3endpointshttp"` - S3endpointshttps []string `json:"s3endpointshttps"` + S3endpointsHTTP []string `json:"s3endpointshttp"` + S3endpointsHTTPS []string `json:"s3endpointshttps"` S3websiteendpoints []string `json:"s3websiteendpoints"` } @@ -41,7 +41,7 @@ type User struct { func NewClient(baseUrl string, tokenBase64 string) *Client { return &Client{ baseURL: baseUrl, - httpClient: &http.Client{}, + HTTPClient: &http.Client{}, token: tokenBase64, } } @@ -64,7 +64,7 @@ func (client Client) ListUsers(ctx context.Context, groupId string, offsetUserId return nil, fmt.Errorf("GET error creating list request: %w", err) } - resp, err := client.httpClient.Do(req) + resp, err := client.HTTPClient.Do(req) if err != nil { return nil, fmt.Errorf("GET list users failed: %w", err) @@ -109,7 +109,7 @@ func (client Client) DeleteUser(ctx context.Context, user User) error { return fmt.Errorf("DELETE error creating request: %w", err) } - resp, err := client.httpClient.Do(req) + resp, err := client.HTTPClient.Do(req) if err != nil { return fmt.Errorf("DELETE to cloudian /user got: %w", err) @@ -146,7 +146,7 @@ func (client Client) DeleteGroup(ctx context.Context, groupId string) error { return fmt.Errorf("error creating request: %w", err) } - 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) @@ -170,7 +170,7 @@ func (client Client) CreateGroup(ctx context.Context, group Group) error { return fmt.Errorf("error creating request: %w", err) } - resp, err := client.httpClient.Do(req) + resp, err := client.HTTPClient.Do(req) if err != nil { return fmt.Errorf("POST to cloudian /group: %w", err) @@ -196,7 +196,7 @@ func (client Client) UpdateGroup(ctx context.Context, group Group) error { return fmt.Errorf("error creating request: %w", err) } - resp, err := client.httpClient.Do(req) + resp, err := client.HTTPClient.Do(req) if err != nil { return fmt.Errorf("PUT to cloudian /group: %w", err) @@ -215,7 +215,7 @@ func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, erro return nil, err } - resp, err := client.httpClient.Do(req) + resp, err := client.HTTPClient.Do(req) if err != nil { return nil, fmt.Errorf("GET error: %w", err) diff --git a/internal/sdk/cloudian/sdk_test.go b/internal/sdk/cloudian/sdk_test.go index 13d9896e..2a77fe73 100644 --- a/internal/sdk/cloudian/sdk_test.go +++ b/internal/sdk/cloudian/sdk_test.go @@ -111,8 +111,8 @@ func (group Group) Generate(rand *rand.Rand, size int) reflect.Value { LDAPSearchUserBase: "", LDAPServerURL: "", LDAPUserDNTemplate: "", - S3endpointshttp: []string{"ALL"}, - S3endpointshttps: []string{"ALL"}, + S3endpointsHTTP: []string{"ALL"}, + S3endpointsHTTPS: []string{"ALL"}, S3websiteendpoints: []string{"ALL"}, }) } From 65a875ef512ae5824240db51d736f7c2e1a540f9 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Mon, 25 Nov 2024 12:24:44 +0100 Subject: [PATCH 41/52] test: fix cloudian sdk test with utf-8 chars --- internal/sdk/cloudian/sdk_test.go | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/internal/sdk/cloudian/sdk_test.go b/internal/sdk/cloudian/sdk_test.go index 2a77fe73..dffca41d 100644 --- a/internal/sdk/cloudian/sdk_test.go +++ b/internal/sdk/cloudian/sdk_test.go @@ -8,16 +8,6 @@ import ( "testing/quick" ) -const charset = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-. " - -func randomString(length int) string { - var sb strings.Builder - for i := 0; i < length; i++ { - sb.WriteByte(charset[rand.Intn(len(charset))]) - } - return sb.String() -} - func TestRealisticGroupSerialization(t *testing.T) { jsonString := `{ "active": "true", @@ -82,7 +72,7 @@ func TestUnmarshalUsers(t *testing.T) { "userType": "User", "website": "", "zip": "" - }]` + }]` users, err := unmarshalUsersJson([]byte(jsonString)) if err != nil { @@ -136,3 +126,14 @@ func TestGroupSerialization(t *testing.T) { t.Error(err) } } + +const charset = "abcdefghijklmnopqrstuvwxyzæøåABCDEFGHIJKLMNOPQRSTUVWXYZÆØÅ-. " + +func randomString(length int) string { + var sb strings.Builder + runes := []rune(charset) + for i := 0; i < length; i++ { + sb.WriteRune(runes[rand.Intn(len(runes))]) + } + return sb.String() +} From 48d1baab5f5bee0b6e82ee5872c0e806cdb746f9 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Mon, 25 Nov 2024 12:51:50 +0100 Subject: [PATCH 42/52] refactor: cloudian sdk, do not export http client --- internal/sdk/cloudian/sdk.go | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 0b3256a0..c109ffe2 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -12,7 +12,7 @@ import ( type Client struct { baseURL string - HTTPClient *http.Client + httpClient *http.Client token string } @@ -41,7 +41,7 @@ type User struct { func NewClient(baseUrl string, tokenBase64 string) *Client { return &Client{ baseURL: baseUrl, - HTTPClient: &http.Client{}, + httpClient: &http.Client{}, token: tokenBase64, } } @@ -64,7 +64,7 @@ func (client Client) ListUsers(ctx context.Context, groupId string, offsetUserId return nil, fmt.Errorf("GET error creating list request: %w", err) } - resp, err := client.HTTPClient.Do(req) + resp, err := client.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("GET list users failed: %w", err) @@ -109,7 +109,7 @@ func (client Client) DeleteUser(ctx context.Context, user User) error { return fmt.Errorf("DELETE error creating request: %w", err) } - resp, err := client.HTTPClient.Do(req) + resp, err := client.httpClient.Do(req) if err != nil { return fmt.Errorf("DELETE to cloudian /user got: %w", err) @@ -146,7 +146,7 @@ func (client Client) DeleteGroup(ctx context.Context, groupId string) error { return fmt.Errorf("error creating request: %w", err) } - 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) @@ -170,7 +170,7 @@ func (client Client) CreateGroup(ctx context.Context, group Group) error { return fmt.Errorf("error creating request: %w", err) } - resp, err := client.HTTPClient.Do(req) + resp, err := client.httpClient.Do(req) if err != nil { return fmt.Errorf("POST to cloudian /group: %w", err) @@ -196,7 +196,7 @@ func (client Client) UpdateGroup(ctx context.Context, group Group) error { return fmt.Errorf("error creating request: %w", err) } - resp, err := client.HTTPClient.Do(req) + resp, err := client.httpClient.Do(req) if err != nil { return fmt.Errorf("PUT to cloudian /group: %w", err) @@ -215,7 +215,7 @@ func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, erro return nil, err } - resp, err := client.HTTPClient.Do(req) + resp, err := client.httpClient.Do(req) if err != nil { return nil, fmt.Errorf("GET error: %w", err) From 7dff0353d7d4a865eb3d18190cd679999033906c Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Mon, 25 Nov 2024 12:53:30 +0100 Subject: [PATCH 43/52] fix: cloudian sdk casing fix --- internal/sdk/cloudian/sdk.go | 6 +++--- internal/sdk/cloudian/sdk_test.go | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index c109ffe2..3d7156c0 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -27,9 +27,9 @@ type Group struct { LDAPSearchUserBase string `json:"ldapSearchUserBase"` LDAPServerURL string `json:"ldapServerURL"` LDAPUserDNTemplate string `json:"ldapUserDNTemplate"` - S3endpointsHTTP []string `json:"s3endpointshttp"` - S3endpointsHTTPS []string `json:"s3endpointshttps"` - S3websiteendpoints []string `json:"s3websiteendpoints"` + S3EndpointsHTTP []string `json:"s3endpointshttp"` + S3EndpointsHTTPS []string `json:"s3endpointshttps"` + S3WebSiteEndpoints []string `json:"s3websiteendpoints"` } type User struct { diff --git a/internal/sdk/cloudian/sdk_test.go b/internal/sdk/cloudian/sdk_test.go index dffca41d..540255c4 100644 --- a/internal/sdk/cloudian/sdk_test.go +++ b/internal/sdk/cloudian/sdk_test.go @@ -101,9 +101,9 @@ func (group Group) Generate(rand *rand.Rand, size int) reflect.Value { LDAPSearchUserBase: "", LDAPServerURL: "", LDAPUserDNTemplate: "", - S3endpointsHTTP: []string{"ALL"}, - S3endpointsHTTPS: []string{"ALL"}, - S3websiteendpoints: []string{"ALL"}, + S3EndpointsHTTP: []string{"ALL"}, + S3EndpointsHTTPS: []string{"ALL"}, + S3WebSiteEndpoints: []string{"ALL"}, }) } From 6161a7b3b51c85335a96c9841bb683409c6428a6 Mon Sep 17 00:00:00 2001 From: Sjur Date: Mon, 25 Nov 2024 13:06:21 +0100 Subject: [PATCH 44/52] Update internal/sdk/cloudian/sdk.go var assignment best practice Co-authored-by: Amund Tenstad --- internal/sdk/cloudian/sdk.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 3d7156c0..9f84ca28 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -52,7 +52,7 @@ func (client Client) ListUsers(ctx context.Context, groupId string, offsetUserId limit := 100 - var offsetQueryParam = "" + offsetQueryParam := "" if offsetUserId != nil { offsetQueryParam = "&offset=" + *offsetUserId } From ce7ece4353825ea98e3a2879c8394663d8ffee64 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Mon, 25 Nov 2024 13:10:31 +0100 Subject: [PATCH 45/52] chore: move errorcheck closer to soursce --- internal/sdk/cloudian/sdk.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 9f84ca28..280eb854 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -65,10 +65,10 @@ func (client Client) ListUsers(ctx context.Context, groupId string, offsetUserId } resp, err := client.httpClient.Do(req) - if err != nil { return nil, fmt.Errorf("GET list users failed: %w", err) } + defer resp.Body.Close() // nolint:errcheck body, err := io.ReadAll(resp.Body) @@ -110,10 +110,10 @@ func (client Client) DeleteUser(ctx context.Context, user User) error { } resp, err := client.httpClient.Do(req) - if err != nil { return fmt.Errorf("DELETE to cloudian /user got: %w", err) } + defer resp.Body.Close() // nolint:errcheck return nil @@ -147,10 +147,10 @@ func (client Client) DeleteGroup(ctx context.Context, groupId string) error { } 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 return nil @@ -171,10 +171,10 @@ func (client Client) CreateGroup(ctx context.Context, group Group) error { } resp, err := client.httpClient.Do(req) - if err != nil { return fmt.Errorf("POST to cloudian /group: %w", err) } + defer resp.Body.Close() // nolint:errcheck return nil @@ -197,10 +197,10 @@ func (client Client) UpdateGroup(ctx context.Context, group Group) error { } resp, err := client.httpClient.Do(req) - if err != nil { return fmt.Errorf("PUT to cloudian /group: %w", err) } + defer resp.Body.Close() // nolint:errcheck return nil @@ -216,10 +216,10 @@ func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, erro } resp, err := client.httpClient.Do(req) - if err != nil { return nil, fmt.Errorf("GET error: %w", err) } + defer resp.Body.Close() // nolint:errcheck switch resp.StatusCode { From 9944c4b5542f6ba8245e0d815d3a29f92b44ca85 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Mon, 25 Nov 2024 13:11:41 +0100 Subject: [PATCH 46/52] chore: newline --- internal/sdk/cloudian/sdk.go | 1 - 1 file changed, 1 deletion(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 280eb854..8cd82752 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -182,7 +182,6 @@ func (client Client) CreateGroup(ctx context.Context, group Group) error { // Updates a group if it does not exists. func (client Client) UpdateGroup(ctx context.Context, group Group) error { - url := client.baseURL + "/group" jsonData, err := marshalGroup(group) From d586aa461998c8b9e13d227e97fcf981c482cc3b Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Mon, 25 Nov 2024 13:23:32 +0100 Subject: [PATCH 47/52] refactor: remove redundant marshalling util functions --- internal/sdk/cloudian/sdk.go | 26 ++++++-------------------- internal/sdk/cloudian/sdk_test.go | 12 ++++++++---- 2 files changed, 14 insertions(+), 24 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 8cd82752..0d03cbb5 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -76,7 +76,8 @@ func (client Client) ListUsers(ctx context.Context, groupId string, offsetUserId return nil, fmt.Errorf("GET reading list users response body failed: %w", err) } - users, err := unmarshalUsersJson(body) + var users []User + err = json.Unmarshal(body, &users) if err != nil { return nil, fmt.Errorf("GET unmarshal users response body failed: %w", err) } @@ -160,7 +161,7 @@ func (client Client) DeleteGroup(ctx context.Context, groupId string) error { func (client Client) CreateGroup(ctx context.Context, group Group) error { url := client.baseURL + "/group" - jsonData, err := marshalGroup(group) + jsonData, err := json.Marshal(group) if err != nil { return fmt.Errorf("error marshaling JSON: %w", err) } @@ -184,7 +185,7 @@ func (client Client) CreateGroup(ctx context.Context, group Group) error { func (client Client) UpdateGroup(ctx context.Context, group Group) error { url := client.baseURL + "/group" - jsonData, err := marshalGroup(group) + jsonData, err := json.Marshal(group) if err != nil { return fmt.Errorf("error marshaling JSON: %w", err) } @@ -228,7 +229,8 @@ func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, erro return nil, fmt.Errorf("GET reading response body failed: %w", err) } - group, err := unmarshalGroupJson(body) + var group Group + err = json.Unmarshal(body, &group) if err != nil { return nil, fmt.Errorf("GET unmarshal response body failed: %w", err) } @@ -253,19 +255,3 @@ func (client Client) newRequest(ctx context.Context, url string, method string, return req, nil } - -func marshalGroup(group Group) ([]byte, error) { - return json.Marshal(group) -} - -func unmarshalGroupJson(data []byte) (Group, error) { - var group Group - err := json.Unmarshal(data, &group) - return group, err -} - -func unmarshalUsersJson(data []byte) ([]User, error) { - var users []User - err := json.Unmarshal(data, &users) - return users, err -} diff --git a/internal/sdk/cloudian/sdk_test.go b/internal/sdk/cloudian/sdk_test.go index 540255c4..3c8abb88 100644 --- a/internal/sdk/cloudian/sdk_test.go +++ b/internal/sdk/cloudian/sdk_test.go @@ -1,6 +1,7 @@ package cloudian import ( + "encoding/json" "math/rand" "reflect" "strings" @@ -25,7 +26,8 @@ func TestRealisticGroupSerialization(t *testing.T) { "s3websiteendpoints": ["ALL"] }` - group, err := unmarshalGroupJson([]byte(jsonString)) + var group Group + err := json.Unmarshal([]byte(jsonString), &group) if err != nil { t.Errorf("Error deserializing from JSON: %v", err) } @@ -74,7 +76,8 @@ func TestUnmarshalUsers(t *testing.T) { "zip": "" }]` - users, err := unmarshalUsersJson([]byte(jsonString)) + var users []User + err := json.Unmarshal([]byte(jsonString), &users) if err != nil { t.Errorf("Error deserializing users from JSON: %v", err) } @@ -109,12 +112,13 @@ func (group Group) Generate(rand *rand.Rand, size int) reflect.Value { func TestGroupSerialization(t *testing.T) { f := func(group Group) bool { - data, err := marshalGroup(group) + data, err := json.Marshal(group) if err != nil { return false } - deserialized, err := unmarshalGroupJson(data) + var deserialized Group + err = json.Unmarshal(data, &deserialized) if err != nil { return false } From 0b89ad852c2769c8689a7f3790780f56b8305e6c Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Mon, 25 Nov 2024 13:39:34 +0100 Subject: [PATCH 48/52] refactor: cloudian sdk introduce custom GroupNotFoundError --- internal/sdk/cloudian/sdk.go | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 0d03cbb5..edd159a2 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -32,6 +32,14 @@ type Group struct { S3WebSiteEndpoints []string `json:"s3websiteendpoints"` } +type GroupNotFoundError struct { + groupId string +} + +func (e GroupNotFoundError) Error() string { + return fmt.Sprintf("group %s not found", e.groupId) +} + type User struct { UserID string `json:"userId"` GroupID string `json:"groupId"` @@ -206,7 +214,7 @@ func (client Client) UpdateGroup(ctx context.Context, group Group) error { return nil } -// Get a group. Returns nil if the group does not exist. +// Get a group. Returns a `GroupNotFoundError` with the groupId if the group does not exist. func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, error) { url := client.baseURL + "/group?groupId=" + groupId @@ -238,7 +246,7 @@ func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, erro return &group, nil case 204: // Cloudian-API returns 204 if the group does not exist - return nil, nil + return nil, GroupNotFoundError{groupId: groupId} default: return nil, fmt.Errorf("GET unexpected status. Failure: %w", err) } From 97aef550944af332a503deb7009f5650fcd0d089 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Tue, 26 Nov 2024 08:58:39 +0100 Subject: [PATCH 49/52] feat: Add exposed ResponseReason enum and util method to check it --- internal/sdk/cloudian/sdk.go | 36 ++++++++++++++++++-------- internal/sdk/cloudian/sdk_test.go | 42 +++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+), 10 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index edd159a2..13197814 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -4,6 +4,7 @@ import ( "bytes" "context" "encoding/json" + "errors" "fmt" "io" "net/http" @@ -32,20 +33,19 @@ type Group struct { S3WebSiteEndpoints []string `json:"s3websiteendpoints"` } -type GroupNotFoundError struct { - groupId string -} - -func (e GroupNotFoundError) Error() string { - return fmt.Sprintf("group %s not found", e.groupId) -} - type User struct { UserID string `json:"userId"` GroupID string `json:"groupId"` CanonicalUserID string `json:"canonicalUserId"` } +type ResponseReason int + +const ( + NotFound ResponseReason = iota + Unknown +) + func NewClient(baseUrl string, tokenBase64 string) *Client { return &Client{ baseURL: baseUrl, @@ -214,7 +214,8 @@ func (client Client) UpdateGroup(ctx context.Context, group Group) error { return nil } -// Get a group. Returns a `GroupNotFoundError` with the groupId if the group does not exist. +// Get a group. Returns an error even in the case of a group not found. +// This error can be checked for an enum value `ResponseReason` using `CheckResponseReason` func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, error) { url := client.baseURL + "/group?groupId=" + groupId @@ -246,13 +247,22 @@ func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, erro return &group, nil case 204: // Cloudian-API returns 204 if the group does not exist - return nil, GroupNotFoundError{groupId: groupId} + return nil, groupNotFoundError{} default: return nil, fmt.Errorf("GET unexpected status. Failure: %w", err) } } +func CheckResponseReason(err error) ResponseReason { + var targetErr groupNotFoundError + + if errors.As(err, &targetErr) { + return NotFound + } + return Unknown +} + 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 { @@ -263,3 +273,9 @@ func (client Client) newRequest(ctx context.Context, url string, method string, return req, nil } + +type groupNotFoundError struct{} + +func (e groupNotFoundError) Error() string { + return "group not found" +} diff --git a/internal/sdk/cloudian/sdk_test.go b/internal/sdk/cloudian/sdk_test.go index 3c8abb88..06a4cf90 100644 --- a/internal/sdk/cloudian/sdk_test.go +++ b/internal/sdk/cloudian/sdk_test.go @@ -2,6 +2,8 @@ package cloudian import ( "encoding/json" + "errors" + "fmt" "math/rand" "reflect" "strings" @@ -110,6 +112,46 @@ func (group Group) Generate(rand *rand.Rand, size int) reflect.Value { }) } +func TestGenericError(t *testing.T) { + e := errors.New("Random failure") + + switch CheckResponseReason(e) { + case NotFound: + t.Error("Expected NotFound") + case Unknown: // Expected + } +} + +func TestWrappedGrouGenericError(t *testing.T) { + e := fmt.Errorf("wrap it: %w", errors.New("Random failure")) + + switch CheckResponseReason(e) { + case NotFound: + t.Error("Expected NotFound") + case Unknown: // Expected + } +} + +func TestGroupNotFoundError(t *testing.T) { + e := groupNotFoundError{} + + switch CheckResponseReason(e) { + case NotFound: // Expected + case Unknown: + t.Error("Expected NotFound") + } +} + +func TestWrappedGroupNotFoundError(t *testing.T) { + e := fmt.Errorf("wrap it: %w", groupNotFoundError{}) + + switch CheckResponseReason(e) { + case NotFound: // Expected + case Unknown: + t.Error("Expected NotFound") + } +} + func TestGroupSerialization(t *testing.T) { f := func(group Group) bool { data, err := json.Marshal(group) From 2673e9bbd1b516df4d895bf64c6d1f2763a9abb0 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Tue, 26 Nov 2024 10:50:11 +0100 Subject: [PATCH 50/52] refactor: cloudian sdk expose ErrNotFound to callsite --- internal/sdk/cloudian/sdk.go | 26 +++------------------ internal/sdk/cloudian/sdk_test.go | 38 ++++++------------------------- 2 files changed, 10 insertions(+), 54 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 13197814..7ee87817 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -39,12 +39,7 @@ type User struct { CanonicalUserID string `json:"canonicalUserId"` } -type ResponseReason int - -const ( - NotFound ResponseReason = iota - Unknown -) +var ErrNotFound = errors.New("not found") func NewClient(baseUrl string, tokenBase64 string) *Client { return &Client{ @@ -215,7 +210,7 @@ func (client Client) UpdateGroup(ctx context.Context, group Group) error { } // Get a group. Returns an error even in the case of a group not found. -// This error can be checked for an enum value `ResponseReason` using `CheckResponseReason` +// This error can then be checked against ErrNotFound: errors.Is(err, ErrNotFound) func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, error) { url := client.baseURL + "/group?groupId=" + groupId @@ -247,22 +242,13 @@ func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, erro return &group, nil case 204: // Cloudian-API returns 204 if the group does not exist - return nil, groupNotFoundError{} + return nil, ErrNotFound default: return nil, fmt.Errorf("GET unexpected status. Failure: %w", err) } } -func CheckResponseReason(err error) ResponseReason { - var targetErr groupNotFoundError - - if errors.As(err, &targetErr) { - return NotFound - } - return Unknown -} - 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 { @@ -273,9 +259,3 @@ func (client Client) newRequest(ctx context.Context, url string, method string, return req, nil } - -type groupNotFoundError struct{} - -func (e groupNotFoundError) Error() string { - return "group not found" -} diff --git a/internal/sdk/cloudian/sdk_test.go b/internal/sdk/cloudian/sdk_test.go index 06a4cf90..c231e109 100644 --- a/internal/sdk/cloudian/sdk_test.go +++ b/internal/sdk/cloudian/sdk_test.go @@ -113,42 +113,18 @@ func (group Group) Generate(rand *rand.Rand, size int) reflect.Value { } func TestGenericError(t *testing.T) { - e := errors.New("Random failure") + err := errors.New("Random failure") - switch CheckResponseReason(e) { - case NotFound: - t.Error("Expected NotFound") - case Unknown: // Expected + if errors.Is(err, ErrNotFound) { + t.Error("Expected not to be ErrNotFound") } } -func TestWrappedGrouGenericError(t *testing.T) { - e := fmt.Errorf("wrap it: %w", errors.New("Random failure")) +func TestWrappedErrNotFound(t *testing.T) { + err := fmt.Errorf("wrap it: %w", ErrNotFound) - switch CheckResponseReason(e) { - case NotFound: - t.Error("Expected NotFound") - case Unknown: // Expected - } -} - -func TestGroupNotFoundError(t *testing.T) { - e := groupNotFoundError{} - - switch CheckResponseReason(e) { - case NotFound: // Expected - case Unknown: - t.Error("Expected NotFound") - } -} - -func TestWrappedGroupNotFoundError(t *testing.T) { - e := fmt.Errorf("wrap it: %w", groupNotFoundError{}) - - switch CheckResponseReason(e) { - case NotFound: // Expected - case Unknown: - t.Error("Expected NotFound") + if !errors.Is(err, ErrNotFound) { + t.Error("Expected to be ErrNotFound") } } From a5ae747203dbc1041de3d152dbe115f352ada5f2 Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Tue, 26 Nov 2024 12:25:43 +0100 Subject: [PATCH 51/52] fix: cloudian sdk pair programming fixes --- internal/sdk/cloudian/sdk.go | 72 +++++++++++++++++++----------------- 1 file changed, 38 insertions(+), 34 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index 7ee87817..dc5b1f88 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -3,6 +3,7 @@ package cloudian import ( "bytes" "context" + "crypto/tls" "encoding/json" "errors" "fmt" @@ -34,18 +35,19 @@ type Group struct { } type User struct { - UserID string `json:"userId"` - GroupID string `json:"groupId"` - CanonicalUserID string `json:"canonicalUserId"` + UserID string `json:"userId"` + GroupID string `json:"groupId"` } var ErrNotFound = errors.New("not found") -func NewClient(baseUrl string, tokenBase64 string) *Client { +func NewClient(baseUrl string, tlsInsecureSkipVerify bool, tokenBase64 string) *Client { return &Client{ - baseURL: baseUrl, - httpClient: &http.Client{}, - token: tokenBase64, + baseURL: baseUrl, + httpClient: &http.Client{Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: tlsInsecureSkipVerify}, + }}, + token: tokenBase64, } } @@ -62,7 +64,7 @@ func (client Client) ListUsers(ctx context.Context, groupId string, offsetUserId url := client.baseURL + "/user/list?groupId=" + groupId + "&userType=all&userStatus=all&limit=" + strconv.Itoa(limit) + offsetQueryParam - req, err := client.newRequest(ctx, http.MethodGet, url, nil) + req, err := client.newRequest(ctx, url, http.MethodGet, nil) if err != nil { return nil, fmt.Errorf("GET error creating list request: %w", err) } @@ -80,8 +82,7 @@ func (client Client) ListUsers(ctx context.Context, groupId string, offsetUserId } var users []User - err = json.Unmarshal(body, &users) - if err != nil { + if err := json.Unmarshal(body, &users); err != nil { return nil, fmt.Errorf("GET unmarshal users response body failed: %w", err) } @@ -89,14 +90,15 @@ func (client Client) ListUsers(ctx context.Context, groupId string, offsetUserId // 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 { + retVal = retVal[0 : len(retVal)-1] // Remove the last element, which is the offset // 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) + // that UserId is the correct key for this parameter // Fetch more results moreUsers, err := client.ListUsers(ctx, groupId, &users[limit].UserID) - if err != nil { return nil, fmt.Errorf("GET list users failed: %w", err) } + retVal = append(retVal, moreUsers...) } @@ -106,7 +108,7 @@ func (client Client) ListUsers(ctx context.Context, groupId string, offsetUserId // Delete a single user. Errors if the user does not exist. func (client Client) DeleteUser(ctx context.Context, user User) error { - url := client.baseURL + "/user?userId=" + user.UserID + "&groupId=" + user.GroupID + "&canonicalUserId=" + user.CanonicalUserID + url := client.baseURL + "/user?userId=" + user.UserID + "&groupId=" + user.GroupID req, err := client.newRequest(ctx, url, http.MethodDelete, nil) if err != nil { @@ -114,28 +116,33 @@ func (client Client) DeleteUser(ctx context.Context, user User) error { } resp, err := client.httpClient.Do(req) + if err != nil { return fmt.Errorf("DELETE to cloudian /user got: %w", err) } + defer resp.Body.Close() - defer resp.Body.Close() // nolint:errcheck + switch resp.StatusCode { + case 200: + return nil + default: + return fmt.Errorf("DELETE unexpected status. Failure: %d", resp.StatusCode) + } - return nil } // Delete a group and all its members. func (client Client) DeleteGroupRecursive(ctx context.Context, groupId string) error { users, err := client.ListUsers(ctx, groupId, nil) + if err != nil { return fmt.Errorf("error listing users: %w", err) } for _, user := range users { - err := client.DeleteUser(ctx, user) - if err != nil { + if err := client.DeleteUser(ctx, user); err != nil { return fmt.Errorf("error deleting user: %w", err) } - } return client.DeleteGroup(ctx, groupId) @@ -155,9 +162,7 @@ func (client Client) DeleteGroup(ctx context.Context, groupId string) error { return fmt.Errorf("DELETE to cloudian /group got: %w", err) } - defer resp.Body.Close() // nolint:errcheck - - return nil + return resp.Body.Close() } // Creates a group. @@ -169,7 +174,7 @@ func (client Client) CreateGroup(ctx context.Context, group Group) error { return fmt.Errorf("error marshaling JSON: %w", err) } - req, err := client.newRequest(ctx, url, http.MethodPost, &jsonData) + req, err := client.newRequest(ctx, url, http.MethodPost, jsonData) if err != nil { return fmt.Errorf("error creating request: %w", err) } @@ -179,9 +184,7 @@ func (client Client) CreateGroup(ctx context.Context, group Group) error { return fmt.Errorf("POST to cloudian /group: %w", err) } - defer resp.Body.Close() // nolint:errcheck - - return nil + return resp.Body.Close() } // Updates a group if it does not exists. @@ -194,7 +197,7 @@ func (client Client) UpdateGroup(ctx context.Context, group Group) error { } // Create a context with a timeout - req, err := client.newRequest(ctx, url, http.MethodPut, &jsonData) + req, err := client.newRequest(ctx, url, http.MethodPut, jsonData) if err != nil { return fmt.Errorf("error creating request: %w", err) } @@ -204,9 +207,7 @@ func (client Client) UpdateGroup(ctx context.Context, group Group) error { return fmt.Errorf("PUT to cloudian /group: %w", err) } - defer resp.Body.Close() // nolint:errcheck - - return nil + return resp.Body.Close() } // Get a group. Returns an error even in the case of a group not found. @@ -234,8 +235,7 @@ func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, erro } var group Group - err = json.Unmarshal(body, &group) - if err != nil { + if err := json.Unmarshal(body, &group); err != nil { return nil, fmt.Errorf("GET unmarshal response body failed: %w", err) } @@ -246,14 +246,18 @@ func (client Client) GetGroup(ctx context.Context, groupId string) (*Group, erro default: return nil, fmt.Errorf("GET unexpected status. Failure: %w", err) } - } -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)) +func (client Client) newRequest(ctx context.Context, url string, method string, body []byte) (*http.Request, error) { + var buffer io.Reader = nil + if body != nil { + buffer = bytes.NewBuffer(body) + } + req, err := http.NewRequestWithContext(ctx, method, url, buffer) if err != nil { return req, fmt.Errorf("error creating request: %w", err) } + req.Header.Set("Accept", "application/json") req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Basic "+client.token) From fd49cd1d20dde814fb6cb89439d54ce84a90d7be Mon Sep 17 00:00:00 2001 From: Sjur Millidahl Date: Tue, 26 Nov 2024 13:22:06 +0100 Subject: [PATCH 52/52] fix: lint --- internal/sdk/cloudian/sdk.go | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/internal/sdk/cloudian/sdk.go b/internal/sdk/cloudian/sdk.go index dc5b1f88..fae14f0d 100644 --- a/internal/sdk/cloudian/sdk.go +++ b/internal/sdk/cloudian/sdk.go @@ -45,7 +45,7 @@ func NewClient(baseUrl string, tlsInsecureSkipVerify bool, tokenBase64 string) * return &Client{ baseURL: baseUrl, httpClient: &http.Client{Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: tlsInsecureSkipVerify}, + TLSClientConfig: &tls.Config{InsecureSkipVerify: tlsInsecureSkipVerify}, // nolint:gosec }}, token: tokenBase64, } @@ -116,11 +116,10 @@ func (client Client) DeleteUser(ctx context.Context, user User) error { } resp, err := client.httpClient.Do(req) - if err != nil { return fmt.Errorf("DELETE to cloudian /user got: %w", err) } - defer resp.Body.Close() + defer resp.Body.Close() // nolint:errcheck switch resp.StatusCode { case 200: