Skip to content

Commit aa6e6e3

Browse files
authored
chore: implement fetch all organizations endpoint (coder#13941)
* chore: implement fetch all organizations endpoint * update ui to use list all orgs
1 parent 6f20a64 commit aa6e6e3

File tree

11 files changed

+169
-18
lines changed

11 files changed

+169
-18
lines changed

coderd/apidoc/docs.go

Lines changed: 26 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

Lines changed: 22 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/coderd.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -865,6 +865,7 @@ func New(options *Options) *API {
865865
apiKeyMiddleware,
866866
)
867867
r.Post("/", api.postOrganizations)
868+
r.Get("/", api.organizations)
868869
r.Route("/{organization}", func(r chi.Router) {
869870
r.Use(
870871
httpmw.ExtractOrganizationParam(options.Database),

coderd/organizations.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,38 @@ import (
1111

1212
"github.com/coder/coder/v2/coderd/audit"
1313
"github.com/coder/coder/v2/coderd/database"
14+
"github.com/coder/coder/v2/coderd/database/db2sdk"
1415
"github.com/coder/coder/v2/coderd/database/dbtime"
1516
"github.com/coder/coder/v2/coderd/httpapi"
1617
"github.com/coder/coder/v2/coderd/httpmw"
1718
"github.com/coder/coder/v2/codersdk"
1819
)
1920

21+
// @Summary Get organizations
22+
// @ID get-organizations
23+
// @Security CoderSessionToken
24+
// @Produce json
25+
// @Tags Organizations
26+
// @Success 200 {object} []codersdk.Organization
27+
// @Router /organizations [get]
28+
func (api *API) organizations(rw http.ResponseWriter, r *http.Request) {
29+
ctx := r.Context()
30+
organizations, err := api.Database.GetOrganizations(ctx)
31+
if httpapi.Is404Error(err) {
32+
httpapi.ResourceNotFound(rw)
33+
return
34+
}
35+
if err != nil {
36+
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
37+
Message: "Internal error fetching organizations.",
38+
Detail: err.Error(),
39+
})
40+
return
41+
}
42+
43+
httpapi.Write(ctx, rw, http.StatusOK, db2sdk.List(organizations, convertOrganization))
44+
}
45+
2046
// @Summary Get organization by ID
2147
// @ID get-organization-by-id
2248
// @Security CoderSessionToken

coderd/organizations_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,15 @@ func TestMultiOrgFetch(t *testing.T) {
2727
require.NoError(t, err)
2828
}
2929

30-
orgs, err := client.OrganizationsByUser(ctx, codersdk.Me)
30+
myOrgs, err := client.OrganizationsByUser(ctx, codersdk.Me)
31+
require.NoError(t, err)
32+
require.NotNil(t, myOrgs)
33+
require.Len(t, myOrgs, len(makeOrgs)+1)
34+
35+
orgs, err := client.Organizations(ctx)
3136
require.NoError(t, err)
3237
require.NotNil(t, orgs)
33-
require.Len(t, orgs, len(makeOrgs)+1)
38+
require.ElementsMatch(t, myOrgs, orgs)
3439
}
3540

3641
func TestOrganizationsByUser(t *testing.T) {

codersdk/organizations.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,21 @@ func (c *Client) OrganizationByName(ctx context.Context, name string) (Organizat
215215
return organization, json.NewDecoder(res.Body).Decode(&organization)
216216
}
217217

218+
func (c *Client) Organizations(ctx context.Context) ([]Organization, error) {
219+
res, err := c.Request(ctx, http.MethodGet, "/api/v2/organizations", nil)
220+
if err != nil {
221+
return []Organization{}, xerrors.Errorf("execute request: %w", err)
222+
}
223+
defer res.Body.Close()
224+
225+
if res.StatusCode != http.StatusOK {
226+
return []Organization{}, ReadBodyAsError(res)
227+
}
228+
229+
var organizations []Organization
230+
return organizations, json.NewDecoder(res.Body).Decode(&organizations)
231+
}
232+
218233
func (c *Client) Organization(ctx context.Context, id uuid.UUID) (Organization, error) {
219234
// OrganizationByName uses the exact same endpoint. It accepts a name or uuid.
220235
// We just provide this function for type safety.

docs/api/organizations.md

Lines changed: 56 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

site/src/api/api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -565,7 +565,7 @@ class ApiMethods {
565565

566566
getOrganizations = async (): Promise<TypesGen.Organization[]> => {
567567
const response = await this.axios.get<TypesGen.Organization[]>(
568-
"/api/v2/users/me/organizations",
568+
"/api/v2/organizations",
569569
);
570570
return response.data;
571571
};

site/src/api/queries/organizations.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type {
44
CreateOrganizationRequest,
55
UpdateOrganizationRequest,
66
} from "api/typesGenerated";
7-
import { meKey, myOrganizationsKey } from "./users";
7+
import { meKey } from "./users";
88

99
export const createOrganization = (queryClient: QueryClient) => {
1010
return {
@@ -13,7 +13,7 @@ export const createOrganization = (queryClient: QueryClient) => {
1313

1414
onSuccess: async () => {
1515
await queryClient.invalidateQueries(meKey);
16-
await queryClient.invalidateQueries(myOrganizationsKey);
16+
await queryClient.invalidateQueries(organizationsKey);
1717
},
1818
};
1919
};
@@ -29,7 +29,7 @@ export const updateOrganization = (queryClient: QueryClient) => {
2929
API.updateOrganization(variables.orgId, variables.req),
3030

3131
onSuccess: async () => {
32-
await queryClient.invalidateQueries(myOrganizationsKey);
32+
await queryClient.invalidateQueries(organizationsKey);
3333
},
3434
};
3535
};
@@ -40,7 +40,7 @@ export const deleteOrganization = (queryClient: QueryClient) => {
4040

4141
onSuccess: async () => {
4242
await queryClient.invalidateQueries(meKey);
43-
await queryClient.invalidateQueries(myOrganizationsKey);
43+
await queryClient.invalidateQueries(organizationsKey);
4444
},
4545
};
4646
};
@@ -78,3 +78,12 @@ export const removeOrganizationMember = (
7878
},
7979
};
8080
};
81+
82+
export const organizationsKey = ["organizations", "me"] as const;
83+
84+
export const organizations = () => {
85+
return {
86+
queryKey: organizationsKey,
87+
queryFn: () => API.getOrganizations(),
88+
};
89+
};

site/src/api/queries/users.ts

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -249,12 +249,3 @@ export const updateAppearanceSettings = (
249249
},
250250
};
251251
};
252-
253-
export const myOrganizationsKey = ["organizations", "me"] as const;
254-
255-
export const myOrganizations = () => {
256-
return {
257-
queryKey: myOrganizationsKey,
258-
queryFn: () => API.getOrganizations(),
259-
};
260-
};

site/src/pages/ManagementSettingsPage/ManagementSettingsLayout.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { createContext, type FC, Suspense, useContext } from "react";
22
import { useQuery } from "react-query";
33
import { Outlet, useLocation, useParams } from "react-router-dom";
44
import { deploymentConfig } from "api/queries/deployment";
5-
import { myOrganizations } from "api/queries/users";
5+
import { organizations } from "api/queries/organizations";
66
import type { Organization } from "api/typesGenerated";
77
import { Loader } from "components/Loader/Loader";
88
import { Margins } from "components/Margins/Margins";
@@ -39,7 +39,7 @@ export const ManagementSettingsLayout: FC = () => {
3939
const { experiments } = useDashboard();
4040
const { organization } = useParams() as { organization: string };
4141
const deploymentConfigQuery = useQuery(deploymentConfig());
42-
const organizationsQuery = useQuery(myOrganizations());
42+
const organizationsQuery = useQuery(organizations());
4343

4444
const multiOrgExperimentEnabled = experiments.includes("multi-organization");
4545

0 commit comments

Comments
 (0)