jinaga
Version:
Data management for web and mobile applications.
272 lines • 12 kB
JavaScript
;
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