allserver
Version:
Multi-protocol simple RPC server and [optional] client. Boilerplate-less. Opinionated. Minimalistic. DX-first.
107 lines (95 loc) • 4.22 kB
JavaScript
module.exports = require("./Transport").compose({
name: "LambdaTransport",
methods: {
async deserializeEvent(ctx) {
if (ctx.lambda.isHttp) {
const { body, query } = ctx.lambda.http;
try {
// If there is no body we will use request query (aka search params)
if (!body) {
ctx.arg = query || {};
} else {
if ((typeof body === "string" && body[0] === "{") || Buffer.isBuffer(body)) {
ctx.arg = JSON.parse(body);
} else {
return false;
}
}
return true;
} catch (err) {
return false;
}
} else {
// TODO: change to this during the next major release:
// ctx.arg = ctx.lambda.invoke || {};
ctx.arg = ctx.lambda.invoke.callArg || ctx.lambda.invoke || {};
return true;
}
},
async _handleRequest(ctx) {
if (await this.deserializeEvent(ctx)) {
await ctx.allserver.handleCall(ctx);
} else {
// HTTP protocol request was malformed (not expected structure).
// We are not going to process it.
ctx.result = { success: false, code: "ALLSERVER_BAD_REQUEST", message: "Can't parse JSON" };
ctx.lambda.statusCode = 400;
this.reply(ctx);
}
},
startServer(defaultCtx) {
return async (event, context) => {
return new Promise((resolve) => {
const lambda = { event, context, resolve };
const path = (event && (event.path || event.requestContext?.http?.path)) || undefined;
lambda.isHttp = Boolean(path);
if (lambda.isHttp) {
lambda.http = {
path,
query: event?.queryStringParameters,
body: event?.body,
headers: event?.headers,
};
} else {
// TODO: change to this during the next major release:
// lambda.invoke = event || {};
lambda.invoke = event?.callArg
? { callContext: event?.callContext, callArg: event?.callArg }
: event || {};
}
this._handleRequest({ ...defaultCtx, lambda });
});
};
},
getProcedureName(ctx) {
const { isHttp, http, invoke } = ctx.lambda;
// TODO: change to this during the next major release:
// return (isHttp ? http.path.substr(1) : invoke._?.procedureName) || ""; // if no procedureName then it's an introspection
return isHttp ? http.path.substr(1) : invoke.callContext?.procedureName || invoke._?.procedureName || ""; // if no procedureName then it's an introspection
},
isIntrospection(ctx) {
return this.getProcedureName(ctx) === "";
},
prepareProcedureErrorReply(ctx) {
// Generic exception.
ctx.lambda.statusCode = 500;
// nodejs assert() exception. In HTTP world this likely means 400 "Bad Request".
if (ctx.error.code === "ERR_ASSERTION") ctx.lambda.statusCode = 400;
},
prepareNotFoundReply(ctx) {
ctx.lambda.statusCode = 404;
},
reply(ctx) {
if (ctx.lambda.isHttp) {
if (!ctx.lambda.statusCode) ctx.lambda.statusCode = 200;
ctx.lambda.resolve({
statusCode: ctx.lambda.statusCode,
headers: { "content-type": "application/json" },
body: JSON.stringify(ctx.result),
});
} else {
ctx.lambda.resolve(ctx.result);
}
},
},
});