UNPKG

jinaga

Version:

Data management for web and mobile applications.

272 lines 12 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; Object.defineProperty(exports, "__esModule", { value: true }); exports.FetchConnection = void 0; const trace_1 = require("../util/trace"); const ContentType_1 = require("./ContentType"); class FetchConnection { constructor(url, getHeaders, reauthenticate) { this.url = url; this.getHeaders = getHeaders; this.reauthenticate = reauthenticate; } get(path) { return trace_1.Trace.dependency('GET', path, () => __awaiter(this, void 0, void 0, function* () { let headers = yield this.getHeaders(); let response = yield this.httpGet(path, headers); if (response.statusCode === 401 || response.statusCode === 407 || response.statusCode === 419) { const retry = yield this.reauthenticate(); if (retry) { headers = yield this.getHeaders(); response = yield this.httpGet(path, headers); } } if (response.statusCode >= 400) { throw new Error(response.statusMessage); } else if (response.statusCode === 200) { if (typeof response.response === 'string') { return JSON.parse(response.response); } else { return response.response; } } else { throw new Error(`Unexpected status code ${response.statusCode}: ${response.statusMessage}`); } })); } httpGet(tail, headers) { return __awaiter(this, void 0, void 0, function* () { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), 30000); try { const response = yield fetch(this.url + tail, { method: 'GET', headers: Object.assign({ 'Accept': ContentType_1.ContentTypeJson }, headers), signal: controller.signal }); clearTimeout(timeoutId); const contentType = response.headers.get('content-type') || ''; const responseBody = contentType.includes(ContentType_1.ContentTypeJson) ? yield response.json() : yield response.text(); return { statusCode: response.status, statusMessage: response.statusText, responseType: contentType, response: responseBody }; } catch (error) { clearTimeout(timeoutId); if (error.name === 'AbortError') { trace_1.Trace.warn('Network request timed out.'); return { statusCode: 408, statusMessage: "Request Timeout", responseType: '', response: null }; } else { trace_1.Trace.warn('Network request failed.'); return { statusCode: 500, statusMessage: "Network request failed", responseType: '', response: null }; } } }); } getStream(path, onResponse, onError) { const controller = new AbortController(); const signal = controller.signal; let closed = false; // Start a background task to read the stream. // This function will read one chunk and pass it to onResponse. // The function will then call itself to read the next chunk. // If an error occurs, it will call onError. (() => __awaiter(this, void 0, void 0, function* () { var _a; try { const headers = yield this.getHeaders(); if (closed) { return; } const response = yield fetch(this.url + path, { method: 'GET', headers: Object.assign({ 'Accept': 'application/x-jinaga-feed-stream' }, headers), signal }); if (!response.ok) { throw new Error(`Unexpected status code ${response.status}: ${response.statusText}`); } const reader = (_a = response.body) === null || _a === void 0 ? void 0 : _a.getReader(); const decoder = new TextDecoder(); let buffer = ''; const read = () => __awaiter(this, void 0, void 0, function* () { if (closed) { return; } try { const { done, value } = yield (reader === null || reader === void 0 ? void 0 : reader.read()); if (done) { return; } buffer += decoder.decode(value, { stream: true }); const lastNewline = buffer.lastIndexOf('\n'); if (lastNewline >= 0) { const jsonText = buffer.substring(0, lastNewline); buffer = buffer.substring(lastNewline + 1); const lines = jsonText.split(/\r?\n/); for (const line of lines) { if (line.length > 0) { try { // As data comes in, parse non-blank lines to JSON and pass to onResponse. const json = JSON.parse(line); yield onResponse(json); } catch (err) { onError(err); } } // Skip blank lines. } } // Continue reading the next chunk. read(); } catch (err) { onError(err); } }); // Start reading the first chunk. read(); } catch (err) { if (err.name === 'AbortError') { // Request was aborted, do nothing } else { onError(err); } } }))(); return () => { // If the connection is closed, exit. closed = true; controller.abort(); }; } post(path, contentType, accept, body, timeoutSeconds) { return trace_1.Trace.dependency('POST', path, () => __awaiter(this, void 0, void 0, function* () { let headers = yield this.getHeaders(); let response = yield this.httpPost(path, headers, contentType, accept, body, timeoutSeconds); if (response.statusCode === 401 || response.statusCode === 407 || response.statusCode === 419) { const reauthenticated = yield this.reauthenticate(); if (reauthenticated) { headers = yield this.getHeaders(); response = yield this.httpPost(path, headers, contentType, accept, body, timeoutSeconds); } } if (response.statusCode === 403) { throw new Error(response.statusMessage); } else if (response.statusCode >= 400) { return { result: "retry", error: response.statusMessage || "Unknown error" }; } else if (response.statusCode === 201) { return { result: "success", response: {} }; } else if (response.statusCode === 200) { if (typeof response.response === 'string') { return { result: "success", response: JSON.parse(response.response) }; } else { return { result: "success", response: response.response }; } } else { throw new Error(`Unexpected status code ${response.statusCode}: ${response.statusMessage}`); } })); } httpPost(tail, headers, contentType, accept, body, timeoutSeconds) { return __awaiter(this, void 0, void 0, function* () { const controller = new AbortController(); const timeoutId = setTimeout(() => controller.abort(), timeoutSeconds * 1000); try { if (accept) { headers = Object.assign({ 'Accept': accept }, headers); } const response = yield fetch(this.url + tail, { method: 'POST', headers: Object.assign({ 'Content-Type': contentType }, headers), body: body, signal: controller.signal }); clearTimeout(timeoutId); const responseContentType = response.headers.get('content-type') || ''; const responseBody = responseContentType.includes(ContentType_1.ContentTypeJson) ? yield response.json() : yield response.text(); return { statusCode: response.status, statusMessage: response.statusText, responseType: responseContentType, response: responseBody }; } catch (error) { clearTimeout(timeoutId); if (error.name === 'AbortError') { trace_1.Trace.warn('Network request timed out.'); return { statusCode: 408, statusMessage: "Request Timeout", responseType: '', response: null }; } else { trace_1.Trace.warn('Network request failed.'); return { statusCode: 500, statusMessage: "Network request failed", responseType: '', response: null }; } } }); } getAcceptedContentTypes(path) { return __awaiter(this, void 0, void 0, function* () { const response = yield fetch(this.url + path, { method: 'OPTIONS' }); const contentTypeHeader = response.headers.get('accept-post'); return contentTypeHeader ? contentTypeHeader.split(',').map(type => type.trim()) : []; }); } } exports.FetchConnection = FetchConnection; //# sourceMappingURL=fetch.js.map