UNPKG

api-first-spec

Version:
211 lines (193 loc) 6.28 kB
import { IParam, Param, TopLevelParam } from "./param"; import { ParamPool } from "./paramPool"; import request from "request"; export enum Method { GET = "GET", POST = "POST", PUT = "PUT", DELETE = "DELETE" } export enum ContentType { CSV = "text/csv", TEXT = "text/plain", JSON = "application/json", URLENCODED = "application/x-www-form-urlencoded", MULTIPART = "multipart/form-data" } export class Request { private _contentType: string; private _params?: IParam = null; private _rules?: any = null; private _headers: any = null; constructor(config: RequestConfig) { this._contentType = config.contentType ? config.contentType : ContentType.URLENCODED; this._rules = config.rules; this._headers = config.headers; if (config.params) { this._params = new TopLevelParam("request", config.params, config.rules); } } public get contentType() { return this._contentType; } public get params() { return this._params; } public get rules() { return this._rules; } public get headers() { return this._headers; } } export class Response { private _contentType: string; private _data?: IParam = null; private _rules?: any = null; constructor(config: ResponseConfig) { this._contentType = config.contentType ? config.contentType : ContentType.JSON; this._rules = config.rules; if (config.data) { this._data = new TopLevelParam("response", config.data, config.rules); } } public get contentType() { return this._contentType; } public get data() { return this._data; } public get rules() { return this._rules; } } export class API { private _name: string; private _endpoint: string; private _method: string; private _request: Request; private _response: Response; private _urlParams: Array<string>; private _verbose: boolean = false; constructor(config: ApiConfig) { this.init(config); } public get name() { return this._name; } public get endpoint() { return this._endpoint; } public get method() { return this._method; } public get request() { return this._request; } public get response() { return this._response; } public get urlParams() { return this._urlParams; } public verbose(v?: boolean) { if (typeof v === "boolean") { this._verbose = v; return this; } else { return this._verbose; } } public isSuccess(data: any, res: request.Response) { return res.statusCode >= 200 && res.statusCode < 300; } public isBadRequest(data: any, res: request.Response) { return res.statusCode === 400; } public isNotFound(data: any, res: request.Response) { return res.statusCode === 404; } public isUnauthorized(data: any, res: request.Response) { return res.statusCode === 401; } public isForbidden(data: any, res: request.Response) { return res.statusCode === 403; } public isClientError(data: any, res: request.Response) { return res.statusCode >= 400 && res.statusCode < 500; } public validateResponse(data: any, res: request.Response, reqData: any) { if (res.statusCode === 204) { return; } let resContentType = res.headers["content-type"]; if (resContentType) { resContentType = resContentType.split(";")[0].trim().toLowerCase(); } if (this.response.contentType !== resContentType) { throw new Error("Content-Type is not match: " + resContentType); } if (resContentType === ContentType.JSON) { this.response.data.validate(data, data, reqData); } } public test() { function testContentType(value: string) { if (!Object.keys(ContentType).find((key: string) => (ContentType as any)[key] === value)) { throw new Error("Invalid ContentType: " + value); } } function testEndpoint(value: string) { if (!value) { throw new Error("endpoint is not defined"); } if (value.charAt(0) !== "/") { throw new Error("endpoint must starts with '/'"); } } function testMethod(value: string) { if (!value) { throw new Error("method is not defined"); } if (!Object.keys(Method).find((key: string) => (Method as any)[key] === value)) { throw new Error("Invalid method: " + value); } } function testRulesReference(params: IParam, rules: any) { const pool = new ParamPool(params); const ret: Array<string> = []; Object.keys(rules).forEach(key => { if (!pool.getParams(key)) { ret.push(key); } }); if (ret.length > 0) { throw new Error("Undefined parameters : " + JSON.stringify(ret)); } } function testRequest(req: Request) { testContentType(req.contentType); if (req.params) { req.params.test(); } if (req.params && req.rules) { testRulesReference(req.params, req.rules); } } function testResponse(response: Response) { if (!response) { throw new Error("response is not defined."); } if (!response.data) { throw new Error("data is not defined."); } response.data.test(); if (response.data && response.rules) { testRulesReference(response.data, response.rules); } } testEndpoint(this.endpoint); testMethod(this.method); testRequest(this.request); testResponse(this.response); } public testCoverage() { if (this.request && this.request.params && this.request.params.coverage() !== 1) { throw new Error(`Request parameter [${this.request.params.uncoveredParamNames()}] are not covered.`); } } public clearCoverage() { if (this.request && this.request.params) { this.request.params.clearCoverage(); } } private init(config: ApiConfig) { this._name = config.name || config.endpoint; this._endpoint = config.endpoint; this._method = config.method; this._request = new Request(config.request || {}); if (config.response) { this._response = new Response(config.response); } if (config.endpoint) { const urlParams = config.endpoint.match(/\[[^\]]*\]/g); if (urlParams && urlParams.length > 0) { this._urlParams = urlParams.map(v => v.substring(1, v.length - 1)); } } } }