Skip to content

Commit c7971bd

Browse files
authored
fix: infinite rerender bug and handle 403 better (#3652)
* fix: infinite rerender bug and handle 403 better * chore: lint
1 parent 7e25c30 commit c7971bd

5 files changed

Lines changed: 199 additions & 30 deletions

File tree

frontend/app/src/hooks/use-cloud.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@ import {
33
APICloudMetadata,
44
FeatureFlags,
55
} from '@/lib/api/generated/cloud/data-contracts';
6-
import { useApiError } from '@/lib/hooks';
76
import { useQuery } from '@tanstack/react-query';
8-
import { AxiosError } from 'axios';
97

108
export const metadataIndicatesCloudEnabled = (cloudMeta: APICloudMetadata) => {
119
// @ts-expect-error errors is returned when this is oss
@@ -67,14 +65,8 @@ type UseCloudReturn =
6765
};
6866

6967
export default function useCloud(tenantId?: string): UseCloudReturn {
70-
const { handleApiError } = useApiError();
71-
7268
const cloudMetaQuery = useQuery(getCloudMetadataQuery);
7369

74-
if (cloudMetaQuery.isError) {
75-
handleApiError(cloudMetaQuery.error as AxiosError);
76-
}
77-
7870
const featureFlagsQuery = useQuery({
7971
queryKey: ['feature-flags:list', tenantId],
8072
retry: false,
@@ -94,10 +86,6 @@ export default function useCloud(tenantId?: string): UseCloudReturn {
9486
staleTime: 1000 * 60,
9587
});
9688

97-
if (featureFlagsQuery.isError) {
98-
handleApiError(featureFlagsQuery.error as AxiosError);
99-
}
100-
10189
if (cloudMetaQuery.data && cloudMetaQuery.data.isCloudEnabled) {
10290
return {
10391
cloud: cloudMetaQuery.data,

frontend/app/src/lib/api/generated/control-plane/Api.ts

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,16 @@ import {
1616
APIControlPlaneMetadata,
1717
APIError,
1818
APIErrors,
19+
APITokenList,
1920
CreateManagementTokenRequest,
2021
CreateManagementTokenResponse,
2122
CreateNewTenantForOrganizationRequest,
2223
CreateOrganizationInviteRequest,
2324
CreateOrganizationRequest,
25+
CreateTenantAPITokenRequest,
26+
CreateTenantAPITokenResponse,
2427
CreateTenantInviteRequest,
28+
ListAPIMetaIntegration,
2529
ManagementTokenList,
2630
Organization,
2731
OrganizationForUserList,
@@ -49,6 +53,34 @@ import { ContentType, HttpClient, RequestParams } from "./http-client";
4953
export class Api<
5054
SecurityDataType = unknown,
5155
> extends HttpClient<SecurityDataType> {
56+
/**
57+
* @description Gets the readiness status
58+
*
59+
* @tags Healthcheck
60+
* @name ReadinessGet
61+
* @summary Get readiness
62+
* @request GET:/api/ready
63+
*/
64+
readinessGet = (params: RequestParams = {}) =>
65+
this.request<void, APIErrors>({
66+
path: `/api/ready`,
67+
method: "GET",
68+
...params,
69+
});
70+
/**
71+
* @description Gets the liveness status
72+
*
73+
* @tags Healthcheck
74+
* @name LivenessGet
75+
* @summary Get liveness
76+
* @request GET:/api/live
77+
*/
78+
livenessGet = (params: RequestParams = {}) =>
79+
this.request<void, APIErrors>({
80+
path: `/api/live`,
81+
method: "GET",
82+
...params,
83+
});
5284
/**
5385
* @description Gets metadata for the Hatchet instance
5486
*
@@ -64,6 +96,23 @@ export class Api<
6496
format: "json",
6597
...params,
6698
});
99+
/**
100+
* @description List all integrations
101+
*
102+
* @tags Metadata
103+
* @name MetadataListIntegrations
104+
* @summary List integrations
105+
* @request GET:/api/v1/control-plane/metadata/integrations
106+
* @secure
107+
*/
108+
metadataListIntegrations = (params: RequestParams = {}) =>
109+
this.request<ListAPIMetaIntegration, APIErrors>({
110+
path: `/api/v1/control-plane/metadata/integrations`,
111+
method: "GET",
112+
secure: true,
113+
format: "json",
114+
...params,
115+
});
67116
/**
68117
* @description Logs in a cloud user.
69118
*
@@ -210,6 +259,41 @@ export class Api<
210259
method: "GET",
211260
...params,
212261
});
262+
/**
263+
* @description Starts the Slack OAuth flow for a tenant
264+
*
265+
* @tags User
266+
* @name CloudUserUpdateSlackOauthStart
267+
* @summary Start Slack OAuth flow
268+
* @request GET:/api/v1/control-plane/tenants/{tenant}/slack/start
269+
* @secure
270+
*/
271+
cloudUserUpdateSlackOauthStart = (
272+
tenant: string,
273+
params: RequestParams = {},
274+
) =>
275+
this.request<any, void>({
276+
path: `/api/v1/control-plane/tenants/${tenant}/slack/start`,
277+
method: "GET",
278+
secure: true,
279+
...params,
280+
});
281+
/**
282+
* @description Completes the Slack OAuth flow
283+
*
284+
* @tags User
285+
* @name CloudUserUpdateSlackOauthCallback
286+
* @summary Complete Slack OAuth flow
287+
* @request GET:/api/v1/control-plane/users/slack/callback
288+
* @secure
289+
*/
290+
cloudUserUpdateSlackOauthCallback = (params: RequestParams = {}) =>
291+
this.request<any, void>({
292+
path: `/api/v1/control-plane/users/slack/callback`,
293+
method: "GET",
294+
secure: true,
295+
...params,
296+
});
213297
/**
214298
* @description List all organizations the authenticated user is a member of
215299
*
@@ -326,6 +410,69 @@ export class Api<
326410
format: "json",
327411
...params,
328412
});
413+
/**
414+
* @description List all API tokens for a tenant
415+
*
416+
* @tags Management
417+
* @name OrganizationTenantListApiTokens
418+
* @summary List API Tokens for Tenant
419+
* @request GET:/api/v1/control-plane/organization-tenants/{tenant}/api-tokens
420+
* @secure
421+
*/
422+
organizationTenantListApiTokens = (
423+
tenant: string,
424+
params: RequestParams = {},
425+
) =>
426+
this.request<APITokenList, APIError>({
427+
path: `/api/v1/control-plane/organization-tenants/${tenant}/api-tokens`,
428+
method: "GET",
429+
secure: true,
430+
format: "json",
431+
...params,
432+
});
433+
/**
434+
* @description Create a new API token for a tenant
435+
*
436+
* @tags Management
437+
* @name OrganizationTenantCreateApiToken
438+
* @summary Create API Token for Tenant
439+
* @request POST:/api/v1/control-plane/organization-tenants/{tenant}/api-tokens
440+
* @secure
441+
*/
442+
organizationTenantCreateApiToken = (
443+
tenant: string,
444+
data: CreateTenantAPITokenRequest,
445+
params: RequestParams = {},
446+
) =>
447+
this.request<CreateTenantAPITokenResponse, APIError>({
448+
path: `/api/v1/control-plane/organization-tenants/${tenant}/api-tokens`,
449+
method: "POST",
450+
body: data,
451+
secure: true,
452+
type: ContentType.Json,
453+
format: "json",
454+
...params,
455+
});
456+
/**
457+
* @description Delete an API token for a tenant
458+
*
459+
* @tags Management
460+
* @name OrganizationTenantDeleteApiToken
461+
* @summary Delete API Token for Tenant
462+
* @request DELETE:/api/v1/control-plane/organization-tenants/{tenant}/api-tokens/{api-token}
463+
* @secure
464+
*/
465+
organizationTenantDeleteApiToken = (
466+
tenant: string,
467+
apiToken: string,
468+
params: RequestParams = {},
469+
) =>
470+
this.request<void, APIError>({
471+
path: `/api/v1/control-plane/organization-tenants/${tenant}/api-tokens/${apiToken}`,
472+
method: "DELETE",
473+
secure: true,
474+
...params,
475+
});
329476
/**
330477
* @description Remove a member from an organization
331478
*

frontend/app/src/lib/api/generated/control-plane/data-contracts.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,10 @@ export type { PaginationResponse } from '@/lib/api/generated/cloud/data-contract
4848

4949
export type { APIResourceMeta } from '@/lib/api/generated/cloud/data-contracts';
5050

51+
export type ListAPIMetaIntegration = APIMetaIntegration[];
52+
53+
export type { APIMetaIntegration } from '@/lib/api/generated/cloud/data-contracts';
54+
5155
export type { User } from '@/lib/api/generated/cloud/data-contracts';
5256

5357
export type { UserLoginRequest } from '@/lib/api/generated/cloud/data-contracts';
@@ -274,3 +278,31 @@ export interface TenantExchangeToken {
274278
*/
275279
expiresAt: string;
276280
}
281+
282+
export interface APIToken {
283+
metadata: APIResourceMeta;
284+
/** The name of the API token */
285+
name: string;
286+
/**
287+
* The timestamp at which the token expires
288+
* @format date-time
289+
*/
290+
expiresAt: string;
291+
}
292+
293+
export interface APITokenList {
294+
rows: APIToken[];
295+
pagination?: PaginationResponse;
296+
}
297+
298+
export interface CreateTenantAPITokenRequest {
299+
/** The name of the API token */
300+
name: string;
301+
/** The duration for which the token should be valid (e.g., "30d", "90d") */
302+
expiresIn?: string;
303+
}
304+
305+
export interface CreateTenantAPITokenResponse {
306+
/** The generated API token */
307+
token: string;
308+
}

frontend/app/src/lib/hooks.ts

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import api, { APIErrors } from './api';
2+
import { controlPlaneApi } from './api/api';
23
import { getFieldErrors } from './utils';
34
import { useToast } from '@/components/hooks/use-toast';
5+
import useControlPlane from '@/hooks/use-control-plane';
46
import { useQuery } from '@tanstack/react-query';
57
import { AxiosError } from 'axios';
68
import { Dispatch, SetStateAction } from 'react';
@@ -79,19 +81,22 @@ export function useApiError(
7981
}
8082

8183
export function useApiMetaIntegrations() {
82-
const { handleApiError } = useApiError({});
84+
const { isControlPlaneEnabled } = useControlPlane();
8385

8486
const metaQuery = useQuery({
85-
queryKey: ['metadata:get:integrations'],
87+
queryKey: ['metadata:get:integrations', isControlPlaneEnabled],
8688
queryFn: async () => {
87-
const meta = await api.metadataListIntegrations();
88-
return meta;
89+
try {
90+
return isControlPlaneEnabled
91+
? await controlPlaneApi.metadataListIntegrations()
92+
: await api.metadataListIntegrations();
93+
} catch (e) {
94+
console.error('Failed to get API meta integrations', e);
95+
return null;
96+
}
8997
},
98+
retry: false,
9099
});
91100

92-
if (metaQuery.isError) {
93-
handleApiError(metaQuery.error as AxiosError);
94-
}
95-
96101
return metaQuery.data?.data;
97102
}

frontend/app/src/pages/auth/hooks/use-api-meta.ts

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,22 @@
11
import api from '@/lib/api';
2-
import { useApiError } from '@/lib/hooks';
32
import { useQuery } from '@tanstack/react-query';
4-
import { AxiosError } from 'axios';
53
import { useMemo } from 'react';
64

75
export default function useApiMeta() {
8-
const { handleApiError } = useApiError({});
9-
106
const metaQuery = useQuery({
117
queryKey: ['metadata:get'],
128
queryFn: async () => {
13-
const meta = await api.metadataGet();
14-
return meta;
9+
try {
10+
return await api.metadataGet();
11+
} catch (e) {
12+
console.error('Failed to get API metadata', e);
13+
return null;
14+
}
1515
},
16+
retry: false,
1617
staleTime: 1000 * 60,
1718
});
1819

19-
if (metaQuery.isError) {
20-
handleApiError(metaQuery.error as AxiosError);
21-
}
22-
2320
const data = useMemo(() => {
2421
return metaQuery.data?.data;
2522
}, [metaQuery.data]);

0 commit comments

Comments
 (0)