Skip to content

Commit d1d1693

Browse files
committed
Add support for GET pagination [fix #14]
1 parent 05f54e2 commit d1d1693

File tree

3 files changed

+114
-23
lines changed

3 files changed

+114
-23
lines changed

src/hooks/useStacSearch.test.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,4 +335,60 @@ describe('useStacSearch', () => {
335335
const postHeader = fetch.mock.calls[1][1]?.headers;
336336
expect(postHeader).toEqual({ 'Content-Type': 'application/json', next: '123abc' });
337337
});
338+
339+
it('loads next-page from GET request', async () => {
340+
const response = {
341+
links: [{
342+
rel: 'next',
343+
href: 'https://fake-stac-api.net/?page=2'
344+
}]
345+
};
346+
fetch.mockResponseOnce(JSON.stringify(response));
347+
348+
const { result, waitForNextUpdate } = renderHook(
349+
() => useStacSearch(stacApi)
350+
);
351+
352+
act(() => result.current.setDateRangeTo('2022-05-17'));
353+
act(() => result.current.submit());
354+
await waitForNextUpdate(); // wait to set results
355+
expect(result.current.results).toEqual(response);
356+
expect(result.current.nextPage).toBeDefined();
357+
358+
fetch.mockResponseOnce(JSON.stringify({ data: '12345' }));
359+
act(() => result.current.nextPage && result.current.nextPage());
360+
await waitForNextUpdate();
361+
362+
expect(fetch.mock.calls[1][0]).toEqual('https://fake-stac-api.net/?page=2');
363+
expect(fetch.mock.calls[1][1]?.method).toEqual('GET');
364+
expect(result.current.results).toEqual({ data: '12345' });
365+
});
366+
367+
it('loads prev-page from GET request', async () => {
368+
const response = {
369+
links: [{
370+
rel: 'prev',
371+
href: 'https://fake-stac-api.net/?page=2'
372+
}]
373+
};
374+
fetch.mockResponseOnce(JSON.stringify(response));
375+
376+
const { result, waitForNextUpdate } = renderHook(
377+
() => useStacSearch(stacApi)
378+
);
379+
380+
act(() => result.current.setDateRangeTo('2022-05-17'));
381+
act(() => result.current.submit());
382+
await waitForNextUpdate(); // wait to set results
383+
expect(result.current.results).toEqual(response);
384+
expect(result.current.previousPage).toBeDefined();
385+
386+
fetch.mockResponseOnce(JSON.stringify({ data: '12345' }));
387+
act(() => result.current.previousPage && result.current.previousPage());
388+
await waitForNextUpdate();
389+
390+
expect(fetch.mock.calls[1][0]).toEqual('https://fake-stac-api.net/?page=2');
391+
expect(fetch.mock.calls[1][1]?.method).toEqual('GET');
392+
expect(result.current.results).toEqual({ data: '12345' });
393+
});
338394
});

src/hooks/useStacSearch.ts

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -42,13 +42,19 @@ function useStacSearch(stacApi: StacApi): StacSearchHook {
4242
const [ nextPageConfig, setNextPageConfig ] = useState<Link>();
4343
const [ previousPageConfig, setPreviousPageConfig ] = useState<Link>();
4444

45+
/**
46+
* Extracts the pagination config from the the links array of the items response
47+
*/
4548
const setPaginationConfig = useCallback(
4649
(links: Link[]) => {
4750
setNextPageConfig(links.find(({ rel }) => rel === 'next'));
4851
setPreviousPageConfig(links.find(({ rel }) => ['prev', 'previous'].includes(rel)));
4952
}, []
5053
);
5154

55+
/**
56+
* Returns the search payload based on the current application state
57+
*/
5258
const getSearchPayload = useCallback(
5359
() => ({
5460
bbox,
@@ -58,40 +64,65 @@ function useStacSearch(stacApi: StacApi): StacSearchHook {
5864
[ bbox, collections, dateRangeFrom, dateRangeTo ]
5965
);
6066

67+
/**
68+
* Resets the state and processes the results from the provided request
69+
*/
70+
const processRequest = useCallback((request: Promise<Response>) => {
71+
setResults(undefined);
72+
setState('LOADING');
73+
setError(undefined);
74+
setNextPageConfig(undefined);
75+
setPreviousPageConfig(undefined);
76+
77+
request
78+
.then(response => response.json())
79+
.then(data => {
80+
setResults(data);
81+
setPaginationConfig(data.links);
82+
})
83+
.catch((err) => setError(err))
84+
.finally(() => setState('IDLE'));
85+
}, [setPaginationConfig]);
86+
87+
/**
88+
* Executes a POST request against the `search` endpoint using the provided payload and headers
89+
*/
6190
const executeSearch = useCallback(
62-
(payload: SearchPayload, headers = {}) => {
63-
setResults(undefined);
64-
setState('LOADING');
65-
setError(undefined);
66-
setNextPageConfig(undefined);
67-
setPreviousPageConfig(undefined);
91+
(payload: SearchPayload, headers = {}) => processRequest(stacApi.search(payload, headers)),
92+
[stacApi, processRequest]
93+
);
6894

69-
stacApi.search(payload, headers)
70-
.then(response => response.json())
71-
.then(data => {
72-
setResults(data);
73-
setPaginationConfig(data.links);
74-
})
75-
.catch((err) => setError(err))
76-
.finally(() => setState('IDLE'));
77-
},
78-
[stacApi, setPaginationConfig]
95+
/**
96+
* Execute a GET request against the provided URL
97+
*/
98+
const getItems = useCallback(
99+
(url: string) => processRequest(stacApi.get(url)),
100+
[stacApi, processRequest]
79101
);
80102

103+
/**
104+
* Retreives a page from a paginatied item set using the provided link config.
105+
* Executes a POST request against the `search` endpoint if pagination uses POST
106+
* or retrieves the page items using GET against the link href
107+
*/
81108
const flipPage = useCallback(
82109
(link?: Link) => {
83110
if (link) {
84111
let payload = link.body as LinkBody;
85-
if (payload.merge) {
86-
payload = {
87-
...payload,
88-
...getSearchPayload()
89-
};
112+
if (payload) {
113+
if (payload.merge) {
114+
payload = {
115+
...payload,
116+
...getSearchPayload()
117+
};
118+
}
119+
executeSearch(payload, link.headers);
120+
} else {
121+
getItems(link.href);
90122
}
91-
executeSearch(payload, link.headers);
92123
}
93124
},
94-
[executeSearch, getSearchPayload]
125+
[executeSearch, getItems, getSearchPayload]
95126
);
96127

97128
const nextPageFn = useCallback(

src/stac-api/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,10 @@ class StacApi {
109109
getCollections(): Promise<Response> {
110110
return this.fetch(`${this.baseUrl}/collections`);
111111
}
112+
113+
get(href: string, headers = {}): Promise<Response> {
114+
return this.fetch(href, { headers });
115+
}
112116
}
113117

114118
export default StacApi;

0 commit comments

Comments
 (0)