UNPKG

12 kBJavaScriptView Raw
1"use strict";
2var __importDefault = (this && this.__importDefault) || function (mod) {
3 return (mod && mod.__esModule) ? mod : { "default": mod };
4};
5Object.defineProperty(exports, "__esModule", { value: true });
6const express_1 = __importDefault(require("express"));
7const body_parser_1 = __importDefault(require("body-parser"));
8const cookie_parser_1 = __importDefault(require("cookie-parser"));
9const lodash_1 = require("lodash");
10const APIConfig_1 = require("./lib/APIConfig");
11exports.APIConfig = APIConfig_1.APIConfig;
12const path_1 = __importDefault(require("path"));
13const APIUtils_1 = require("./lib/APIUtils");
14exports.APIUtils = APIUtils_1.APIUtils;
15require("reflect-metadata");
16const APIResponse_1 = require("./lib/APIResponse");
17exports.APIResponse = APIResponse_1.APIResponse;
18const APIError_1 = require("./lib/APIError");
19exports.APIError = APIError_1.APIError;
20const KVService_1 = require("./lib/Services/KeyValue/KVService");
21exports.APIKVService = KVService_1.KVService;
22const FileService_1 = require("./lib/Services/File/FileService");
23exports.APIFileService = FileService_1.FileService;
24const Config_1 = require("./lib/Services/Config");
25exports.EnvVarSync = Config_1.EnvVarSync;
26const APIAuthUtils_1 = require("./lib/APIAuthUtils");
27exports.APIAuthUtils = APIAuthUtils_1.APIAuthUtils;
28function _createHandlerWrapperFunction(handlerData, thisObject) {
29 return (req, res, next) => {
30 let apiResponse = new APIResponse_1.APIResponse(req, res, next);
31 // Does this require authentication?
32 if (handlerData.options.requireAuthentication) {
33 if (!req.auth || !req.auth.isAuthenticated || req.auth.isExpired) {
34 apiResponse.withError(APIError_1.APIError.create401UnauthorizedError());
35 return;
36 }
37 }
38 let handlerArgs = [];
39 let validationErrors = [];
40 // Loop through each parameter in our function and pull it from the request
41 for (let index = 0; index < handlerData.handlerParameterNames.length; index++) {
42 let paramData = handlerData.handlerParameterData[index];
43 let paramOptions = lodash_1.get(paramData, "paramOptions", {});
44 let paramName = APIUtils_1.APIUtils.coalesce(paramOptions.rawName, handlerData.handlerParameterNames[index]);
45 // Ignore request and response parameters if the function asks for it
46 if ((index === handlerData.handlerParameterNames.length - 1 || index === handlerData.handlerParameterNames.length - 2) && ["req", "request", "res", "response"].indexOf(paramName.toLowerCase()) >= 0) {
47 continue;
48 }
49 let paramSources = lodash_1.castArray(lodash_1.get(paramOptions, "sources", ["params", "query", "body", "cookie", "headers"]));
50 let paramValue;
51 if (req.auth && paramData.paramType === "APIAuthUser") {
52 paramValue = APIAuthUtils_1.APIAuthUtils.getAPIAuthUserFromAuthCredentials(req.auth);
53 }
54 else {
55 for (let paramSource of paramSources) {
56 let paramValues = lodash_1.get(req, paramSource);
57 if (lodash_1.isNil(paramValues)) {
58 continue;
59 }
60 if (paramOptions.includeFullSource ||
61 (/[\.\[\]]/g).test(paramSource) // If the source contains any of the characters ".[]" (ie a path), assume the developer meant to include the full source.
62 ) {
63 paramValue = paramValues;
64 break;
65 }
66 else {
67 if (lodash_1.has(paramValues, paramName)) {
68 paramValue = paramValues[paramName];
69 if (paramSource === "query") {
70 paramValue = decodeURIComponent(paramValue);
71 }
72 break;
73 }
74 }
75 }
76 }
77 let argValue = APIUtils_1.APIUtils.coalesce(paramValue, paramOptions.defaultValue);
78 if (paramOptions.processor) {
79 try {
80 argValue = paramOptions.processor(argValue, req);
81 }
82 catch (error) {
83 validationErrors.push({
84 parameter: paramName,
85 message: error.message || error.toString()
86 });
87 continue;
88 }
89 }
90 if (lodash_1.isNil(argValue)) {
91 // Is this parameter required?
92 if (!lodash_1.get(paramOptions, "optional", false)) {
93 validationErrors.push({
94 parameter: paramName,
95 message: "missing"
96 });
97 }
98 handlerArgs.push(undefined);
99 continue;
100 }
101 argValue = APIUtils_1.APIUtils.convertToType(argValue, paramData.paramRawType);
102 if (lodash_1.isNil(argValue) || lodash_1.isNaN(argValue)) {
103 validationErrors.push({
104 parameter: paramName,
105 message: "invalid"
106 });
107 continue;
108 }
109 handlerArgs.push(argValue);
110 }
111 if (validationErrors.length > 0) {
112 apiResponse.withError(APIError_1.APIError.createValidationError(validationErrors));
113 return;
114 }
115 apiResponse.processHandlerFunction(thisObject, handlerData.handlerFunction, handlerArgs, handlerData.options.disableFriendlyResponse, handlerData.options.successResponse);
116 };
117}
118function _loadAPI(apiRouter, apiDefinition) {
119 let apiModule;
120 try {
121 apiModule = require(path_1.default.resolve(process.cwd(), apiDefinition.require));
122 }
123 catch (e) {
124 console.error(e);
125 return null;
126 }
127 if (lodash_1.isNil(apiModule)) {
128 return null;
129 }
130 let moduleName = APIUtils_1.APIUtils.coalesce(apiDefinition.moduleName, path_1.default.basename(apiDefinition.require));
131 let apiClass = APIUtils_1.APIUtils.coalesce(apiModule[moduleName], apiModule.default, apiModule);
132 let apiInstance;
133 lodash_1.each(lodash_1.get(apiClass, "__handlerData", {}), (handlerData, name) => {
134 // If this is an instance function, we need to create an instance of the class
135 if (handlerData.isInstance && lodash_1.isNil(apiInstance)) {
136 apiInstance = new apiClass();
137 }
138 let options = handlerData.options;
139 let argsArray = [options.path];
140 if (options.middleware) {
141 argsArray = argsArray.concat(lodash_1.castArray(options.middleware));
142 }
143 let handlerWrapper = _createHandlerWrapperFunction(handlerData, handlerData.isInstance ? apiInstance : apiClass);
144 argsArray.push(handlerWrapper);
145 apiRouter[options.method.toLowerCase()].apply(apiRouter, argsArray);
146 });
147}
148class APILove {
149 static start(options) {
150 if (options.loadStandardMiddleware !== false) {
151 this.app.use(cookie_parser_1.default());
152 this.app.use(body_parser_1.default.json({ limit: "50mb" }));
153 this.app.use(body_parser_1.default.urlencoded({ limit: "50mb", extended: false, parameterLimit: 50000 }));
154 this.app.use(body_parser_1.default.text({ limit: "50mb" }));
155 this.app.use((req, res, next) => {
156 req.auth = APIAuthUtils_1.APIAuthUtils.getAuthCredentialsFromRequest(req, true);
157 next();
158 });
159 }
160 for (let mw of lodash_1.get(options, "middleware", [])) {
161 this.app.use(mw);
162 }
163 // Here we load our APIs, but we only load them when requested
164 for (let api of lodash_1.get(options, "apis", [])) {
165 if (lodash_1.isNil(api.apiPath)) {
166 api.apiPath = "";
167 }
168 if (APIConfig_1.APIConfig.LAZY_LOAD_APIS) {
169 let apiRouter;
170 this.app.use(api.apiPath, (req, res, next) => {
171 // Lazy load our API
172 if (!apiRouter) {
173 apiRouter = express_1.default.Router();
174 _loadAPI(apiRouter, api);
175 }
176 apiRouter(req, res, next);
177 });
178 }
179 else {
180 let apiRouter = express_1.default.Router();
181 _loadAPI(apiRouter, api);
182 this.app.use(api.apiPath, apiRouter);
183 }
184 }
185 if (!lodash_1.isNil(options.defaultRouteHandler)) {
186 this.app.use(options.defaultRouteHandler);
187 }
188 // Setup our default error handler
189 if (!lodash_1.isNil(options.defaultErrorHandler)) {
190 this.app.use(options.defaultErrorHandler);
191 }
192 else {
193 this.app.use((error, req, res, next) => {
194 if (error instanceof APIError_1.APIError) {
195 let apiError = error;
196 res.status(apiError.statusCode).send(APIConfig_1.APIConfig.OUTPUT_HAPI_RESULTS ? apiError.hapiOut() : apiError.out());
197 }
198 else {
199 let apiResponse = new APIResponse_1.APIResponse(res, res);
200 apiResponse.withError(error);
201 }
202 });
203 }
204 if (APIConfig_1.APIConfig.RUN_AS_SERVER) {
205 this.app.listen(APIConfig_1.APIConfig.WEB_PORT, () => console.log(`API listening on port ${APIConfig_1.APIConfig.WEB_PORT}`));
206 return this.app;
207 }
208 else {
209 let serverless = require("serverless-http");
210 return serverless(this.app, { callbackWaitsForEmptyEventLoop: !!options.callbackWaitsForEmptyEventLoop });
211 }
212 }
213}
214exports.APILove = APILove;
215APILove.app = express_1.default();
216function APIParameter(options) {
217 return function (target, key, parameterIndex) {
218 let isInstance = lodash_1.isNil(target.prototype);
219 let theClass = isInstance ? target.constructor : target.prototype.constructor;
220 let handlerData = lodash_1.get(theClass, `__handlerData.${key}`, {});
221 lodash_1.set(handlerData, `handlerParameterData.${parameterIndex}.paramOptions`, options);
222 lodash_1.set(theClass, `__handlerData.${key}`, handlerData);
223 };
224}
225exports.APIParameter = APIParameter;
226function APIEndpoint(options) {
227 return function (target, key, descriptor) {
228 let isInstance = lodash_1.isNil(target.prototype);
229 let theClass = isInstance ? target.constructor : target.prototype.constructor;
230 let handlerData = lodash_1.get(theClass, `__handlerData.${key}`, {});
231 options = lodash_1.defaultsDeep({}, options, {
232 method: "get",
233 path: "/"
234 });
235 let parameterMetadata = Reflect.getMetadata("design:paramtypes", target, key);
236 let parameterNames = APIUtils_1.APIUtils.getFunctionParamNames(descriptor.value);
237 handlerData.isInstance = isInstance;
238 handlerData.handlerFunction = descriptor.value;
239 handlerData.options = options;
240 handlerData.handlerParameterNames = parameterNames;
241 for (let parameterIndex = 0; parameterIndex < parameterNames.length; parameterIndex++) {
242 lodash_1.set(handlerData, `handlerParameterData.${parameterIndex}.paramRawType`, APIUtils_1.APIUtils.getRawTypeName(parameterMetadata[parameterIndex].prototype));
243 lodash_1.set(handlerData, `handlerParameterData.${parameterIndex}.paramType`, parameterMetadata[parameterIndex].name);
244 lodash_1.set(handlerData, `handlerParameterData.${parameterIndex}.paramName`, parameterNames[parameterIndex]);
245 }
246 lodash_1.set(theClass, `__handlerData.${key}`, handlerData);
247 };
248}
249exports.APIEndpoint = APIEndpoint;
250//# sourceMappingURL=APILove.js.map
\No newline at end of file