Skip to content

Commit ff7fa7a

Browse files
Merge pull request #98 from solo-io/charlesthebird/appDetailsApiKeysSection
App Details page: API Keys Section
2 parents 8d78eb4 + 7ca428c commit ff7fa7a

File tree

24 files changed

+575
-104
lines changed

24 files changed

+575
-104
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
changelog:
2+
- type: FIX
3+
issueLink: https://github.com/solo-io/solo-projects/issues/6881
4+
description: >-
5+
Adds an API keys section to the App Details page.

projects/ui/src/Apis/api-types.ts

+10
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,16 @@ export type App = {
108108
teamId: string;
109109
};
110110

111+
export type ApiKey = {
112+
id: string;
113+
createdAt: string;
114+
updatedAt: string;
115+
deletedAt: string;
116+
apiKey: string;
117+
name: string;
118+
metadata: Record<string, string>;
119+
};
120+
111121
export type Team = {
112122
createdAt: string;
113123
description: string;

projects/ui/src/Apis/gg_hooks.ts

+59-19
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import useSWRMutation from "swr/mutation";
44
import { AuthContext } from "../Context/AuthContext";
55
import { omitErrorMessageResponse } from "../Utility/utility";
66
import {
7+
ApiKey,
78
ApiProductDetails,
89
ApiProductSummary,
910
ApiVersion,
@@ -19,15 +20,15 @@ import {
1920
import { fetchJSON, useMultiSwrWithAuth, useSwrWithAuth } from "./utility";
2021

2122
//
22-
// Queries
23+
// region Queries
2324
//
2425

25-
// User
26+
// region User
2627
export function useGetCurrentUser() {
2728
return useSwrWithAuth<User>("/me");
2829
}
2930

30-
// Apps
31+
// region Apps + API Keys
3132
export function useListAppsForTeam(team: Team) {
3233
return useSwrWithAuth<App[]>(`/teams/${team.id}/apps`);
3334
}
@@ -55,8 +56,11 @@ export function useListFlatAppsForTeamsOmitErrors(teams: Team[]) {
5556
export function useGetAppDetails(id?: string) {
5657
return useSwrWithAuth<App>(`/apps/${id}`);
5758
}
59+
export function useListApiKeysForApp(appId: string) {
60+
return useSwrWithAuth<ApiKey[]>(`/apps/${appId}/api-keys`);
61+
}
5862

59-
// Teams
63+
// region Teams
6064
const TEAMS_SWR_KEY = "teams";
6165
export function useListTeams() {
6266
return useSwrWithAuth<Team[]>(`/teams`);
@@ -68,7 +72,7 @@ export function useGetTeamDetails(id?: string) {
6872
return useSwrWithAuth<Team>(`/teams/${id}`);
6973
}
7074

71-
// Api Products
75+
// region API Products
7276
export function useListApiProducts() {
7377
return useSwrWithAuth<ApiProductSummary[]>("/api-products");
7478
}
@@ -79,7 +83,7 @@ export function useGetApiProductVersions(id?: string) {
7983
return useSwrWithAuth<ApiVersion[]>(`/api-products/${id}/versions`);
8084
}
8185

82-
// Subscriptions
86+
// region Subscriptions
8387
// this is an admin endpoint
8488
export function useListSubscriptionsForStatus(status: SubscriptionStatus) {
8589
const swrResponse = useSwrWithAuth<Subscription[] | SubscriptionsListError>(
@@ -115,7 +119,7 @@ export function useListSubscriptionsForApps(apps: App[]) {
115119
}
116120

117121
//
118-
// Mutations
122+
// region Mutations
119123
//
120124

121125
const getLatestAuthHeaders = (latestAccessToken: string | undefined) => {
@@ -129,7 +133,7 @@ const getLatestAuthHeaders = (latestAccessToken: string | undefined) => {
129133
type MutationWithArgs<T> = { arg: T };
130134

131135
// ------------------------ //
132-
// Create Team
136+
// region Create Team
133137

134138
type CreateTeamParams = MutationWithArgs<{ name: string; description: string }>;
135139

@@ -149,7 +153,7 @@ export function useCreateTeamMutation() {
149153
}
150154

151155
// ------------------------ //
152-
// Create Team Member
156+
// region Create Team Member
153157

154158
type AddTeamMemberParams = MutationWithArgs<{ email: string; teamId: string }>;
155159

@@ -169,7 +173,7 @@ export function useAddTeamMemberMutation() {
169173
}
170174

171175
// ------------------------ //
172-
// Remove Team Member
176+
// region Remove Team Member
173177

174178
type AdminRemoveTeamMemberParams = MutationWithArgs<{
175179
teamId: string;
@@ -194,7 +198,7 @@ export function useRemoveTeamMemberMutation() {
194198
}
195199

196200
// ------------------------ //
197-
// Create App
201+
// region Create App
198202

199203
type CreateAppParams = MutationWithArgs<{ name: string; description: string }>;
200204

@@ -220,7 +224,7 @@ export function useCreateAppMutation(teamId: string | undefined) {
220224
}
221225

222226
// ------------------------ //
223-
// Update App
227+
// region Update App
224228

225229
type UpdateAppParams = MutationWithArgs<{
226230
appId: string;
@@ -247,7 +251,7 @@ export function useUpdateAppMutation() {
247251
}
248252

249253
// ------------------------ //
250-
// Update Team
254+
// region Update Team
251255

252256
type UpdateTeamParams = MutationWithArgs<{
253257
teamId: string;
@@ -272,7 +276,7 @@ export function useUpdateTeamMutation() {
272276
}
273277

274278
// ------------------------ //
275-
// Create App and Subscription
279+
// region Create App and Subscription
276280

277281
type CreateAppAndSubscriptionParams = MutationWithArgs<{
278282
appName: string;
@@ -315,7 +319,7 @@ export function useCreateAppAndSubscriptionMutation() {
315319
}
316320

317321
// ------------------------ //
318-
// Create Subscription
322+
// region Create Subscription
319323

320324
type CreateSubscriptionParams = MutationWithArgs<{
321325
apiProductId: string;
@@ -344,7 +348,7 @@ export function useCreateSubscriptionMutation(appId: string) {
344348
}
345349

346350
// -------------------------------- //
347-
// (Admin) Approve/Reject Subscription
351+
// region (Admin) Approve/Reject Subscription
348352

349353
type UpdateSubscriptionParams = MutationWithArgs<{
350354
subscription: Subscription;
@@ -387,7 +391,7 @@ export function useAdminRejectSubscriptionMutation() {
387391
}
388392

389393
// -------------------------------- //
390-
// Delete Subscription
394+
// region Delete Subscription
391395

392396
export function useDeleteSubscriptionMutation() {
393397
const { latestAccessToken } = useContext(AuthContext);
@@ -406,7 +410,7 @@ export function useDeleteSubscriptionMutation() {
406410
}
407411

408412
// -------------------------------- //
409-
// Delete Team
413+
// region Delete Team
410414

411415
type DeleteTeamParams = MutationWithArgs<{ teamId: string }>;
412416

@@ -424,7 +428,7 @@ export function useDeleteTeamMutation() {
424428
}
425429

426430
// -------------------------------- //
427-
// Delete App
431+
// region Delete App
428432

429433
type DeleteAppParams = MutationWithArgs<{ appId: string }>;
430434

@@ -440,3 +444,39 @@ export function useDeleteAppMutation() {
440444
};
441445
return useSWRMutation(`delete-team`, deleteApp);
442446
}
447+
448+
// -------------------------------- //
449+
// region Create API Key
450+
451+
type CreateApiKeyParams = MutationWithArgs<{ apiKeyName: string }>;
452+
453+
export function useCreateApiKeyMutation(appId: string) {
454+
const { latestAccessToken } = useContext(AuthContext);
455+
const createApiKey = async (_: string, { arg }: CreateApiKeyParams) => {
456+
return await fetchJSON(`/apps/${appId}/api-keys`, {
457+
method: "POST",
458+
headers: getLatestAuthHeaders(latestAccessToken),
459+
body: JSON.stringify(arg),
460+
});
461+
};
462+
return useSWRMutation<ApiKey, any, string, CreateApiKeyParams["arg"]>(
463+
`/apps/${appId}/api-keys`,
464+
createApiKey
465+
);
466+
}
467+
468+
// -------------------------------- //
469+
// region Delete API Key
470+
471+
type DeleteApiKeyParams = MutationWithArgs<{ apiKeyId: string }>;
472+
473+
export function useDeleteApiKeyMutation(appId: string) {
474+
const { latestAccessToken } = useContext(AuthContext);
475+
const deleteApiKey = async (_: string, { arg }: DeleteApiKeyParams) => {
476+
await fetchJSON(`/api-keys/${arg.apiKeyId}`, {
477+
method: "DELETE",
478+
headers: getLatestAuthHeaders(latestAccessToken),
479+
});
480+
};
481+
return useSWRMutation(`/apps/${appId}/api-keys`, deleteApiKey);
482+
}

projects/ui/src/Components/ApiDetails/gloo-gateway-components/ApiProductDetailsPageBody.tsx

+6-8
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
ApiVersionSchema,
88
} from "../../../Apis/api-types";
99
import { ContentWidthDiv } from "../../../Styles/ContentWidthHelpers";
10-
import { SimpleEmptyContent } from "../../Common/EmptyData";
10+
import { EmptyData } from "../../Common/EmptyData";
1111
import DocsTabContent from "./DocsTab/DocsTabContent";
1212
import SchemaTabContent from "./SchemaTab/SchemaTabContent";
1313

@@ -71,7 +71,6 @@ export function ApiProductDetailsPageBody({
7171
*/}
7272
<Tabs.Panel value={apiProductDetailsTabValues.SPEC} pt={"xl"}>
7373
<SchemaTabContent
74-
apiProduct={apiProduct}
7574
apiProductVersions={apiProductVersions}
7675
apiVersionSpec={apiVersionSpec}
7776
selectedApiVersion={selectedApiVersion}
@@ -81,17 +80,16 @@ export function ApiProductDetailsPageBody({
8180
{includesDocumentation ? (
8281
<DocsTabContent selectedApiVersion={selectedApiVersion} />
8382
) : (
84-
<SimpleEmptyContent title="No documentation found.">
83+
<EmptyData title="No documentation found.">
8584
<small>
8685
You may add documentation for this API in the{" "}
87-
<Code sx={{ whiteSpace: "nowrap" }}>
86+
<Code>
8887
spec.versions[your-version].openapiMetadata.description
8988
</Code>{" "}
90-
field of this{" "}
91-
<Code sx={{ whiteSpace: "nowrap" }}>ApiProduct</Code> resource.
92-
Markdown is supported.
89+
field of this <Code>ApiProduct</Code> resource. Markdown is
90+
supported.
9391
</small>
94-
</SimpleEmptyContent>
92+
</EmptyData>
9593
)}
9694
</Tabs.Panel>
9795
</Tabs>

projects/ui/src/Components/ApiDetails/gloo-gateway-components/SchemaTab/SchemaTabContent.tsx

+13-14
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,25 @@
1-
import { Box } from "@mantine/core";
2-
import {
3-
ApiProductSummary,
4-
ApiVersion,
5-
ApiVersionSchema,
6-
} from "../../../../Apis/api-types";
1+
import { Box, Code } from "@mantine/core";
2+
import { ApiVersion, ApiVersionSchema } from "../../../../Apis/api-types";
73
import { EmptyData } from "../../../Common/EmptyData";
84
import { ErrorBoundary } from "../../../Common/ErrorBoundary";
95
import { ApiSchemaDisplay } from "./ApiSchemaDisplay";
106

117
const SchemaTabContent = ({
12-
apiProduct,
138
selectedApiVersion,
149
apiProductVersions,
1510
apiVersionSpec,
1611
}: {
17-
apiProduct: ApiProductSummary;
1812
selectedApiVersion: ApiVersion;
1913
apiProductVersions: ApiVersion[];
2014
apiVersionSpec: ApiVersionSchema | undefined;
2115
}) => {
2216
if (!apiProductVersions.length) {
2317
return (
2418
<Box m="60px">
25-
<EmptyData
26-
topicMessageOverride={`The API Product, "${apiProduct.name}", has no API Version data.`}
27-
/>
19+
<EmptyData title={`No API versions found.`}>
20+
Add a version to the <Code>spec.versions</Code> field of this{" "}
21+
<Code>ApiProduct</Code> for data to appear.
22+
</EmptyData>
2823
</Box>
2924
);
3025
}
@@ -36,9 +31,13 @@ const SchemaTabContent = ({
3631
// There is a selected API version, but no schema.
3732
return (
3833
<Box m="60px">
39-
<EmptyData
40-
topicMessageOverride={`No schema was returned for the API Version: "${selectedApiVersion.name}".`}
41-
/>
34+
<EmptyData title={`No schema found.`}>
35+
The schema was not returned for this <Code>ApiProduct</Code> version.
36+
<br />
37+
Verify that your OpenApi spec was generated correctly in the
38+
corresponding <Code>ApiDoc</Code> resource for this{" "}
39+
<Code>Service</Code>.
40+
</EmptyData>
4241
</Box>
4342
);
4443
}

projects/ui/src/Components/Apis/EmptyApisPage.tsx

+4-8
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { AuthContext } from "../../Context/AuthContext";
55
import { apisImageURL } from "../../user_variables.tmplr";
66
import { BannerHeading } from "../Common/Banner/BannerHeading";
77
import { BannerHeadingTitle } from "../Common/Banner/BannerHeadingTitle";
8-
import { SimpleEmptyContent } from "../Common/EmptyData";
8+
import { EmptyData } from "../Common/EmptyData";
99
import { PageContainer } from "../Common/PageContainer";
1010
import { StyledApisListMain } from "./gloo-mesh-gateway-components/ApisPage.style";
1111

@@ -31,19 +31,15 @@ export const EmptyApisPageContent = () => {
3131
const { isLoggedIn } = useContext(AuthContext);
3232

3333
if (!!isLoggedIn)
34-
return (
35-
<SimpleEmptyContent>
36-
No API Products have been created.
37-
</SimpleEmptyContent>
38-
);
34+
return <EmptyData>No API Products have been created.</EmptyData>;
3935
return (
40-
<SimpleEmptyContent title="No API Products were found.">
36+
<EmptyData title="No API Products were found.">
4137
<small>
4238
To view API Products in private Portals, please log in.
4339
<br />
4440
To view API Products in public Portals, the Portal resource must have{" "}
4541
<Code>spec.visibility.public = true</Code>.
4642
</small>
47-
</SimpleEmptyContent>
43+
</EmptyData>
4844
);
4945
};

projects/ui/src/Components/Apis/gloo-gateway-components/ApisTab/ApisList.tsx

+6-1
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,13 @@ export function ApisList({
7878
if (apiProductsList === undefined) {
7979
return <Loading message="Getting list of apis..." />;
8080
}
81+
if (!apiProductsList.length) {
82+
return <EmptyData title="No API Products were found." />;
83+
}
8184
if (!filteredApiProductsList.length) {
82-
return <EmptyData topic="API" />;
85+
return (
86+
<EmptyData title="No API Products were found matching these filters." />
87+
);
8388
}
8489
if (preferGridView) {
8590
return (

projects/ui/src/Components/Apis/gloo-mesh-gateway-components/ApisList.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export function ApisList({
7272
}
7373

7474
if (!displayedApisList.length) {
75-
return <EmptyData topic="API" />;
75+
return <EmptyData title="No APIs were found matching these filters." />;
7676
}
7777
return (
7878
<>

0 commit comments

Comments
 (0)