zaphyra's git: oeffisearch

fast and simple tripplanner

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 
import { parse as parseContentType } from 'content-type';
import { HafasError } from 'db-vendo-client/lib/errors.js';

export const
	checkIfVendoResponseIsOk = (_) => {
		const
			{ body, errProps: baseErrProps } = _,
			errProps = { ...baseErrProps };

		if (body.id)
			errProps.hafasResponseId = body.id;

		// Because we want more accurate stack traces, we don't construct the error here,
		// but only return the constructor & error message.
		const getError = (_) => {
			// mutating here is ugly but pragmatic
			if (_.fehlerNachricht.ueberschrift) errProps.hafasMessage = _.fehlerNachricht.ueberschrift;
			if (_.fehlerNachricht.text) errProps.hafasDescription = _.fehlerNachricht.text;

			return {
				Error: HafasError,
				message: errProps.hafasMessage || 'unknown error',
				props: { code: _.fehlerNachricht.code },
			};
		};

		if (body.fehlerNachricht || body.errors) { // TODO better handling
			const {Error: HafasError, message, props} = getError(body);
			throw new HafasError(message, body.err || body.errors, {...errProps, ...props});
		}
	},

	vendoRequest = async (ctx, userAgent, reqData) => {
		const
			{profile, opt} = ctx,
			endpoint = reqData.endpoint;

		delete reqData.endpoint;

		const
			rawReqBody = profile.transformReqBody(ctx, reqData.body),
			reqOptions = profile.transformReq(ctx, {
				agent: null,
				keepalive: true,
				method: reqData.method,
				redirect: 'follow',
				body: JSON.stringify(rawReqBody),
				query: reqData.query,
				headers: {
					'Content-Type': 'application/json',
					// 'Accept-Encoding': 'gzip, deflate, br, zstd',
					'Accept': 'application/json',
					'Accept-Language': opt.language || profile.defaultLanguage || 'en',
					'user-agent': userAgent,
					...reqData.headers,
				},
			}),

			url = `${[ endpoint, reqData?.path ].filter(x => typeof x === 'string' && x.length > 0).join('/')}?${(new URLSearchParams(reqOptions.query)).toString()}`,
			fetchReq = new Request(url, reqOptions),
			res = await fetch(url, reqOptions),
			errProps = {
				// todo [breaking]: assign as non-enumerable property
				request: fetchReq,
				// todo [breaking]: assign as non-enumerable property
				response: res,
				url,
			};

		if (!res.ok) {
			// todo [breaking]: make this a FetchError or a HafasClientError?
			console.log(JSON.stringify(res), await res.text());
			const err = new Error(res.statusText);
			Object.assign(err, errProps);
			throw err;
		}

		const cType = res.headers.get('content-type');
		if (cType) {
			const { type } = parseContentType(cType);
			// For some reason, the reqOptions.headers object is sometimes a plain object
			// and sometimes a Headers object (In browser env). For the latter, .get() must
			// be used.
			if (type !== reqOptions.headers['Accept'] && type !== reqOptions.headers.get('Accept'))
				throw new HafasError('invalid/unsupported response content-type: ' + cType, null, errProps);
		}

		const body = JSON.parse(await res.text());

		checkIfVendoResponseIsOk({ body, errProps });

		return {
			res: body,
			common: {},
		};
	};