@faceteer/cdk
Version:
CDK 2.0 constructs and helpers that make composing a Lambda powered service easier.
190 lines (189 loc) • 6.65 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.ApiHandler = ApiHandler;
const qs = __importStar(require("qs"));
const response_1 = require("../response");
const handler_1 = require("./handler");
/**
* Creates a handler that will be attached to the service api
* @param options
* @param handler
* @returns
*/
function ApiHandler(options, handler) {
const { authorizer, validators, ...definition } = options;
const wrappedHandler = async (event, context) => {
try {
const validatedParameters = checkPathParameters(event.pathParameters, definition.pathParameters);
if (typeof validatedParameters === 'string') {
return (0, response_1.FailedResponse)(`The parameter "${validatedParameters}" was not found in the route "${options.route}". Please check your route configuration`);
}
let queryBody = {};
if (event.rawQueryString) {
try {
queryBody = qs.parse(event.rawQueryString);
}
catch (error) {
return (0, response_1.FailedResponse)(error);
}
}
let validateBodyResult = {
success: true,
data: event.body,
};
let validateQueryResult = {
success: true,
data: queryBody,
};
if (validators) {
validateBodyResult = validateInput(validators.body, event.body);
validateQueryResult = validateInput(validators.query, queryBody);
}
if (!validateBodyResult.success) {
return (0, response_1.FailedResponse)(validateBodyResult.error, { statusCode: 400 });
}
if (!validateQueryResult.success) {
return (0, response_1.FailedResponse)(validateQueryResult.error, { statusCode: 400 });
}
const parsedEvent = {
...event,
input: {
body: validateBodyResult.data,
query: validateQueryResult.data,
path: validatedParameters,
},
};
let auth = undefined;
if (authorizer) {
try {
const authResult = authorizer(parsedEvent, options);
if (authResult) {
auth = authResult;
}
else {
return (0, response_1.FailedResponse)('Unauthorized', { statusCode: 403 });
}
}
catch {
return (0, response_1.FailedResponse)('Unauthorized', { statusCode: 403 });
}
}
const validatedEvent = {
...event,
input: {
...parsedEvent.input,
auth: auth,
},
};
const result = handler(validatedEvent, context, () => { });
if (result) {
const finalResult = await result;
if ('bodyString' in finalResult) {
return {
body: finalResult.bodyString,
cookies: finalResult.cookies,
headers: finalResult.headers,
isBase64Encoded: finalResult.isBase64Encoded,
statusCode: finalResult.statusCode,
};
}
return finalResult;
}
throw new Error('The API handler return an invalid response type');
}
catch (error) {
return (0, response_1.FailedResponse)(error);
}
};
return Object.assign(wrappedHandler, {
definition: {
...definition,
validators,
},
type: handler_1.HandlerTypes.API,
});
}
/**
* Validate the input for our query or our body
* @param validator
* @param input
* @returns
*/
function validateInput(validator, input) {
if (!validator) {
return { success: true, data: input };
}
/**
* Assume any string inputs are JSON
*/
if (typeof input === 'string') {
input = JSON.parse(input);
}
try {
const validatedInput = validator(input);
if (validatedInput) {
return { success: true, data: validatedInput };
}
throw new Error('Input validation failed');
}
catch (error) {
return {
success: false,
error: error instanceof Error ? { message: error.message } : error,
};
}
}
/**
* Check to make sure that path parameters that have
* been defined exist on the function
* @param pathParameters
* @param definedParameters
* @returns
*/
function checkPathParameters(pathParameters = {}, definedParameters) {
if (!definedParameters) {
return {};
}
const validatedParameters = {};
for (const param of definedParameters) {
const value = pathParameters[param];
if (!value) {
return param;
}
validatedParameters[param] = value;
}
return validatedParameters;
}