demarches-normaliennes/app/javascript/components/shared/queryClient.ts

102 lines
2.8 KiB
TypeScript
Raw Normal View History

import { QueryClient, QueryFunction } from 'react-query';
import { httpRequest, isNumeric, getConfig } from '@utils';
2021-02-16 13:17:36 +01:00
import { matchSorter } from 'match-sorter';
2020-10-07 17:41:20 +02:00
const API_EDUCATION_QUERY_LIMIT = 5;
const API_GEO_QUERY_LIMIT = 5;
const API_ADRESSE_QUERY_LIMIT = 5;
// When searching for short strings like "mer", le exact match shows up quite far in
// the ordering (~50).
//
// That's why we deliberately fetch a lot of results, and then let the local matching
// (match-sorter) do the work.
//
// NB: 60 is arbitrary, we may add more if needed.
const API_GEO_COMMUNES_QUERY_LIMIT = 60;
const {
autocomplete: { api_geo_url, api_adresse_url, api_education_url }
} = getConfig();
2020-10-07 17:41:20 +02:00
type QueryKey = readonly [
scope: string,
term: string,
extra: string | undefined
];
2020-10-07 17:41:20 +02:00
function buildURL(scope: string, term: string, extra?: string) {
term = encodeURIComponent(term.replace(/\(|\)/g, ''));
const params = new URLSearchParams();
let path = `${api_geo_url}/${scope}`;
if (scope == 'adresse') {
path = `${api_adresse_url}/search`;
params.set('q', term);
params.set('limit', `${API_ADRESSE_QUERY_LIMIT}`);
} else if (scope == 'annuaire-education') {
path = `${api_education_url}/search`;
params.set('q', term);
params.set('rows', `${API_EDUCATION_QUERY_LIMIT}`);
params.set('dataset', 'fr-en-annuaire-education');
} else if (scope == 'communes') {
if (extra) {
params.set('codeDepartement', extra);
}
if (isNumeric(term)) {
params.set('codePostal', term);
} else {
params.set('nom', term);
params.set('boost', 'population');
}
params.set('limit', `${API_GEO_COMMUNES_QUERY_LIMIT}`);
} else {
2021-04-20 12:55:07 +02:00
if (isNumeric(term)) {
params.set('code', term.padStart(2, '0'));
} else {
params.set('nom', term);
}
if (scope == 'departements') {
params.set('zone', 'metro,drom,com');
2021-04-20 12:55:07 +02:00
}
params.set('limit', `${API_GEO_QUERY_LIMIT}`);
2020-10-07 17:41:20 +02:00
}
return `${path}?${params}`;
2020-10-07 17:41:20 +02:00
}
const defaultQueryFn: QueryFunction<unknown, QueryKey> = async ({
queryKey: [scope, term, extra],
signal
}) => {
2020-11-25 15:17:59 +01:00
if (scope == 'pays') {
return matchSorter(await getPays(signal), term, { keys: ['label'] });
2020-11-25 15:17:59 +01:00
}
const url = buildURL(scope, term, extra);
return httpRequest(url, { csrf: false, signal }).json();
};
2020-11-30 10:53:55 +01:00
let paysCache: { label: string }[];
async function getPays(signal?: AbortSignal): Promise<{ label: string }[]> {
2020-11-30 10:53:55 +01:00
if (!paysCache) {
const data = await httpRequest('/api/pays', { signal }).json<
typeof paysCache
>();
if (data) {
paysCache = data;
}
2020-11-30 10:53:55 +01:00
}
return paysCache;
}
export const queryClient = new QueryClient({
defaultOptions: {
queries: {
// we don't really care about global queryFn type
// eslint-disable-next-line @typescript-eslint/no-explicit-any
queryFn: defaultQueryFn as any
}
}
});