Skip to content

Commit

Permalink
Merge pull request #811 from navikt/show-alert-when-geography-service…
Browse files Browse the repository at this point in the history
…-is-down

Show alerts when geography service is down
  • Loading branch information
ChristofferKarlsson authored Sep 27, 2024
2 parents 83bed7d + 710dedf commit a5cdfd9
Show file tree
Hide file tree
Showing 13 changed files with 160 additions and 42 deletions.
17 changes: 14 additions & 3 deletions src/app/(sok)/_components/Search.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useEffect, useState } from "react";
import PropTypes from "prop-types";
import { HGrid, Hide, Show, VStack } from "@navikt/ds-react";
import { Alert, HGrid, Hide, Show, VStack } from "@navikt/ds-react";
import { FETCH_SEARCH_WITHIN_DISTANCE_ERROR } from "@/app/(sok)/_utils/fetchTypes";
import SearchResult from "./searchResult/SearchResult";
import DoYouWantToSaveSearch from "./howToPanels/DoYouWantToSaveSearch";
import Feedback from "./feedback/Feedback";
Expand All @@ -12,8 +13,10 @@ import SearchBox from "./searchBox/SearchBox";
import SearchPagination from "./searchResult/SearchPagination";
import MaxResultsBox from "./searchResult/MaxResultsBox";

export default function Search({ searchResult, aggregations, locations, postcodes, resultsPerPage }) {
export default function Search({ searchResult, aggregations, locations, postcodes, resultsPerPage, errors }) {
const [isFiltersVisible, setIsFiltersVisible] = useState(false);
const failedToSearchForPostcodes =
errors.length > 0 && errors.find((error) => error.type === FETCH_SEARCH_WITHIN_DISTANCE_ERROR);

useEffect(() => {
logAmplitudeEvent("Stillinger - Utførte søk");
Expand All @@ -22,7 +25,6 @@ export default function Search({ searchResult, aggregations, locations, postcode
return (
<div className="mb-24">
<SearchBox aggregations={aggregations} locations={locations} postcodes={postcodes} />

<SearchResultHeader
setIsFiltersVisible={setIsFiltersVisible}
isFiltersVisible={isFiltersVisible}
Expand All @@ -40,6 +42,7 @@ export default function Search({ searchResult, aggregations, locations, postcode
locations={locations}
postcodes={postcodes}
searchResult={searchResult}
errors={errors}
/>
</Hide>

Expand All @@ -51,11 +54,19 @@ export default function Search({ searchResult, aggregations, locations, postcode
postcodes={postcodes}
onCloseClick={() => setIsFiltersVisible(false)}
searchResult={searchResult}
errors={errors}
/>
)}
</Show>

<VStack gap="10">
{failedToSearchForPostcodes && (
<Alert variant="warning">
Reisevei-filteret er midlertidig utilgjengelig og påvirker ikke søkeresultatene. For å
avgrense søket, bruk kommune- eller fylkesfilteret.
</Alert>
)}

<SearchResult searchResult={searchResult} />
<MaxResultsBox resultsPerPage={resultsPerPage} />
<SearchPagination searchResult={searchResult} resultsPerPage={resultsPerPage} />
Expand Down
3 changes: 2 additions & 1 deletion src/app/(sok)/_components/SearchWrapper.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import PropTypes from "prop-types";
import Search from "@/app/(sok)/_components/Search";
import { SearchQueryProvider } from "@/app/(sok)/_components/SearchQueryProvider";

export default function SearchWrapper({ searchResult, aggregations, locations, postcodes, resultsPerPage }) {
export default function SearchWrapper({ searchResult, aggregations, locations, postcodes, resultsPerPage, errors }) {
return (
<SearchQueryProvider>
<Search
Expand All @@ -14,6 +14,7 @@ export default function SearchWrapper({ searchResult, aggregations, locations, p
aggregations={aggregations}
postcodes={postcodes}
resultsPerPage={resultsPerPage}
errors={errors}
/>
</SearchQueryProvider>
);
Expand Down
6 changes: 4 additions & 2 deletions src/app/(sok)/_components/filters/DistanceOrLocation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { CarIcon, LocationPinIcon } from "@navikt/aksel-icons";
import { Postcode } from "@/app/(sok)/_utils/fetchPostcodes";
import { UserPreferencesContext } from "@/app/_common/user/UserPreferenceProvider";
import SearchResult from "@/app/(sok)/_types/SearchResult";
import { FetchError } from "@/app/(sok)/_utils/fetchTypes";
import Counties from "./Locations";

// TODO: Fix disable no-explicit-any when new search field branch is merged
Expand All @@ -14,9 +15,10 @@ interface DistanceOrLocationProps {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
locations: any;
searchResult: SearchResult;
errors: FetchError[];
}

function DistanceOrLocation({ postcodes, locations, searchResult }: DistanceOrLocationProps): ReactElement {
function DistanceOrLocation({ postcodes, locations, searchResult, errors }: DistanceOrLocationProps): ReactElement {
const { locationOrDistance } = useContext(UserPreferencesContext);
const [selectedOption, setSelectedOption] = useState(locationOrDistance || "distance");

Expand All @@ -41,7 +43,7 @@ function DistanceOrLocation({ postcodes, locations, searchResult }: DistanceOrLo
label="Sted"
/>
</ToggleGroup>
{selectedOption === "distance" && <DrivingDistance postcodes={postcodes} />}
{selectedOption === "distance" && <DrivingDistance postcodes={postcodes} errors={errors} />}
{selectedOption === "location" && (
<Counties locations={locations} updatedValues={searchResult.aggregations} />
)}
Expand Down
15 changes: 13 additions & 2 deletions src/app/(sok)/_components/filters/DrivingDistance.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import React, { ReactElement, useEffect, useState } from "react";
import { BodyShort, Button, Fieldset, Select, UNSAFE_Combobox } from "@navikt/ds-react";
import { Alert, BodyShort, Button, Fieldset, Select, UNSAFE_Combobox } from "@navikt/ds-react";
import { TrashIcon } from "@navikt/aksel-icons";
import { Postcode } from "@/app/(sok)/_utils/fetchPostcodes";
import { ComboboxOption } from "@navikt/ds-react/esm/form/combobox/types";
import "./DrivingDistance.css";
import { logFilterChanged } from "@/app/_common/monitoring/amplitude";
import useSearchQuery from "@/app/(sok)/_components/SearchQueryProvider";
import { DISTANCE, POSTCODE } from "@/app/(sok)/_components/searchParamNames";
import { FETCH_POSTCODES_ERROR, FetchError } from "@/app/(sok)/_utils/fetchTypes";

interface DrivingDistanceProps {
postcodes: Postcode[];
errors: FetchError[];
}

function DrivingDistance({ postcodes }: DrivingDistanceProps): ReactElement {
function DrivingDistance({ postcodes, errors }: DrivingDistanceProps): ReactElement {
const searchQuery = useSearchQuery();
const [selectedPostcode, setSelectedPostcode] = useState<ComboboxOption[] | string[]>(searchQuery.getAll(POSTCODE));
const [filteredPostcodeOptions, setFilteredPostcodeOptions] = useState<ComboboxOption[]>([]);
Expand Down Expand Up @@ -106,6 +108,9 @@ function DrivingDistance({ postcodes }: DrivingDistanceProps): ReactElement {
searchQuery.remove(DISTANCE);
}

const failedToFetchPostcodes =
errors.length > 0 && errors.find((error) => error.type === FETCH_POSTCODES_ERROR) !== undefined;

return (
<Fieldset
legend={
Expand All @@ -115,6 +120,12 @@ function DrivingDistance({ postcodes }: DrivingDistanceProps): ReactElement {
}
className="FilterModal__fieldset mt-2"
>
{failedToFetchPostcodes && (
<Alert variant="warning" className="mb-4" inline>
Reisevei-filteret er midlertidig utilgjengelig og påvirker ikke søkeresultatene. For å avgrense
søket, bruk kommune- eller fylkesfilteret.
</Alert>
)}
<UNSAFE_Combobox
label="Fra sted eller postnummer"
className="Combobox"
Expand Down
10 changes: 9 additions & 1 deletion src/app/(sok)/_components/filters/FiltersDesktop.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import DistanceOrLocation from "@/app/(sok)/_components/filters/DistanceOrLocati
import FilterAggregations from "@/app/(sok)/_types/FilterAggregations";
import SearchResult from "@/app/(sok)/_types/SearchResult";
import { Postcode } from "@/app/(sok)/_utils/fetchPostcodes";
import { FetchError } from "@/app/(sok)/_utils/fetchTypes";
import FilterAccordionItem from "./FilterAccordionItem";
import Published from "./Published";
import Occupations from "./Occupations";
Expand All @@ -22,13 +23,15 @@ interface FiltersDesktopProps {
locations: [];
postcodes: Postcode[];
searchResult: SearchResult;
errors: FetchError[];
}

export default function FiltersDesktop({
aggregations,
locations,
postcodes,
searchResult,
errors,
}: FiltersDesktopProps): ReactElement {
return (
<div>
Expand All @@ -41,7 +44,12 @@ export default function FiltersDesktop({
/>
</FilterAccordionItem>
<FilterAccordionItem title="Sted" panelId="sted">
<DistanceOrLocation postcodes={postcodes} locations={locations} searchResult={searchResult} />
<DistanceOrLocation
postcodes={postcodes}
locations={locations}
searchResult={searchResult}
errors={errors}
/>
</FilterAccordionItem>
<FilterAccordionItem title="Yrkeskategori og sektor" panelId="yrke">
<Occupations
Expand Down
9 changes: 7 additions & 2 deletions src/app/(sok)/_components/filters/FiltersMobile.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import Sector from "./Sector";
import EngagementType from "./Engagement";
import WorkLanguage from "./WorkLanguage";

function FiltersMobile({ onCloseClick, searchResult, aggregations, locations, postcodes }) {
function FiltersMobile({ onCloseClick, searchResult, aggregations, locations, postcodes, errors }) {
const [selectedFilter, setSelectedFilter] = useState("");
const headingRef = useRef();

Expand Down Expand Up @@ -87,7 +87,12 @@ function FiltersMobile({ onCloseClick, searchResult, aggregations, locations, po
)}

{selectedFilter === "Sted" && (
<DistanceOrLocation postcodes={postcodes} locations={locations} searchResult={searchResult} />
<DistanceOrLocation
postcodes={postcodes}
locations={locations}
searchResult={searchResult}
errors={errors}
/>
)}

{selectedFilter === "Yrkeskategori og sektor" && (
Expand Down
4 changes: 3 additions & 1 deletion src/app/(sok)/_components/searchBox/SearchBox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ function SearchBox({ aggregations, locations, postcodes }) {
savedSearchUrlWithoutVersion.delete(URL_VERSION);
const showSaveAndResetButton = savedSearchUrlWithoutVersion.size > 0 && !onlyPostcodeOrDistanceFilterActive;
const chosenPostcodeCity =
drivingDistanceFilterActive && postcodes.find((p) => p.postcode === searchQuery.get(POSTCODE)).city;
drivingDistanceFilterActive &&
postcodes.size > 0 &&
postcodes.find((p) => p.postcode === searchQuery.get(POSTCODE)).city;

return (
<Box paddingBlock={{ xs: "0 6", lg: "10 12" }}>
Expand Down
40 changes: 32 additions & 8 deletions src/app/(sok)/_utils/fetchElasticSearch.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
"use server";

import elasticSearchRequestBody from "@/app/(sok)/_utils/elasticSearchRequestBody";
import { getDefaultHeaders } from "@/app/_common/utils/fetch";
import simplifySearchResponse from "@/app/(sok)/_utils/simplifySearchResponse";
Expand All @@ -16,15 +18,30 @@ We can't use the built-in 'cache' in React either, since the route segment is dy
https://nextjs.org/docs/app/building-your-application/data-fetching/fetching-caching-and-revalidating#fetching-data-on-the-server-with-third-party-libraries
*/

export async function fetchElasticSearch(query, fetchOptions = {}) {
export async function fetchElasticSearch(query, fetchOptions = {}, performSearchIfDrivingDistanceError = true) {
const elasticSearchQuery = query;
const shouldLookupLocationsWithinDrivingDistance = elasticSearchQuery.postcode && elasticSearchQuery.distance;
const errors = [];

if (shouldLookupLocationsWithinDrivingDistance) {
elasticSearchQuery.withinDrivingDistance = await fetchLocationsWithinDrivingDistance(
const withinDrivingDistanceResult = await fetchLocationsWithinDrivingDistance(
elasticSearchQuery.postcode,
elasticSearchQuery.distance,
);

if (withinDrivingDistanceResult.data) {
elasticSearchQuery.withinDrivingDistance = withinDrivingDistanceResult.data;
}

if (withinDrivingDistanceResult.errors) {
errors.push(...withinDrivingDistanceResult.errors);

if (!performSearchIfDrivingDistanceError) {
return {
errors: errors,
};
}
}
}
const measureSearchDuration = elasticSearchDurationHistogram.startTimer();

Expand All @@ -40,7 +57,10 @@ export async function fetchElasticSearch(query, fetchOptions = {}) {

incrementElasticSearchRequests(res.ok);

return res;
return {
errors: errors,
response: res,
};
}

export const fetchCachedSimplifiedElasticSearch = unstable_cache(
Expand All @@ -52,13 +72,17 @@ export const fetchCachedSimplifiedElasticSearch = unstable_cache(
);

async function fetchSimplifiedElasticSearch(query) {
const res = await fetchElasticSearch(query);
const result = await fetchElasticSearch(query);
const { response } = result;

if (!res.ok) {
throw new Error(`Failed to fetch data: ${res.status}`);
if (!response.ok) {
throw new Error(`Failed to fetch data from elastic search: ${response.status}`);
}

const data = await res.json();
const data = await response.json();

return simplifySearchResponse(data);
return {
data: simplifySearchResponse(data),
errors: result.errors,
};
}
16 changes: 12 additions & 4 deletions src/app/(sok)/_utils/fetchLocationsWithinDrivingDistance.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
"use server";

import { getDefaultHeaders } from "@/app/_common/utils/fetch";
import { logger } from "@sentry/utils";
import { FETCH_SEARCH_WITHIN_DISTANCE_ERROR, FetchResult } from "./fetchTypes";

export interface Locations {
postcodes: string[];
Expand All @@ -15,7 +19,7 @@ interface AvstandApiDto {
export async function fetchLocationsWithinDrivingDistance(
referencePostCode: string,
distance: number,
): Promise<Locations> {
): Promise<FetchResult<Locations>> {
const res = await fetch(
`${process.env.PAM_GEOGRAFI_API_URL}/innen-avstand/${referencePostCode}?avstand=${distance}`,
{
Expand All @@ -24,14 +28,18 @@ export async function fetchLocationsWithinDrivingDistance(
);

if (!res.ok) {
throw new Error(`Failed to fetch within distance data: ${res.status} ${res.statusText}`);
logger.error(`Failed to fetch within distance data: ${res.status} ${res.statusText}`);
return {
errors: [{ type: FETCH_SEARCH_WITHIN_DISTANCE_ERROR }],
};
}

const data: AvstandApiDto = await res.json();

return {
const locations = {
postcodes: data.postnummer,
municipals: data.kommuner,
counties: data.fylker,
};

return { data: locations };
}
36 changes: 30 additions & 6 deletions src/app/(sok)/_utils/fetchPostcodes.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
"use server";

import { getDefaultHeaders } from "@/app/_common/utils/fetch";
import { unstable_cache } from "next/cache";
import { revalidateTag, unstable_cache } from "next/cache";
import logger from "@/app/_common/utils/logger";
import { FETCH_POSTCODES_ERROR, FetchResult } from "./fetchTypes";

export interface Postcode {
postcode: string;
Expand All @@ -13,23 +17,43 @@ interface PostdataDto {
// fylke: FylkeDTO
}

async function fetchPostcodes(): Promise<Postcode[]> {
async function fetchPostcodes(): Promise<FetchResult<Postcode[]>> {
const res = await fetch(`${process.env.PAM_GEOGRAFI_API_URL}/postdata?sort=asc`, {
headers: getDefaultHeaders(),
});

if (!res.ok) {
throw new Error("Failed to fetch postcode data");
logger.error(`Failed to fetch postcode data: ${res.status} ${res.statusText}`);
return {
errors: [{ type: FETCH_POSTCODES_ERROR }],
data: [],
};
}

const data: PostdataDto[] = await res.json();

return data.map((postdata) => ({
const postcodes = data.map((postdata) => ({
postcode: postdata.postkode,
city: postdata.by,
}));

return {
data: postcodes,
};
}

export const fetchCachedPostcodes = unstable_cache(async () => fetchPostcodes(), ["postcodes-query"], {
const CACHE_KEY = "postcodes-query";

const fetchCachedPostcodesInternal = unstable_cache(async () => fetchPostcodes(), [CACHE_KEY], {
revalidate: 3600,
});

export async function fetchCachedPostcodes(): Promise<FetchResult<Postcode[]>> {
const result = await fetchCachedPostcodesInternal();

if (result.errors && result.errors.length > 0) {
logger.warn("Errors when fetching postcodes, manually purging cache");
revalidateTag(CACHE_KEY);
}

return result;
}
Loading

0 comments on commit a5cdfd9

Please sign in to comment.