Skip to content

Commit 08ab7a2

Browse files
authored
feat: Rate limit deletion (#3638)
1 parent b32ba9f commit 08ab7a2

12 files changed

Lines changed: 475 additions & 64 deletions

File tree

api-contracts/openapi/paths/rate-limits/rate_limits.yaml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,3 +67,41 @@ withTenant:
6767
summary: List rate limits
6868
tags:
6969
- Rate Limits
70+
delete:
71+
x-resources: ["tenant"]
72+
description: Delete a rate limit for a tenant.
73+
operationId: rate-limit:delete
74+
parameters:
75+
- description: The tenant id
76+
in: path
77+
name: tenant
78+
required: true
79+
schema:
80+
type: string
81+
format: uuid
82+
minLength: 36
83+
maxLength: 36
84+
- description: The limit key
85+
in: query
86+
name: key
87+
required: true
88+
schema:
89+
type: string
90+
responses:
91+
"204":
92+
description: Successfully deleted the rate limit
93+
"400":
94+
content:
95+
application/json:
96+
schema:
97+
$ref: "../../components/schemas/_index.yaml#/APIErrors"
98+
description: A malformed or bad request
99+
"403":
100+
content:
101+
application/json:
102+
schema:
103+
$ref: "../../components/schemas/_index.yaml#/APIErrors"
104+
description: Forbidden
105+
summary: Delete rate limit
106+
tags:
107+
- Rate Limits

api/v1/server/authz/rbac.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ roles:
6666
- SnsDelete
6767
- StepRunListEvents
6868
- RateLimitList
69+
- RateLimitDelete
6970
- WorkflowVersionGet
7071
- TenantGetPrometheusMetrics
7172
- EventUpdateCancel
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package rate_limits
2+
3+
import (
4+
"context"
5+
"time"
6+
7+
"github.com/labstack/echo/v4"
8+
9+
"github.com/hatchet-dev/hatchet/api/v1/server/oas/apierrors"
10+
"github.com/hatchet-dev/hatchet/api/v1/server/oas/gen"
11+
"github.com/hatchet-dev/hatchet/pkg/repository/sqlcv1"
12+
)
13+
14+
func (t *RateLimitService) RateLimitDelete(ctx echo.Context, request gen.RateLimitDeleteRequestObject) (gen.RateLimitDeleteResponseObject, error) {
15+
tenant := ctx.Get("tenant").(*sqlcv1.Tenant)
16+
tenantId := tenant.ID
17+
key := request.Params.Key
18+
dbCtx, cancel := context.WithTimeout(ctx.Request().Context(), 30*time.Second)
19+
defer cancel()
20+
err := t.config.V1.RateLimit().DeleteRateLimits(dbCtx, tenantId, key)
21+
if err != nil {
22+
return gen.RateLimitDelete400JSONResponse(apierrors.NewAPIErrors("failed to delete rate-limit")), nil
23+
}
24+
return gen.RateLimitDelete204Response{}, nil
25+
}

api/v1/server/oas/gen/openapi.gen.go

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

frontend/app/src/lib/api/generated/Api.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2311,6 +2311,31 @@ export class Api<
23112311
...params,
23122312
xResources: ["tenant"],
23132313
}), { resources: new Set<string>(["tenant"]) });
2314+
/**
2315+
* @description Delete a rate limit for a tenant.
2316+
*
2317+
* @tags Rate Limits
2318+
* @name RateLimitDelete
2319+
* @summary Delete rate limit
2320+
* @request DELETE:/api/v1/tenants/{tenant}/rate-limits
2321+
* @secure
2322+
*/
2323+
rateLimitDelete = Object.assign((
2324+
tenant: string,
2325+
query: {
2326+
/** The limit key */
2327+
key: string;
2328+
},
2329+
params: RequestParams = {},
2330+
) =>
2331+
this.request<void, APIErrors>({
2332+
path: `/api/v1/tenants/${tenant}/rate-limits`,
2333+
method: "DELETE",
2334+
query: query,
2335+
secure: true,
2336+
...params,
2337+
xResources: ["tenant"],
2338+
}), { resources: new Set<string>(["tenant"]) });
23142339
/**
23152340
* @description Gets a list of tenant members
23162341
*

frontend/app/src/pages/main/v1/rate-limits/components/rate-limit-columns.tsx

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { LimitIndicator } from '../../tenant-settings/resource-limits/components/resource-limit-columns';
22
import { RateLimitWithMetadata } from '../hooks/use-rate-limits';
33
import { DataTableColumnHeader } from '@/components/v1/molecules/data-table/data-table-column-header';
4+
import { TableRowActions } from '@/components/v1/molecules/data-table/data-table-row-actions';
45
import RelativeDate from '@/components/v1/molecules/relative-date';
56
import { capitalize } from '@/lib/utils';
67
import { ColumnDef } from '@tanstack/react-table';
@@ -11,6 +12,7 @@ export const RateLimitColumn = {
1112
limit: 'Limit',
1213
lastRefill: 'Last Refill',
1314
window: 'Window',
15+
actions: 'Actions',
1416
};
1517

1618
type RateLimitColumnKeys = keyof typeof RateLimitColumn;
@@ -20,8 +22,13 @@ const valueKey: RateLimitColumnKeys = 'value';
2022
const limitKey: RateLimitColumnKeys = 'limit';
2123
const lastRefillKey: RateLimitColumnKeys = 'lastRefill';
2224
const windowKey: RateLimitColumnKeys = 'window';
25+
const actionsKey: RateLimitColumnKeys = 'actions';
2326

24-
export const columns: ColumnDef<RateLimitWithMetadata>[] = [
27+
export const columns = ({
28+
onDeleteClick,
29+
}: {
30+
onDeleteClick: (row: RateLimitWithMetadata) => void;
31+
}): ColumnDef<RateLimitWithMetadata>[] => [
2532
{
2633
accessorKey: keyKey,
2734
header: ({ column }) => (
@@ -87,4 +94,25 @@ export const columns: ColumnDef<RateLimitWithMetadata>[] = [
8794
},
8895
enableSorting: false,
8996
},
97+
{
98+
accessorKey: actionsKey,
99+
header: ({ column }) => (
100+
<DataTableColumnHeader column={column} title={RateLimitColumn.actions} />
101+
),
102+
cell: ({ row }) => (
103+
<div className="flex flex-row justify-center">
104+
<TableRowActions
105+
row={row.original}
106+
actions={[
107+
{
108+
label: 'Delete',
109+
onClick: () => onDeleteClick(row.original),
110+
},
111+
]}
112+
/>
113+
</div>
114+
),
115+
enableSorting: false,
116+
enableHiding: false,
117+
},
90118
];

frontend/app/src/pages/main/v1/rate-limits/index.tsx

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,26 @@ import {
44
RateLimitColumn,
55
} from './components/rate-limit-columns';
66
import { useRateLimits } from './hooks/use-rate-limits';
7+
import { RateLimitWithMetadata } from './hooks/use-rate-limits';
78
import { DocsButton } from '@/components/v1/docs/docs-button';
89
import { DataTable } from '@/components/v1/molecules/data-table/data-table';
910
import { ToolbarType } from '@/components/v1/molecules/data-table/data-table-toolbar';
11+
import { useCurrentTenantId } from '@/hooks/use-tenant';
12+
import api from '@/lib/api';
1013
import { docsPages } from '@/lib/generated/docs';
14+
import { useApiError } from '@/lib/hooks';
15+
import { useMutation } from '@tanstack/react-query';
1116
import { VisibilityState } from '@tanstack/react-table';
12-
import { useState } from 'react';
17+
import { useMemo, useState } from 'react';
1318

1419
export default function RateLimits() {
1520
return <RateLimitsTable />;
1621
}
1722

1823
function RateLimitsTable() {
1924
const [columnVisibility, setColumnVisibility] = useState<VisibilityState>({});
25+
const { tenantId } = useCurrentTenantId();
26+
const { handleApiError } = useApiError({});
2027

2128
const {
2229
data,
@@ -33,11 +40,25 @@ function RateLimitsTable() {
3340
resetFilters,
3441
} = useRateLimits({ key: 'rate-limits-table' });
3542

43+
const deleteMutation = useMutation({
44+
mutationKey: ['rate-limit:delete', tenantId],
45+
mutationFn: async (row: RateLimitWithMetadata) => {
46+
await api.rateLimitDelete(tenantId, { key: row.key });
47+
},
48+
onSuccess: () => refetch(),
49+
onError: handleApiError,
50+
});
51+
52+
const tableColumns = useMemo(
53+
() => columns({ onDeleteClick: (row) => deleteMutation.mutate(row) }),
54+
[deleteMutation],
55+
);
56+
3657
return (
3758
<DataTable
3859
error={error}
3960
isLoading={isLoading}
40-
columns={columns}
61+
columns={tableColumns}
4162
data={data}
4263
filters={[
4364
{

0 commit comments

Comments
 (0)