mirror of
https://github.com/coder/coder.git
synced 2026-09-22 05:05:20 +08:00
chore: implement fetch all organizations endpoint (#13941)
* chore: implement fetch all organizations endpoint * update ui to use list all orgs
This commit is contained in:
Generated
+26
@@ -2037,6 +2037,32 @@ const docTemplate = `{
|
||||
}
|
||||
},
|
||||
"/organizations": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"Organizations"
|
||||
],
|
||||
"summary": "Get organizations",
|
||||
"operationId": "get-organizations",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.Organization"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
|
||||
Generated
+22
@@ -1779,6 +1779,28 @@
|
||||
}
|
||||
},
|
||||
"/organizations": {
|
||||
"get": {
|
||||
"security": [
|
||||
{
|
||||
"CoderSessionToken": []
|
||||
}
|
||||
],
|
||||
"produces": ["application/json"],
|
||||
"tags": ["Organizations"],
|
||||
"summary": "Get organizations",
|
||||
"operationId": "get-organizations",
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "OK",
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/definitions/codersdk.Organization"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"security": [
|
||||
{
|
||||
|
||||
@@ -865,6 +865,7 @@ func New(options *Options) *API {
|
||||
apiKeyMiddleware,
|
||||
)
|
||||
r.Post("/", api.postOrganizations)
|
||||
r.Get("/", api.organizations)
|
||||
r.Route("/{organization}", func(r chi.Router) {
|
||||
r.Use(
|
||||
httpmw.ExtractOrganizationParam(options.Database),
|
||||
|
||||
@@ -11,12 +11,38 @@ import (
|
||||
|
||||
"github.com/coder/coder/v2/coderd/audit"
|
||||
"github.com/coder/coder/v2/coderd/database"
|
||||
"github.com/coder/coder/v2/coderd/database/db2sdk"
|
||||
"github.com/coder/coder/v2/coderd/database/dbtime"
|
||||
"github.com/coder/coder/v2/coderd/httpapi"
|
||||
"github.com/coder/coder/v2/coderd/httpmw"
|
||||
"github.com/coder/coder/v2/codersdk"
|
||||
)
|
||||
|
||||
// @Summary Get organizations
|
||||
// @ID get-organizations
|
||||
// @Security CoderSessionToken
|
||||
// @Produce json
|
||||
// @Tags Organizations
|
||||
// @Success 200 {object} []codersdk.Organization
|
||||
// @Router /organizations [get]
|
||||
func (api *API) organizations(rw http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
organizations, err := api.Database.GetOrganizations(ctx)
|
||||
if httpapi.Is404Error(err) {
|
||||
httpapi.ResourceNotFound(rw)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error fetching organizations.",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
httpapi.Write(ctx, rw, http.StatusOK, db2sdk.List(organizations, convertOrganization))
|
||||
}
|
||||
|
||||
// @Summary Get organization by ID
|
||||
// @ID get-organization-by-id
|
||||
// @Security CoderSessionToken
|
||||
|
||||
@@ -27,10 +27,15 @@ func TestMultiOrgFetch(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
orgs, err := client.OrganizationsByUser(ctx, codersdk.Me)
|
||||
myOrgs, err := client.OrganizationsByUser(ctx, codersdk.Me)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, myOrgs)
|
||||
require.Len(t, myOrgs, len(makeOrgs)+1)
|
||||
|
||||
orgs, err := client.Organizations(ctx)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, orgs)
|
||||
require.Len(t, orgs, len(makeOrgs)+1)
|
||||
require.ElementsMatch(t, myOrgs, orgs)
|
||||
}
|
||||
|
||||
func TestOrganizationsByUser(t *testing.T) {
|
||||
|
||||
@@ -215,6 +215,21 @@ func (c *Client) OrganizationByName(ctx context.Context, name string) (Organizat
|
||||
return organization, json.NewDecoder(res.Body).Decode(&organization)
|
||||
}
|
||||
|
||||
func (c *Client) Organizations(ctx context.Context) ([]Organization, error) {
|
||||
res, err := c.Request(ctx, http.MethodGet, "/api/v2/organizations", nil)
|
||||
if err != nil {
|
||||
return []Organization{}, xerrors.Errorf("execute request: %w", err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
if res.StatusCode != http.StatusOK {
|
||||
return []Organization{}, ReadBodyAsError(res)
|
||||
}
|
||||
|
||||
var organizations []Organization
|
||||
return organizations, json.NewDecoder(res.Body).Decode(&organizations)
|
||||
}
|
||||
|
||||
func (c *Client) Organization(ctx context.Context, id uuid.UUID) (Organization, error) {
|
||||
// OrganizationByName uses the exact same endpoint. It accepts a name or uuid.
|
||||
// We just provide this function for type safety.
|
||||
|
||||
Generated
+56
@@ -87,6 +87,62 @@ curl -X POST http://coder-server:8080/api/v2/licenses/refresh-entitlements \
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Get organizations
|
||||
|
||||
### Code samples
|
||||
|
||||
```shell
|
||||
# Example request using curl
|
||||
curl -X GET http://coder-server:8080/api/v2/organizations \
|
||||
-H 'Accept: application/json' \
|
||||
-H 'Coder-Session-Token: API_KEY'
|
||||
```
|
||||
|
||||
`GET /organizations`
|
||||
|
||||
### Example responses
|
||||
|
||||
> 200 Response
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"created_at": "2019-08-24T14:15:22Z",
|
||||
"description": "string",
|
||||
"display_name": "string",
|
||||
"icon": "string",
|
||||
"id": "497f6eca-6276-4993-bfeb-53cbbbba6f08",
|
||||
"is_default": true,
|
||||
"name": "string",
|
||||
"updated_at": "2019-08-24T14:15:22Z"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Responses
|
||||
|
||||
| Status | Meaning | Description | Schema |
|
||||
| ------ | ------------------------------------------------------- | ----------- | ----------------------------------------------------------------- |
|
||||
| 200 | [OK](https://tools.ietf.org/html/rfc7231#section-6.3.1) | OK | array of [codersdk.Organization](schemas.md#codersdkorganization) |
|
||||
|
||||
<h3 id="get-organizations-responseschema">Response Schema</h3>
|
||||
|
||||
Status Code **200**
|
||||
|
||||
| Name | Type | Required | Restrictions | Description |
|
||||
| ---------------- | ----------------- | -------- | ------------ | ----------- |
|
||||
| `[array item]` | array | false | | |
|
||||
| `» created_at` | string(date-time) | true | | |
|
||||
| `» description` | string | false | | |
|
||||
| `» display_name` | string | false | | |
|
||||
| `» icon` | string | false | | |
|
||||
| `» id` | string(uuid) | true | | |
|
||||
| `» is_default` | boolean | true | | |
|
||||
| `» name` | string | false | | |
|
||||
| `» updated_at` | string(date-time) | true | | |
|
||||
|
||||
To perform this operation, you must be authenticated. [Learn more](authentication.md).
|
||||
|
||||
## Create organization
|
||||
|
||||
### Code samples
|
||||
|
||||
+1
-1
@@ -565,7 +565,7 @@ class ApiMethods {
|
||||
|
||||
getOrganizations = async (): Promise<TypesGen.Organization[]> => {
|
||||
const response = await this.axios.get<TypesGen.Organization[]>(
|
||||
"/api/v2/users/me/organizations",
|
||||
"/api/v2/organizations",
|
||||
);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
@@ -4,7 +4,7 @@ import type {
|
||||
CreateOrganizationRequest,
|
||||
UpdateOrganizationRequest,
|
||||
} from "api/typesGenerated";
|
||||
import { meKey, myOrganizationsKey } from "./users";
|
||||
import { meKey } from "./users";
|
||||
|
||||
export const createOrganization = (queryClient: QueryClient) => {
|
||||
return {
|
||||
@@ -13,7 +13,7 @@ export const createOrganization = (queryClient: QueryClient) => {
|
||||
|
||||
onSuccess: async () => {
|
||||
await queryClient.invalidateQueries(meKey);
|
||||
await queryClient.invalidateQueries(myOrganizationsKey);
|
||||
await queryClient.invalidateQueries(organizationsKey);
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -29,7 +29,7 @@ export const updateOrganization = (queryClient: QueryClient) => {
|
||||
API.updateOrganization(variables.orgId, variables.req),
|
||||
|
||||
onSuccess: async () => {
|
||||
await queryClient.invalidateQueries(myOrganizationsKey);
|
||||
await queryClient.invalidateQueries(organizationsKey);
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -40,7 +40,7 @@ export const deleteOrganization = (queryClient: QueryClient) => {
|
||||
|
||||
onSuccess: async () => {
|
||||
await queryClient.invalidateQueries(meKey);
|
||||
await queryClient.invalidateQueries(myOrganizationsKey);
|
||||
await queryClient.invalidateQueries(organizationsKey);
|
||||
},
|
||||
};
|
||||
};
|
||||
@@ -78,3 +78,12 @@ export const removeOrganizationMember = (
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const organizationsKey = ["organizations", "me"] as const;
|
||||
|
||||
export const organizations = () => {
|
||||
return {
|
||||
queryKey: organizationsKey,
|
||||
queryFn: () => API.getOrganizations(),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -249,12 +249,3 @@ export const updateAppearanceSettings = (
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export const myOrganizationsKey = ["organizations", "me"] as const;
|
||||
|
||||
export const myOrganizations = () => {
|
||||
return {
|
||||
queryKey: myOrganizationsKey,
|
||||
queryFn: () => API.getOrganizations(),
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@ import { createContext, type FC, Suspense, useContext } from "react";
|
||||
import { useQuery } from "react-query";
|
||||
import { Outlet, useLocation, useParams } from "react-router-dom";
|
||||
import { deploymentConfig } from "api/queries/deployment";
|
||||
import { myOrganizations } from "api/queries/users";
|
||||
import { organizations } from "api/queries/organizations";
|
||||
import type { Organization } from "api/typesGenerated";
|
||||
import { Loader } from "components/Loader/Loader";
|
||||
import { Margins } from "components/Margins/Margins";
|
||||
@@ -39,7 +39,7 @@ export const ManagementSettingsLayout: FC = () => {
|
||||
const { experiments } = useDashboard();
|
||||
const { organization } = useParams() as { organization: string };
|
||||
const deploymentConfigQuery = useQuery(deploymentConfig());
|
||||
const organizationsQuery = useQuery(myOrganizations());
|
||||
const organizationsQuery = useQuery(organizations());
|
||||
|
||||
const multiOrgExperimentEnabled = experiments.includes("multi-organization");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user