From 703667377b2c771e72a495d76962b5b792295e90 Mon Sep 17 00:00:00 2001 From: Matt Dzwonczyk <9063128+mattgd@users.noreply.github.com> Date: Thu, 2 Jan 2025 09:09:26 -0500 Subject: [PATCH] Add GET /organization/:orgId/roles support. (#388) --- pkg/organizations/client.go | 50 +++++++++++ pkg/organizations/client_test.go | 108 ++++++++++++++++++++++++ pkg/organizations/organizations.go | 8 ++ pkg/organizations/organizations_test.go | 42 +++++++++ pkg/roles/README.md | 15 ++++ pkg/roles/client.go | 32 +++++++ 6 files changed, 255 insertions(+) create mode 100644 pkg/roles/README.md create mode 100644 pkg/roles/client.go diff --git a/pkg/organizations/client.go b/pkg/organizations/client.go index 6cad3760..6ddb817a 100644 --- a/pkg/organizations/client.go +++ b/pkg/organizations/client.go @@ -9,6 +9,7 @@ import ( "sync" "time" + "github.com/workos/workos-go/v4/pkg/roles" "github.com/workos/workos-go/v4/pkg/workos_errors" "github.com/google/go-querystring/query" @@ -217,6 +218,17 @@ type UpdateOrganizationOpts struct { DomainData []OrganizationDomainData `json:"domain_data"` } +// ListOrganizationsOpts contains the options to request Organizations. +type ListOrganizationRolesOpts struct { + // The Organization's unique identifier. + OrganizationID string +} + +type ListOrganizationRolesResponse struct { + // List of roles for the given organization. + Data []roles.Role `json:"data"` +} + // GetOrganization gets an Organization. func (c *Client) GetOrganization( ctx context.Context, @@ -443,3 +455,41 @@ func (c *Client) DeleteOrganization( return workos_errors.TryGetHTTPError(res) } + +// ListOrganizationRoles gets a list of roles for the given organization. +func (c *Client) ListOrganizationRoles( + ctx context.Context, + opts ListOrganizationRolesOpts, +) (ListOrganizationRolesResponse, error) { + c.once.Do(c.init) + + endpoint := fmt.Sprintf("%s/organizations/%s/roles", c.Endpoint, opts.OrganizationID) + req, err := http.NewRequest( + http.MethodGet, + endpoint, + nil, + ) + if err != nil { + return ListOrganizationRolesResponse{}, err + } + + req = req.WithContext(ctx) + req.Header.Set("Authorization", "Bearer "+c.APIKey) + req.Header.Set("Content-Type", "application/json") + req.Header.Set("User-Agent", "workos-go/"+workos.Version) + + res, err := c.HTTPClient.Do(req) + if err != nil { + return ListOrganizationRolesResponse{}, err + } + defer res.Body.Close() + + if err = workos_errors.TryGetHTTPError(res); err != nil { + return ListOrganizationRolesResponse{}, err + } + + var body ListOrganizationRolesResponse + dec := json.NewDecoder(res.Body) + err = dec.Decode(&body) + return body, err +} diff --git a/pkg/organizations/client_test.go b/pkg/organizations/client_test.go index 400cf3b4..1da6360a 100644 --- a/pkg/organizations/client_test.go +++ b/pkg/organizations/client_test.go @@ -10,6 +10,7 @@ import ( "github.com/stretchr/testify/require" "github.com/workos/workos-go/v4/pkg/common" + "github.com/workos/workos-go/v4/pkg/roles" ) func TestGetOrganization(t *testing.T) { @@ -542,3 +543,110 @@ func updateOrganizationTestHandler(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) w.Write(body) } + +func TestListOrganizationRoles(t *testing.T) { + tests := []struct { + scenario string + client *Client + options ListOrganizationRolesOpts + expected ListOrganizationRolesResponse + err bool + }{ + { + scenario: "Request without API Key returns an error", + client: &Client{}, + err: true, + }, + { + scenario: "Request returns list of roles", + client: &Client{ + APIKey: "test", + }, + options: ListOrganizationRolesOpts{ + OrganizationID: "organization_id", + }, + expected: ListOrganizationRolesResponse{ + Data: []roles.Role{ + { + ID: "role_01EHWNCE74X7JSDV0X3SZ3KJNY", + Name: "Member", + Slug: "member", + Description: "The default role for all users.", + Type: roles.Environment, + CreatedAt: "2024-12-01T00:00:00.000Z", + UpdatedAt: "2024-12-01T00:00:00.000Z", + }, + { + ID: "role_01EHWNCE74X7JSDV0X3SZ3KJSE", + Name: "Org. Member", + Slug: "org-member", + Description: "The default role for org. members.", + Type: roles.Organization, + CreatedAt: "2024-12-02T00:00:00.000Z", + UpdatedAt: "2024-12-02T00:00:00.000Z", + }, + }, + }, + }, + } + + for _, test := range tests { + t.Run(test.scenario, func(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(listOrganizationRolesTestHandler)) + defer server.Close() + + client := test.client + client.Endpoint = server.URL + client.HTTPClient = server.Client() + + response, err := client.ListOrganizationRoles(context.Background(), test.options) + if test.err { + require.Error(t, err) + return + } + require.NoError(t, err) + require.Equal(t, test.expected, response) + }) + } +} + +func listOrganizationRolesTestHandler(w http.ResponseWriter, r *http.Request) { + auth := r.Header.Get("Authorization") + if auth != "Bearer test" { + http.Error(w, "bad auth", http.StatusUnauthorized) + return + } + + body, err := json.Marshal(struct { + ListOrganizationRolesResponse + }{ListOrganizationRolesResponse{ + Data: []roles.Role{ + { + ID: "role_01EHWNCE74X7JSDV0X3SZ3KJNY", + Name: "Member", + Slug: "member", + Description: "The default role for all users.", + Type: roles.Environment, + CreatedAt: "2024-12-01T00:00:00.000Z", + UpdatedAt: "2024-12-01T00:00:00.000Z", + }, + { + ID: "role_01EHWNCE74X7JSDV0X3SZ3KJSE", + Name: "Org. Member", + Slug: "org-member", + Description: "The default role for org. members.", + Type: roles.Organization, + CreatedAt: "2024-12-02T00:00:00.000Z", + UpdatedAt: "2024-12-02T00:00:00.000Z", + }, + }, + }}) + + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + + w.WriteHeader(http.StatusOK) + w.Write(body) +} diff --git a/pkg/organizations/organizations.go b/pkg/organizations/organizations.go index 3b7d8d63..505683e1 100644 --- a/pkg/organizations/organizations.go +++ b/pkg/organizations/organizations.go @@ -56,3 +56,11 @@ func DeleteOrganization( ) error { return DefaultClient.DeleteOrganization(ctx, opts) } + +// ListOrganizationRoles lists roles for an Organization. +func ListOrganizationRoles( + ctx context.Context, + opts ListOrganizationRolesOpts, +) (ListOrganizationRolesResponse, error) { + return DefaultClient.ListOrganizationRoles(ctx, opts) +} diff --git a/pkg/organizations/organizations_test.go b/pkg/organizations/organizations_test.go index 09da018e..0fac0f9f 100644 --- a/pkg/organizations/organizations_test.go +++ b/pkg/organizations/organizations_test.go @@ -8,6 +8,7 @@ import ( "github.com/stretchr/testify/require" "github.com/workos/workos-go/v4/pkg/common" + "github.com/workos/workos-go/v4/pkg/roles" ) func TestOrganizationsGetOrganization(t *testing.T) { @@ -156,3 +157,44 @@ func TestOrganizationsUpdateOrganization(t *testing.T) { require.NoError(t, err) require.Equal(t, expectedResponse, organization) } + +func TestOrganizationsListOrganizationRoles(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(listOrganizationRolesTestHandler)) + defer server.Close() + + DefaultClient = &Client{ + HTTPClient: server.Client(), + Endpoint: server.URL, + } + SetAPIKey("test") + + expectedResponse := ListOrganizationRolesResponse{ + Data: []roles.Role{ + { + ID: "role_01EHWNCE74X7JSDV0X3SZ3KJNY", + Name: "Member", + Slug: "member", + Description: "The default role for all users.", + Type: roles.Environment, + CreatedAt: "2024-12-01T00:00:00.000Z", + UpdatedAt: "2024-12-01T00:00:00.000Z", + }, + { + ID: "role_01EHWNCE74X7JSDV0X3SZ3KJSE", + Name: "Org. Member", + Slug: "org-member", + Description: "The default role for org. members.", + Type: roles.Organization, + CreatedAt: "2024-12-02T00:00:00.000Z", + UpdatedAt: "2024-12-02T00:00:00.000Z", + }, + }, + } + + rolesResponse, err := ListOrganizationRoles(context.Background(), ListOrganizationRolesOpts{ + OrganizationID: "organization_id", + }) + + require.NoError(t, err) + require.Equal(t, expectedResponse, rolesResponse) +} diff --git a/pkg/roles/README.md b/pkg/roles/README.md new file mode 100644 index 00000000..733a4719 --- /dev/null +++ b/pkg/roles/README.md @@ -0,0 +1,15 @@ +# roles + +[![Go Report Card](https://img.shields.io/badge/dev-reference-007d9c?logo=go&logoColor=white&style=flat)](https://pkg.go.dev/github.com/workos/workos-go/v4/pkg/roles) + +A Go package to make requests to the WorkOS Roles API. + +## Install + +```sh +go get -u github.com/workos/workos-go/v4/pkg/roles +``` + +## How it works + +See the [Roles API reference](https://workos.com/docs/reference/roles). diff --git a/pkg/roles/client.go b/pkg/roles/client.go new file mode 100644 index 00000000..a600bea6 --- /dev/null +++ b/pkg/roles/client.go @@ -0,0 +1,32 @@ +package roles + +// RoleType represents the type of a Role. +type RoleType string + +// Constants that enumerate the type of a Role. +const ( + Environment RoleType = "EnvironmentRole" + Organization RoleType = "OrganizationRole" +) + +// Role contains data about a WorkOS Role. +type Role struct { + // The Role's unique identifier. + ID string `json:"id"` + + Name string `json:"name"` + + // The Role's slug key for referencing it in code. + Slug string `json:"slug"` + + Description string `json:"description"` + + // The type of role + Type RoleType `json:"type"` + + // The timestamp of when the Role was created. + CreatedAt string `json:"created_at"` + + // The timestamp of when the Role was updated. + UpdatedAt string `json:"updated_at"` +}