-
Notifications
You must be signed in to change notification settings - Fork 22
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Issue #3518] download search results #3689
Conversation
@@ -0,0 +1,11 @@ | |||
import { OpportunityApiResponse } from "src/types/opportunity/opportunityResponseTypes"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since the bare fetcher no longer returns the json result, we'll create a basic wrapper here.
@@ -19,7 +19,7 @@ import ServerErrorAlert from "src/components/ServerErrorAlert"; | |||
|
|||
export interface ParsedError { | |||
message: string; | |||
searchInputs: ServerSideSearchParams; | |||
searchInputs: OptionalStringDict; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
just a rename to reflect that this type is generic rather than specific in any way
|
||
*/ | ||
|
||
export async function GET(request: NextRequest) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
making API request here in order to avoid exposing the API key
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like a shame we have to do this. Maybe at some point we can open up some of the APIs for the general public.
Noting the API is a POST
while we're using a GET
, I think GET
is the correct interpretation for search since a search request isn't meaningfully creating or updating server entities.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree it's not great, but the best option we have for now. We should maybe talk as a team about how we want to handle this sort of thing.
As far as the GET v POST, I'd be fine to make the first request as POST as well, but since it's largely internal and not sending a request body, a GET seemed fine. The search itself being a post makes sense to me since we're sending data in the body of the request.
* Send a request and handle the response | ||
* @param queryParamData: note that this is only used in error handling in order to help restore original page state | ||
*/ | ||
export async function sendRequest<ResponseType extends APIResponse>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
since this function wasn't doing much, and what it was doing (reading a json response body) was making our implementation less flexible, I got rid of it and moved all this functionality either into the fetcher generator or endpoint specific wrappers
// note that this will pass along filter inputs in order to maintain the state | ||
// of the page when relaying an error, but anything passed in the body of the request, | ||
// such as keyword search query will not be included | ||
function handleNotOkResponse( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
similar to the above, this wasn't doing much, moved the behavior into the fetcher generator
|
||
return response; | ||
}; | ||
} | ||
|
||
export const fetchOpportunity = cache( | ||
requesterForEndpoint<OpportunityApiResponse>(fetchOpportunityEndpoint), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these all return Response
now, so the generic isn't necessary any more
@@ -7,7 +7,7 @@ import { | |||
|
|||
test.describe("Search page tests", () => { | |||
// Loadiing indicator resolves too quickly to reliably test in e2e. | |||
skip("should show and hide loading state", async () => { | |||
test.skip("should show and hide loading state", async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the other implementation didn't seem to actually skip the test?
@@ -156,12 +156,12 @@ test.describe("Search page tests", () => { | |||
page, | |||
}: PageProps) => { | |||
await page.goto("/search"); | |||
await waitForSearchResultsInitialLoad(page); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
not sure why, but these tests got flaky. Adding the wait on initial page load seems to solve the problem
@@ -16,9 +14,6 @@ interface OpportunityDocumentsProps { | |||
documents: OpportunityDocument[]; | |||
} | |||
|
|||
dayjs.extend(advancedFormat); | |||
dayjs.extend(timezone); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved this into a central location
@@ -23,3 +30,5 @@ export function formatDate(dateStr: string | null): string { | |||
}; | |||
return date.toLocaleDateString("en-US", options); | |||
} | |||
|
|||
export const getConfiguredDayJs = () => dayjs; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to support the timestamp in the filename
@@ -11,6 +11,8 @@ interface IconProps { | |||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access | |||
const sprite_uri = SpriteSVG.src as string; | |||
|
|||
// height prop doesn't seem to work |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like this could be discussion rather than a code comment. The USWDS component doesn't work with SVGs as far as I recall. We could revisit that, was a while ago this was adopted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
true, I'll remove the comment. Ticket for follow up here: #3748
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great.
Summary
Fixes #3158
Time to review: 30 mins
Changes proposed
The goal here is adding a button to the search page that will allow users to download a csv file containing all (well, the first 5000) search results.
To accomplish this a few larger refactors were made:
Context for reviewers
Test steps
seed_local_db.py
to up the size of each type by a bunch. Try updatingsize=5
tosize=50
. This is important for testing with a full width pagination. Then runmake db-seed-local populate-search-opportunities
npm run dev
grants-search-<timestamp>.csv
)Additional information