faker-api
Version:
A fully customizible rest api faking package that allows you to mock , clone and fake Rest API with fake yet realistic data
335 lines • 14.6 kB
JavaScript
"use strict";
/* tslint:disable:variable-name*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.FakerServer = void 0;
var express_1 = require("express");
var express_2 = __importDefault(require("express"));
var models_1 = require("../models");
var logger_1 = require("./logger");
var path_1 = require("./path");
var transformers_1 = require("../transformers");
var router_1 = require("../router");
/**
* This is the class that is been used to instantiate the api faker server instance
*
*/
var FakerServer = /** @class */ (function () {
function FakerServer(serverPath, expressInstance) {
if (serverPath === void 0) { serverPath = ""; }
var _this = this;
/**
* @type string specific the route the server would be running on
*/
this.serverPath = "";
this.appPassed = false;
// Single method handler , handlers can be a function or a model
this.methodRequestHanders = new Map();
this.routerMaps = new Map();
this.__internlRouterRoot = "/___internalRouter";
var path = serverPath === null || serverPath === void 0 ? void 0 : serverPath.trim();
if (expressInstance) {
this.appPassed = true;
this.expressApp = expressInstance;
}
else {
this.expressApp = (0, express_2.default)();
}
this.__internalRouter = new router_1.Router();
this.route(this.__internlRouterRoot, this.__internalRouter);
// Request the handler for all request
this.expressRouter = (0, express_1.Router)();
this.expressApp.use(this.serverPath, this.expressRouter);
if (path === "" || !path) {
this.expressRouter.all("*", function (req, res) {
_this.onNewRequest(req, res);
});
}
else {
var pattern = RegExp("^\/+");
var p = this.serverPath.replace(pattern, "");
p = "/" + p;
this.serverPath = p;
this.expressRouter.all(p, function (req, res) {
_this.onNewRequest(req, res);
});
}
}
/**
* Returns an instance of a FakerServer with the given express application instance
*
* @param expressInstance An express application instance
* @param path [string="/api"] The path the Faker Server will be running on, on the express application
* @returns An instance of FakerServer
*
*/
FakerServer.from = function (expressInstance, path) {
if (path === void 0) { path = "/api"; }
return new FakerServer(path, expressInstance);
};
/**
* Start the faker server.
* @remark This method should be called if the faker server instance was created / instantiated without passing an express application instance.
* @param port - The port the faker server internally created express application instance will run on
*/
FakerServer.prototype.run = function (port) {
var _this = this;
if (port === void 0) { port = 8800; }
if (this.appPassed) {
(0, logger_1.logMessage)("Message an instance of Express app was pass to the constructor, no need to call run, the Faker server will start the moment the Express app starts", "warning");
return;
}
this.expressApp.listen(port, function () {
(0, logger_1.logMessage)("Faker Server running on \n \u001B[42m http://localhost:".concat(port).concat(_this.serverPath, "\u001B[0m "), "debug");
});
};
/**
* Adds a new request method handle to the faker server
* @param path - The url the handler should handle
* @param handler - The handler it self
* @param method - The request method type the handle should accept
*/
FakerServer.prototype.__addMethodRequestHandler = function (path, handler, method) {
if (!this.methodRequestHanders.has(path)) {
this.methodRequestHanders.set(path, {});
}
var _handlers = this.methodRequestHanders.get(path);
if (_handlers)
_handlers[method] = handler;
};
/**
* Adds a new GET request handler to the faker server instance
* @param path - The url the handler should handle
* @param handler - The handler it self
*
*/
FakerServer.prototype.get = function (path, handler) {
this.__addMethodRequestHandler(path, handler, "GET");
return this;
};
/**
* Adds a new POST request handler to the faker server instance
* @param path - The url the handler should handle
* @param handler - The handler it self
*
*/
FakerServer.prototype.post = function (path, handler) {
this.__addMethodRequestHandler(path, handler, "POST");
return this;
};
/**
* Adds a new DELETE request handler to the faker server instance
* @param path - The url the handler should handle
* @param handler - The handler it self
*
*/
FakerServer.prototype.delete = function (path, handler) {
this.__addMethodRequestHandler(path, handler, "DELETE");
return this;
};
/**
* Adds a new PUT request handler to the faker server instance
* @param path - The url the handler should handle
* @param handler - The handler it self
*
*/
FakerServer.prototype.put = function (path, handler) {
this.__addMethodRequestHandler(path, handler, "PUT");
return this;
};
/**
* Adds a new PATCH request handler to the faker server instance
* @param path - The url the handler should handle
* @param handler - The handler it self
*
*/
FakerServer.prototype.patch = function (path, handler) {
this.__addMethodRequestHandler(path, handler, "PATCH");
return this;
};
/**
* Register a new Router to the faker server instance
* @param path - The path the router should handle request to (avoid using paths that ends with a /)
* @param router - The router instance
*
* @remark This method can throw error if the `path` ends with /
*/
FakerServer.prototype.route = function (path, routerOrViewSet) {
var _a, _b;
if (!(routerOrViewSet instanceof router_1.Router)) {
try {
if (path.trimStart().startsWith("/") || path.trimEnd().endsWith("/")) {
throw new Error("Path starting or ending with / is not allowed when registering a viewset");
}
var viewset = routerOrViewSet;
this.__internalRouter.register(path, viewset);
return;
}
catch (error) {
var e = error;
(0, logger_1.logMessage)("".concat(e.message, " \n ").concat((_a = e.stack) === null || _a === void 0 ? void 0 : _a.split("\n").splice(0, 3).join("\n")));
}
}
var router = routerOrViewSet;
try {
if (path.match(/\/$/) && path !== this.__internlRouterRoot) {
throw new Error("Avoid using router path that ends with \u001B[41m\u001B[37m / \u001B[0m ");
}
if (this.routerMaps.has(path)) {
throw new Error("Another router with thesame path ".concat(path, " is been override @ \n "));
}
}
catch (error) {
var _e = error;
var requiredStag = (_b = _e.stack) === null || _b === void 0 ? void 0 : _b.split("\n").slice(1, 3).join("\n");
(0, logger_1.logMessage)("".concat(_e.message, " \n ").concat(requiredStag, " \n"), "warning");
}
router._root = path;
this.routerMaps.set(path, router);
};
/**
* Handles request that matches a specific static path that has a method handler registered to it
*
* @param path - The url the handler should handle
* @param handler - The handler it self
* @param method - The request method type the handle should accept
*/
FakerServer.prototype._handleMethodHandler = function (request, response, path, params) {
// Gets all method handlers associated to the path
var handlers = this.methodRequestHanders.get(path);
var method = request.method.toUpperCase();
// The method is an Option request,Add the handlers methods to the response Accept header
if (!(method in handlers)) {
if (method === "OPTIONS") {
response.setHeader("Accept", Object.keys(handlers).join(","));
}
return false;
}
var handler = handlers[method];
// Could not the a handler for the request method type
if (!handler)
return false;
// The handler was is a function
if (typeof handler === "function") {
handler(request, response, params);
return true;
}
else if (handler instanceof models_1.AbstractModel) {
// Handler is a model send the model's generated data
response.send(handler.generate(request, params));
return true;
}
else if (handler instanceof transformers_1.Transformer) {
// The handler is a transformer call the transform function and send the data it returns
response.send(handler.transform(request, params));
return true;
}
else {
response.send(handler);
}
return false;
};
/**
* Called internally to check if there exist a router that can handle a request, after confirming that there is no method handler found to handle the request
* @param request - The request object
* @param response - The response object
* path - The request path without the server prefix path
* @returns - true if the request handled by a router or false if there was no router to handle the request
*/
FakerServer.prototype._handleRouterRequest = function (request, response, path) {
// Get all the routers path prefix
var routersPath = Array.from(this.routerMaps.keys());
// loop through the routerPath to see those that match the start of the path
for (var _i = 0, routersPath_1 = routersPath; _i < routersPath_1.length; _i++) {
var routerPath = routersPath_1[_i];
if (path.startsWith(routerPath) && routerPath !== this.__internlRouterRoot) {
// A match was found
// strip the prefix of the router from the path
var _m = path.replace(routerPath, "");
// This insures that the right router is select since router prefix are not allowed to end with /
/**
* This plevent for example
* router prefix is router/game
* then path router/game/more
* should match the router but
* path = router/game//more will not macth
*/
if (!_m.startsWith("/"))
continue;
var router = this.routerMaps.get(routerPath);
// pass the request to the found router and see if it can handle the request
var result = router.on(request, response, _m);
if (result)
return true;
}
else if (routerPath === this.__internlRouterRoot) {
// For internal router , This is to handle ViewSet that where passed into the route feature, there is no need to strip the router path
var router = this.routerMaps.get(routerPath);
// pass the request to the found router and see if it can handle the request
var result = router.on(request, response, path);
if (result)
return true;
}
continue;
}
return false;
};
/**
* This method is called internally everytime a new request is directed to the faker server
* @param request - The new request object
* @param response - The request response object
*/
FakerServer.prototype.onNewRequest = function (request, response) {
var path = request.path.replace(this.serverPath, "").trim();
var tempPattern = RegExp("\/$");
if (!path.match(tempPattern)) {
path = path + "/";
}
if (request.method === "OPTIONS")
response.setHeader("Accept", "");
(0, logger_1.logRequest)(request);
var handled = false;
// First loop through Method handlers first
// loop through each method handler if path matches current request path
var handlersPath = Array.from(this.methodRequestHanders.keys());
for (var _i = 0, handlersPath_1 = handlersPath; _i < handlersPath_1.length; _i++) {
var handlerPath = handlersPath_1[_i];
if (handled)
break;
// Run the pathParams finder if the paths maches it will return an object of paramsType
var pathMatchResult = path_1.PathUtil.isMatch(handlerPath, path, true);
if (pathMatchResult) {
handled = this._handleMethodHandler(request, response, handlerPath, pathMatchResult);
}
}
// Pass it to the routers to handle if no method found to handle the request
if (handled === false) {
handled = this._handleRouterRequest(request, response, path.replace(/^\/\//, "/"));
}
// no methods or routers available to handle such request
if (handled === false) {
if (request.method !== "OPTIONS")
(0, logger_1.logMessage)("Unhandled request made to `".concat(request.method.toUpperCase(), " ").concat(path, "`"), "error");
try {
if (request.method.toUpperCase() === "OPTIONS") {
response.status(200);
response.send();
}
else {
response.status(404);
response.send({
detail: "No handler for this route @FakerAPI",
});
}
}
catch (error) {
// Todo: Handle this error
}
}
};
return FakerServer;
}());
exports.FakerServer = FakerServer;
//# sourceMappingURL=index.js.map