-
-
Notifications
You must be signed in to change notification settings - Fork 132
/
dataAccess.ts
122 lines (96 loc) · 3.82 KB
/
dataAccess.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import isomorphicFetch from "isomorphic-unfetch";
import { PagedCollection } from "../types/collection";
import { Item } from "../types/item";
import { ENTRYPOINT } from "../config/entrypoint";
const MIME_TYPE = "application/ld+json";
interface Violation {
message: string;
propertyPath: string;
}
export interface FetchResponse<TData> {
hubURL: string | null;
data: TData;
text: string;
}
export interface FetchError {
message: string;
status: string;
fields: {[key: string]: string};
}
const extractHubURL = (response: Response): null | URL => {
const linkHeader = response.headers.get("Link");
if (!linkHeader) return null;
const matches = linkHeader.match(
/<([^>]+)>;\s+rel=(?:mercure|"[^"]*mercure[^"]*")/
);
return matches && matches[1] ? new URL(matches[1], ENTRYPOINT) : null;
};
export const fetch = async <TData>(id: string, init: RequestInit = {}): Promise<FetchResponse<TData>|undefined> => {
if (typeof init.headers === "undefined") init.headers = {};
if (!init.headers.hasOwnProperty("Accept"))
init.headers = { ...init.headers, Accept: MIME_TYPE };
if (
init.body !== undefined &&
!(init.body instanceof FormData) &&
!init.headers?.hasOwnProperty("Content-Type")
)
init.headers = { ...init.headers, "Content-Type": MIME_TYPE };
const resp = await isomorphicFetch(ENTRYPOINT + id, init);
if (resp.status === 204) return;
const text = await resp.text();
const json = JSON.parse(text);
if (resp.ok) {
return {
hubURL: extractHubURL(resp)?.toString() || null, // URL cannot be serialized as JSON, must be sent as string
data: json,
text,
};
}
const errorMessage = json["{{{hydraPrefix}}}title"];
const status = json["{{{hydraPrefix}}}description"] || resp.statusText;
if (!json.violations) throw Error(errorMessage);
const fields: { [key: string]: string } = {};
json.violations.map(
(violation: Violation) =>
(fields[violation.propertyPath] = violation.message)
);
throw { message: errorMessage, status, fields } as FetchError;
};
export const getItemPath = (iri: string | undefined, pathTemplate: string): string => {
if (!iri) {
return '';
}
const resourceId = iri.split('/').slice(-1)[0];
return pathTemplate.replace('[id]', resourceId);
}
export const parsePage = (resourceName: string, path: string) => parseInt(new RegExp(`^/${resourceName}\\?page=(\\d+)`).exec(path)?.[1] ?? '1', 10);
export const getItemPaths = async <TData extends Item>(response: FetchResponse<PagedCollection<TData>> | undefined, resourceName: string, pathTemplate: string) => {
if (!response) return [];
try {
const view = response.data["{{{hydraPrefix}}}view"];
const { "{{{hydraPrefix}}}last": last } = view ?? {};
const paths = response.data["{{{hydraPrefix}}}member"]?.map((resourceData) => getItemPath(resourceData['@id'] ?? '', pathTemplate)) ?? [];
const lastPage = parsePage(resourceName, last ?? '');
for (let page = 2; page <= lastPage; page++) {
paths.push(
...(await fetch<PagedCollection<TData>>(`/${resourceName}?page=${page}`))
?.data["{{{hydraPrefix}}}member"]?.map((resourceData) => getItemPath(resourceData['@id'] ?? '', pathTemplate)) ?? []
);
}
return paths;
} catch (e) {
console.error(e);
return [];
}
};
export const getCollectionPaths = async <TData extends Item>(response: FetchResponse<PagedCollection<TData>> | undefined, resourceName: string, pathTemplate: string) => {
if (!response) return [];
const view = response.data["{{{hydraPrefix}}}view"];
const { "{{{hydraPrefix}}}last": last } = view ?? {};
const paths = [pathTemplate.replace('[page]', '1')];
const lastPage = parsePage(resourceName, last ?? '');
for (let page = 2; page <= lastPage; page++) {
paths.push(pathTemplate.replace('[page]', page.toString()));
}
return paths;
};