@breautek/storm
Version:
Object-Oriented REST API framework
154 lines (129 loc) • 5.25 kB
text/typescript
/*
Copyright 2017-2021 Norman Breau
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import {StatusCode} from './StatusCode';
import {ResponseData} from './ResponseData';
import {StormError, IErrorResponse} from './StormError';
import * as express from 'express';
import { InternalError } from './InternalError';
import { getInstance } from './instance';
import { Stream } from 'stream';
const TAG: string = 'Response';
export type SendableData = ResponseData | Error | IErrorResponse | Buffer | any;
export interface IHeaderKeyValuePair {
[key: string]: string;
}
export class Response<TResponse = SendableData, TErrorResponse = Error | IErrorResponse | string> {
private $response: express.Response;
private $created: Date;
private $requestURL: string;
public constructor(response: express.Response, requestURL: string) {
this.$response = response;
this.$created = new Date();
this.$requestURL = requestURL;
}
public setStatus(status: StatusCode): Response<TResponse, TErrorResponse> {
this.$response.status(status);
return this;
}
public getStatus(): StatusCode {
return this.$response.statusCode;
}
public redirect(url: string): void {
this.$response.redirect(url);
}
private $send(data?: TResponse | TErrorResponse | StormError | IErrorResponse | Buffer, statusOverride?: StatusCode): void {
if (data === null || data === undefined) {
this.setStatus(statusOverride || StatusCode.OK_NO_CONTENT);
this.$response.send()
}
else if (typeof data === 'number') {
// Numbers needs to be toString as
// express will interpet them as a status code
this.$response.send(data.toString());
}
else if (data instanceof Buffer || [ 'string', 'boolean' ].indexOf(typeof data) > -1) {
this.$response.send(data);
}
else if (data instanceof Stream.Readable) {
this.pipe(data);
}
else if (data instanceof ResponseData) {
if (data.getRedirect() !== null) {
this.redirect(data.getRedirect());
return;
}
let headers: Map<string, string> = data.getHeaders();
for (let header of headers) {
this.setHeader(header[0], header[1]);
}
this.setStatus(data.getStatus());
this.$send(data.getData(), data.getStatus());
}
else if (data instanceof StormError) {
this.setStatus(statusOverride || data.getHTTPCode()).send(data.getErrorResponse());
}
else {
this.$response.send(data);
}
}
public send(data?: TResponse | TErrorResponse | StormError | IErrorResponse | Buffer): void {
this.$send(data);
getInstance().getLogger().info(TAG, `API ${this.$requestURL} (${this.getStatus()}) responded in ${new Date().getTime() - this.$created.getTime()}ms`);
}
public pipe(stream: NodeJS.ReadableStream): void {
stream.on('end', () => {
stream.unpipe(this.$response);
});
stream.pipe(this.$response);
}
public success(data?: TResponse): void {
if (data === undefined) {
this.setStatus(StatusCode.OK_NO_CONTENT);
}
else {
this.setStatus(StatusCode.OK);
}
this.send(data);
}
public setHeader(key: string, value: string): void {
this.$response.set(key, value);
}
public setHeaders(keyValuePair: IHeaderKeyValuePair): void {
this.$response.set(keyValuePair);
}
public isHeadersSent(): boolean {
return this.$response.headersSent;
}
public error(error?: TErrorResponse | ResponseData<TErrorResponse>): void {
if (error) {
if (error instanceof StormError) {
this.send(error);
}
else if (error instanceof ResponseData) {
let headers: Map<string, string> = error.getHeaders();
for (let header of headers) {
this.setHeader(header[0], header[1]);
}
// If it was not ResponseData<TResponse> then
// the method signature should have caught it
this.send((error as unknown as TErrorResponse));
}
else {
this.send(new InternalError(error));
}
}
else {
this.send(new InternalError(error));
}
}
}