Skip to content

Commit b4fbf49

Browse files
committed
Support date range filters on request history logs
1 parent 577825e commit b4fbf49

File tree

9 files changed

+144
-28
lines changed

9 files changed

+144
-28
lines changed

Diff for: services/backend-api/client/src/features/feed/api/getUserFeedRequests.ts

+10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ export interface GetUserFeedRequestsInput {
77
data: {
88
limit: number;
99
skip: number;
10+
afterDate?: string;
11+
beforeDate?: string;
1012
};
1113
}
1214

@@ -34,6 +36,14 @@ export const getUserFeedRequests = async ({
3436
params.append("limit", data.limit.toString());
3537
params.append("skip", data.skip.toString());
3638

39+
if (data.afterDate) {
40+
params.append("afterDate", data.afterDate);
41+
}
42+
43+
if (data.beforeDate) {
44+
params.append("beforeDate", data.beforeDate);
45+
}
46+
3747
const query = params.toString();
3848

3949
const res = await fetchRest(`/api/v1/user-feeds/${feedId}/requests?${query}`, {

Diff for: services/backend-api/client/src/features/feed/components/UserFeedLogs/RequestHistory/index.tsx

+94-9
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@ import {
66
Center,
77
Divider,
88
Flex,
9+
FormControl,
10+
FormLabel,
911
HStack,
1012
Heading,
13+
Input,
1114
Popover,
1215
PopoverArrow,
1316
PopoverBody,
@@ -29,12 +32,14 @@ import {
2932
import { useTranslation } from "react-i18next";
3033
import dayjs from "dayjs";
3134
import { QuestionOutlineIcon, Search2Icon } from "@chakra-ui/icons";
32-
import { forwardRef } from "react";
35+
import { forwardRef, useEffect, useState } from "react";
3336
import { useUserFeedRequestsWithPagination } from "../../../hooks";
3437
import { UserFeedRequestStatus } from "../../../types";
3538
import { InlineErrorAlert } from "../../../../../components";
3639
import { useUserFeedContext } from "../../../../../contexts/UserFeedContext";
3740
import { RequestDetails } from "./RequestDetails";
41+
import { GetUserFeedRequestsInput } from "../../../api";
42+
import { DismissableAlert } from "../../../../../components/DismissableAlert";
3843

3944
const QuestionOutlineComponent = forwardRef<any>((props, ref) => (
4045
<QuestionOutlineIcon fontSize={12} tabIndex={-1} ref={ref} aria-hidden {...props} />
@@ -84,16 +89,95 @@ export const RequestHistory = () => {
8489
const {
8590
userFeed: { id: feedId },
8691
} = useUserFeedContext();
92+
const [startDate, setStartDate] = useState<string>();
93+
const [endDate, setEndDate] = useState<string>();
94+
const [requestData, setRequestData] = useState<Partial<GetUserFeedRequestsInput["data"]>>({});
8795
const { data, status, error, skip, nextPage, prevPage, fetchStatus, limit } =
8896
useUserFeedRequestsWithPagination({
8997
feedId,
90-
data: {},
98+
data: requestData,
9199
});
100+
const [isInvalidDateRange, setIsInvalidDateRange] = useState(false);
92101
const { t } = useTranslation();
93102

94103
const onFirstPage = skip === 0;
95104
const hasNoData = data?.result.requests.length === 0 && skip === 0;
96105

106+
useEffect(() => {
107+
setIsInvalidDateRange(false);
108+
}, [fetchStatus]);
109+
110+
const onApplyDateRange = () => {
111+
if (fetchStatus === "fetching") {
112+
return;
113+
}
114+
115+
if (startDate && endDate && new Date(startDate) > new Date(endDate)) {
116+
setIsInvalidDateRange(true);
117+
} else {
118+
const newDateRange = {
119+
...requestData,
120+
afterDate: startDate ? new Date(startDate).toISOString() : undefined,
121+
beforeDate: endDate ? new Date(endDate).toISOString() : undefined,
122+
};
123+
setRequestData(newDateRange);
124+
setIsInvalidDateRange(false);
125+
}
126+
};
127+
128+
const dateRangeForm = (
129+
<Stack
130+
as="form"
131+
onSubmit={(e) => {
132+
e.preventDefault();
133+
onApplyDateRange();
134+
}}
135+
>
136+
<HStack flexWrap="wrap">
137+
<FormControl flex={1}>
138+
<FormLabel>Start Date Range</FormLabel>
139+
<Input
140+
bg="gray.900"
141+
type="datetime-local"
142+
size="sm"
143+
onChange={(e) => {
144+
setStartDate(e.target.value);
145+
}}
146+
/>
147+
</FormControl>
148+
<FormControl flex={1}>
149+
<FormLabel>End Date Range</FormLabel>
150+
<Input
151+
bg="gray.900"
152+
type="datetime-local"
153+
size="sm"
154+
onChange={(e) => {
155+
setEndDate(e.target.value);
156+
}}
157+
/>
158+
</FormControl>
159+
</HStack>
160+
{isInvalidDateRange && (
161+
<DismissableAlert
162+
status="error"
163+
title="Invalid Date Range"
164+
description="The start date must be before the end date."
165+
onClosed={() => setIsInvalidDateRange(false)}
166+
/>
167+
)}
168+
<Box>
169+
<Button
170+
size="sm"
171+
onClick={onApplyDateRange}
172+
aria-disabled={fetchStatus === "fetching"}
173+
type="submit"
174+
>
175+
Apply Date Range
176+
</Button>
177+
</Box>
178+
</Stack>
179+
);
180+
97181
return (
98182
<Stack spacing={4} mb={8} border="solid 1px" borderColor="gray.700" borderRadius="md">
99183
<Box>
@@ -111,17 +195,17 @@ export const RequestHistory = () => {
111195
<Box srOnly aria-live="polite">
112196
{status === "loading" && (
113197
<span>
114-
Loading request history rows ${skip + 1} through ${skip + limit}
198+
Loading request history rows {skip + 1} through {skip + limit}
115199
</span>
116200
)}
117201
{status === "success" && (
118202
<span>
119-
Finished loading request history rows ${skip + 1} through ${skip + limit}
203+
Finished loading request history rows {skip + 1} through {skip + limit}
120204
</span>
121205
)}
122206
{status === "success" && fetchStatus === "fetching" && (
123207
<span>
124-
Loading request history rows ${skip + 1} through ${skip + limit}
208+
Loading request history rows {skip + 1} through {skip + limit}
125209
</span>
126210
)}
127211
</Box>
@@ -145,13 +229,14 @@ export const RequestHistory = () => {
145229
</Alert>
146230
)}
147231
{hasNoData && (
148-
<Text color="whiteAlpha.700">
149-
No historical requests found. This is likely because the feed has not been polled yet -
150-
please check back later.
151-
</Text>
232+
<Stack>
233+
{dateRangeForm}
234+
<Text color="whiteAlpha.700">No requests found.</Text>
235+
</Stack>
152236
)}
153237
{data && !hasNoData && (
154238
<Stack>
239+
{dateRangeForm}
155240
<Box>
156241
<TableContainer>
157242
<Table size="sm" variant="simple" aria-labelledby="request-history-table-title">

Diff for: services/backend-api/src/features/user-feeds/dto/get-user-feed-requests-input.dto.ts

-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ export class GetUserFeedRequestsInputDto {
1010
limit = 25;
1111

1212
@IsInt()
13-
@Max(1000)
1413
@Min(0)
1514
@Type(() => Number)
1615
@IsOptional()

Diff for: services/backend-api/src/features/user-feeds/user-feeds.controller.ts

+4-6
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
Param,
1010
Patch,
1111
Post,
12+
Query,
1213
Res,
1314
UseFilters,
1415
UseGuards,
@@ -19,7 +20,7 @@ import { NestedFieldPipe } from "../../common/pipes/nested-field.pipe";
1920
import { TransformValidationPipe } from "../../common/pipes/TransformValidationPipe";
2021
import { DiscordAccessToken } from "../discord-auth/decorators/DiscordAccessToken";
2122
import { DiscordOAuth2Guard } from "../discord-auth/guards/DiscordOAuth2.guard";
22-
import { FastifyReply } from "fastify";
23+
import { FastifyReply, FastifyRequest } from "fastify";
2324
import { SessionAccessToken } from "../discord-auth/types/SessionAccessToken.type";
2425

2526
import { ADD_DISCORD_CHANNEL_CONNECTION_ERROR_CODES } from "../feed-connections/filters";
@@ -39,7 +40,6 @@ import {
3940
GetUserFeedDailyLimitOutputDto,
4041
GetUserFeedDeliveryLogsInputDto,
4142
GetUserFeedOutputDto,
42-
GetUserFeedRequestsInputDto,
4343
GetUserFeedsInputDto,
4444
GetUserFeedsOutputDto,
4545
UpdateUserFeedInputDto,
@@ -248,13 +248,11 @@ export class UserFeedsController {
248248
async getFeedRequests(
249249
@Param("feed", GetUserFeedsPipe())
250250
[{ feed }]: GetUserFeedsPipeOutput,
251-
@NestedQuery(TransformValidationPipe)
252-
{ limit, skip }: GetUserFeedRequestsInputDto
251+
@Query() query: FastifyRequest["query"]
253252
) {
254253
return this.userFeedsService.getFeedRequests({
255254
url: feed.url,
256-
limit,
257-
skip,
255+
query: query as Record<string, string>,
258256
feed,
259257
});
260258
}

Diff for: services/backend-api/src/features/user-feeds/user-feeds.service.ts

+3-6
Original file line numberDiff line numberDiff line change
@@ -825,14 +825,12 @@ export class UserFeedsService {
825825

826826
async getFeedRequests({
827827
feed,
828-
skip,
829-
limit,
830828
url,
829+
query,
831830
}: {
832831
feed: UserFeed;
833-
skip: number;
834-
limit: number;
835832
url: string;
833+
query: Record<string, string>;
836834
}) {
837835
const lookupDetails = getFeedRequestLookupDetails({
838836
feed,
@@ -843,8 +841,7 @@ export class UserFeedsService {
843841
});
844842

845843
return this.feedFetcherApiService.getRequests({
846-
limit,
847-
skip,
844+
query,
848845
url: lookupDetails?.url || url,
849846
requestLookupKey: lookupDetails?.key,
850847
});

Diff for: services/backend-api/src/services/feed-fetcher/feed-fetcher-api.service.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -84,14 +84,12 @@ export class FeedFetcherApiService {
8484
}
8585

8686
async getRequests(payload: {
87-
limit: number;
88-
skip: number;
87+
query: Record<string, string>;
8988
url: string;
9089
requestLookupKey?: string;
9190
}) {
9291
const urlParams = new URLSearchParams({
93-
limit: payload.limit.toString(),
94-
skip: payload.skip.toString(),
92+
...payload.query,
9593
url: payload.url,
9694
lookupKey: payload.requestLookupKey || "",
9795
});

Diff for: services/feed-requests/src/feed-fetcher/dto/get-feed-requests-input.dto.ts

+8
Original file line numberDiff line numberDiff line change
@@ -27,4 +27,12 @@ export class GetFeedRequestsInputDto {
2727
@IsString()
2828
@IsOptional()
2929
lookupKey?: string;
30+
31+
@IsString()
32+
@IsOptional()
33+
afterDate?: string;
34+
35+
@IsString()
36+
@IsOptional()
37+
beforeDate?: string;
3038
}

Diff for: services/feed-requests/src/feed-fetcher/feed-fetcher.service.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -105,12 +105,21 @@ export class FeedFetcherService {
105105
);
106106
}
107107

108-
async getRequests({ skip, limit, url, lookupKey }: GetFeedRequestsInputDto) {
108+
async getRequests({
109+
skip,
110+
limit,
111+
url,
112+
lookupKey,
113+
afterDate,
114+
beforeDate,
115+
}: GetFeedRequestsInputDto) {
109116
return this.partitionedRequestsStore.getRequests({
110117
limit,
111118
skip,
112119
url,
113120
lookupKey,
121+
afterDate,
122+
beforeDate,
114123
});
115124
}
116125

Diff for: services/feed-requests/src/partitioned-requests-store/partitioned-requests-store.service.ts

+13-1
Original file line numberDiff line numberDiff line change
@@ -256,11 +256,15 @@ export default class PartitionedRequestsStoreService {
256256
skip,
257257
url,
258258
lookupKey,
259+
afterDate,
260+
beforeDate,
259261
}: {
260262
skip: number;
261263
limit: number;
262264
url: string;
263265
lookupKey?: string;
266+
afterDate?: string;
267+
beforeDate?: string;
264268
}) {
265269
const em = this.orm.em.getConnection();
266270

@@ -278,10 +282,18 @@ export default class PartitionedRequestsStoreService {
278282
`SELECT id, url, created_at, next_retry_date, status, response_status_code,` +
279283
` fetch_options, response_headers, request_initiated_at FROM request_partitioned
280284
WHERE lookup_key = ?
285+
${afterDate ? 'AND created_at >= ?' : ''}
286+
${beforeDate ? 'AND created_at <= ?' : ''}
281287
ORDER BY created_at DESC
282288
LIMIT ?
283289
OFFSET ?`,
284-
[lookupKey || url, limit, skip],
290+
[
291+
lookupKey || url,
292+
...(afterDate ? [afterDate] : []),
293+
...(beforeDate ? [beforeDate] : []),
294+
limit,
295+
skip,
296+
],
285297
);
286298

287299
return results.map((result) => ({

0 commit comments

Comments
 (0)