import HTTP from 'http';
import Node from 'type/node';
import TypeNode from 'type/type-node';
import SystemId from 'system/enum/id';
import HttpError from 'http/error';
import HeaderMap from 'http/type/header-map';
import Repository from 'repository';
import HttpHeader from 'http/enum/header';
import HttpMethod from 'http/enum/method';
import BodyParser from 'server/body-parser';
import StatusCode from 'http/enum/status-code';
import AccountNode from 'type/node/account';
import ContentType from 'http/enum/content-type';
import ServerError from 'http/error/server-error';
import InstanceNode from 'type/instance-node';
import UrlParameters from 'http/type/url-parameters';
import JsonBodyParser from 'server/body-parser/json';
import BadRequestError from 'http/error/bad-request';
import parseQuerystring from 'http/utility/parse-querystring';
import FetchNodeOperation from 'operation/fetch-node';
import UrlEncodedBodyParser from 'server/body-parser/url-encoded';
import getSuccessfulStatusCode from 'http/utility/get-successful-status-code';
import LoadNodeFromUrlOperation from 'operation/load-node-from-url';
import LoadAccountForRequestOperation from 'operation/load-account-for-request';
// eslint-disable-next-line @typescript-eslint/ban-types
type AllowedOutputs = string | Buffer | object;
abstract class Endpoint {
private request: HTTP.IncomingMessage;
private response: HTTP.ServerResponse;
private url_parameters: UrlParameters;
private query_parameters: Record | undefined;
private repository: Repository;
private status_code: StatusCode;
private response_headers: HeaderMap;
private request_body: Input | undefined;
private account: AccountNode | undefined;
public constructor(
request: HTTP.IncomingMessage,
response: HTTP.ServerResponse,
url_parameters: UrlParameters,
repository: Repository
) {
this.request = request;
this.response = response;
this.url_parameters = url_parameters;
this.repository = repository;
this.status_code = getSuccessfulStatusCode(request);
this.response_headers = {
[HttpHeader.CONTENT_TYPE]: this.getResponseContentType()
};
}
public serve(): void {
this.serveInternal()
.then(this.handleResult.bind(this))
.catch(this.handleError.bind(this));
}
protected getRequest(): HTTP.IncomingMessage {
return this.request;
}
protected getResponse(): HTTP.ServerResponse {
return this.response;
}
protected setHeaderValue(header: HttpHeader, value: string): void {
const headers = this.getResponseHeaders();
headers[header] = value;
}
protected getResponseHeaders(): HeaderMap {
return this.response_headers;
}
protected getUrlParameter(parameter: string): string {
const parameters = this.getUrlParameters();
const value = parameters[parameter];
if (value === undefined) {
throw new BadRequestError();
}
return value;
}
protected getQueryParameter(parameter: string): any {
const query_parameters = this.getQueryParameters();
return query_parameters[parameter];
}
protected setStatusCode(status_code: StatusCode): void {
this.status_code = status_code;
}
protected getRepository(): Repository {
return this.repository;
}
protected getRequestBody(): Input {
if (this.hasUnparsableMethod()) {
return {} as Input;
}
if (this.request_body === undefined) {
throw new Error('Tried to read request body, but it was not set');
}
return this.request_body;
}
protected getAccount(): AccountNode {
if (this.account === undefined) {
throw new Error('Tried to read account, but it was not set');
}
return this.account;
}
protected redirectToUrl(url: string): void {
this.setStatusCode(StatusCode.REDIRECT);
this.setHeaderValue(HttpHeader.LOCATION, url);
this.sendString('');
}
protected async fetchType(type_id: string): Promise {
const node = await this.fetchNode(SystemId.GENERIC_TYPE, type_id);
return node as TypeNode;
}
protected async fetchInstance(
type_id: string,
id: string
): Promise {
const node = await this.fetchNode(type_id, id);
return node as InstanceNode;
}
protected async loadInstanceFromUrl(url: string): Promise {
const node = await this.loadNodeFromUrl(url);
return node as InstanceNode;
}
protected async loadTypeFromUrl(url: string): Promise {
const node = await this.loadNodeFromUrl(url);
return node as TypeNode;
}
private fetchNode(type_id: string, id: string): Promise {
const repository = this.getRepository();
const account = this.getAccount();
const input = {
type_id,
id,
repository,
account
};
const operation = new FetchNodeOperation(input);
return operation.perform();
}
private loadNodeFromUrl(url: string): Promise {
const repository = this.getRepository();
const account = this.getAccount();
const input = {
url,
repository,
account
};
const operation = new LoadNodeFromUrlOperation(input);
return operation.perform();
}
private async serveInternal(): Promise