@mcma/api
Advanced tools
Comparing version 0.16.1 to 0.16.2
export * from "./http-statuses"; | ||
export * from "./mcma-api-controller"; | ||
export * from "./mcma-api-error"; | ||
export * from "./mcma-api-middleware"; | ||
export * from "./mcma-api-request-config"; | ||
@@ -5,0 +6,0 @@ export * from "./mcma-api-request-context"; |
@@ -20,2 +20,3 @@ "use strict"; | ||
__exportStar(require("./mcma-api-error"), exports); | ||
__exportStar(require("./mcma-api-middleware"), exports); | ||
__exportStar(require("./mcma-api-request-config"), exports); | ||
@@ -22,0 +23,0 @@ __exportStar(require("./mcma-api-request-context"), exports); |
import { McmaApiRouteCollection } from "../routing"; | ||
import { McmaApiRequestContext } from "./mcma-api-request-context"; | ||
import { McmaApiMiddleware } from "./mcma-api-middleware"; | ||
export declare class McmaApiController { | ||
private readonly routes?; | ||
constructor(routes?: McmaApiRouteCollection); | ||
private readonly pipeline; | ||
constructor(routes?: McmaApiRouteCollection, middleware?: McmaApiMiddleware[]); | ||
handleRequest(requestContext: McmaApiRequestContext): Promise<void>; | ||
private processRoute; | ||
} |
@@ -17,5 +17,16 @@ "use strict"; | ||
routes; | ||
constructor(routes) { | ||
pipeline; | ||
constructor(routes, middleware) { | ||
this.routes = routes; | ||
this.routes = this.routes || new routing_1.McmaApiRouteCollection(); | ||
this.pipeline = this.processRoute.bind(this); | ||
if (Array.isArray(middleware)) { | ||
for (let i = middleware.length - 1; i >= 0; i--) { | ||
const mw = middleware[i]; | ||
const pipeline = this.pipeline; | ||
this.pipeline = async (requestContext) => { | ||
await mw(requestContext, pipeline); | ||
}; | ||
} | ||
} | ||
} | ||
@@ -26,117 +37,122 @@ async handleRequest(requestContext) { | ||
response.headers = getDefaultResponseHeaders(); | ||
try { | ||
await this.pipeline(requestContext); | ||
} | ||
catch (error) { | ||
const logger = requestContext.getLogger(); | ||
logger?.error(error); | ||
response.statusCode = http_statuses_1.HttpStatusCode.InternalServerError; | ||
response.headers = getDefaultResponseHeaders(); | ||
response.body = new mcma_api_error_1.McmaApiError({ | ||
status: response.statusCode, | ||
message: error.message, | ||
path: request.path | ||
}); | ||
} | ||
} | ||
async processRoute(requestContext) { | ||
const request = requestContext.request; | ||
const response = requestContext.response; | ||
let pathMatched = false; | ||
let methodMatched = false; | ||
try { | ||
let requestBodyOK = true; | ||
if (request.body && typeof request.body === "string" && request.headers[Object.keys(request.headers).find(h => h.toLowerCase() === "content-type")]?.toLowerCase().startsWith("application/json")) { | ||
try { | ||
request.body = JSON.parse(request.body, core_1.Utils.reviver); | ||
let requestBodyOK = true; | ||
if (request.body && typeof request.body === "string" && request.headers[Object.keys(request.headers).find(h => h.toLowerCase() === "content-type")]?.toLowerCase().startsWith("application/json")) { | ||
try { | ||
request.body = JSON.parse(request.body, core_1.Utils.reviver); | ||
} | ||
catch (error) { | ||
response.statusCode = http_statuses_1.HttpStatusCode.BadRequest; | ||
response.body = new mcma_api_error_1.McmaApiError({ | ||
status: response.statusCode, | ||
message: error.message, | ||
path: request.path | ||
}); | ||
requestBodyOK = false; | ||
} | ||
} | ||
if (requestBodyOK) { | ||
let methodsAllowed = ""; | ||
for (const route of this.routes) { | ||
if (route.template.test(request.path, { strict: true })) { | ||
pathMatched = true; | ||
if (methodsAllowed) { | ||
methodsAllowed += ", "; | ||
} | ||
methodsAllowed += route.httpMethod; | ||
if (route.httpMethod === request.httpMethod) { | ||
methodMatched = true; | ||
request.pathVariables = route.template.fromUri(request.path, { strict: true }); | ||
await route.handler(requestContext); | ||
break; | ||
} | ||
} | ||
catch (error) { | ||
response.statusCode = http_statuses_1.HttpStatusCode.BadRequest; | ||
response.body = new mcma_api_error_1.McmaApiError({ | ||
status: response.statusCode, | ||
message: error.message, | ||
path: request.path | ||
}); | ||
requestBodyOK = false; | ||
} | ||
} | ||
if (requestBodyOK) { | ||
let methodsAllowed = ""; | ||
for (const route of this.routes) { | ||
if (route.template.test(request.path, { strict: true })) { | ||
pathMatched = true; | ||
if (methodsAllowed) { | ||
methodsAllowed += ", "; | ||
} | ||
methodsAllowed += route.httpMethod; | ||
if (route.httpMethod === request.httpMethod) { | ||
methodMatched = true; | ||
request.pathVariables = route.template.fromUri(request.path, { strict: true }); | ||
await route.handler(requestContext); | ||
break; | ||
} | ||
if (!pathMatched) { | ||
response.statusCode = http_statuses_1.HttpStatusCode.NotFound; | ||
response.headers = getDefaultResponseHeaders(); | ||
response.body = new mcma_api_error_1.McmaApiError({ | ||
status: response.statusCode, | ||
message: "Resource not found on path '" + request.path + "'.", | ||
path: request.path | ||
}); | ||
} | ||
else if (!methodMatched) { | ||
if (!methodsAllowed.includes("OPTIONS")) { | ||
if (methodsAllowed) { | ||
methodsAllowed += ", "; | ||
} | ||
methodsAllowed += "OPTIONS"; | ||
} | ||
if (!pathMatched) { | ||
response.statusCode = http_statuses_1.HttpStatusCode.NotFound; | ||
if (request.httpMethod === "OPTIONS") { | ||
response.statusCode = http_statuses_1.HttpStatusCode.OK; | ||
response.headers = getDefaultResponseHeaders(); | ||
response.body = new mcma_api_error_1.McmaApiError({ | ||
status: response.statusCode, | ||
message: "Resource not found on path '" + request.path + "'.", | ||
path: request.path | ||
}); | ||
} | ||
else if (!methodMatched) { | ||
if (!methodsAllowed.includes("OPTIONS")) { | ||
if (methodsAllowed) { | ||
methodsAllowed += ", "; | ||
} | ||
methodsAllowed += "OPTIONS"; | ||
} | ||
if (request.httpMethod === "OPTIONS") { | ||
response.statusCode = http_statuses_1.HttpStatusCode.OK; | ||
response.headers = getDefaultResponseHeaders(); | ||
let corsMethod; | ||
let corsHeaders; | ||
// checking if its a CORS pre-flight request | ||
for (const prop in request.headers) { | ||
if (request.headers.hasOwnProperty(prop)) { | ||
if (prop.toLowerCase() === "access-control-request-method") { | ||
corsMethod = request.headers[prop]; | ||
} | ||
if (prop.toLowerCase() === "access-control-request-headers") { | ||
corsHeaders = request.headers[prop]; | ||
} | ||
let corsMethod; | ||
let corsHeaders; | ||
// checking if its a CORS pre-flight request | ||
for (const prop in request.headers) { | ||
if (request.headers.hasOwnProperty(prop)) { | ||
if (prop.toLowerCase() === "access-control-request-method") { | ||
corsMethod = request.headers[prop]; | ||
} | ||
} | ||
if (corsMethod) { // handling CORS pre-flight request | ||
response.headers["Access-Control-Allow-Methods"] = methodsAllowed; | ||
if (corsHeaders) { | ||
response.headers["Access-Control-Allow-Headers"] = corsHeaders; | ||
if (prop.toLowerCase() === "access-control-request-headers") { | ||
corsHeaders = request.headers[prop]; | ||
} | ||
} | ||
else { // handling regular OPTIONS request | ||
response.headers["Allow"] = methodsAllowed; | ||
} | ||
if (corsMethod) { // handling CORS pre-flight request | ||
response.headers["Access-Control-Allow-Methods"] = methodsAllowed; | ||
if (corsHeaders) { | ||
response.headers["Access-Control-Allow-Headers"] = corsHeaders; | ||
} | ||
} | ||
else { | ||
response.statusCode = http_statuses_1.HttpStatusCode.MethodNotAllowed; | ||
response.headers = getDefaultResponseHeaders(); | ||
else { // handling regular OPTIONS request | ||
response.headers["Allow"] = methodsAllowed; | ||
response.body = new mcma_api_error_1.McmaApiError({ | ||
status: response.statusCode, | ||
message: "Method '" + request.httpMethod + "' not allowed on path '" + request.path + "'.", | ||
path: request.path | ||
}); | ||
} | ||
} | ||
else if ((response.statusCode / 200 << 0) * 200 === 400) { | ||
if (!response.body) { | ||
response.body = new mcma_api_error_1.McmaApiError({ | ||
status: response.statusCode, | ||
message: response.errorMessage, | ||
path: request.path | ||
}); | ||
} | ||
else { | ||
response.statusCode = http_statuses_1.HttpStatusCode.MethodNotAllowed; | ||
response.headers = getDefaultResponseHeaders(); | ||
response.headers["Allow"] = methodsAllowed; | ||
response.body = new mcma_api_error_1.McmaApiError({ | ||
status: response.statusCode, | ||
message: "Method '" + request.httpMethod + "' not allowed on path '" + request.path + "'.", | ||
path: request.path | ||
}); | ||
} | ||
else if (response.statusCode === 0) { | ||
response.statusCode = http_statuses_1.HttpStatusCode.OK; | ||
} | ||
else if ((response.statusCode / 200 << 0) * 200 === 400) { | ||
if (!response.body) { | ||
response.body = new mcma_api_error_1.McmaApiError({ | ||
status: response.statusCode, | ||
message: response.errorMessage, | ||
path: request.path | ||
}); | ||
} | ||
} | ||
else if (response.statusCode === 0) { | ||
response.statusCode = http_statuses_1.HttpStatusCode.OK; | ||
} | ||
} | ||
catch (error) { | ||
const logger = requestContext.getLogger(); | ||
logger?.error(error); | ||
response.statusCode = http_statuses_1.HttpStatusCode.InternalServerError; | ||
response.headers = getDefaultResponseHeaders(); | ||
response.body = new mcma_api_error_1.McmaApiError({ | ||
status: response.statusCode, | ||
message: error.message, | ||
path: request.path | ||
}); | ||
} | ||
} | ||
} | ||
exports.McmaApiController = McmaApiController; |
{ | ||
"name": "@mcma/api", | ||
"version": "0.16.1", | ||
"version": "0.16.2", | ||
"description": "Node module for building APIs based on the EBU MCMA framework", | ||
@@ -40,12 +40,12 @@ "engines": { | ||
"peerDependencies": { | ||
"@mcma/client": "0.16.1", | ||
"@mcma/core": "0.16.1", | ||
"@mcma/data": "0.16.1", | ||
"@mcma/worker-invoker": "0.16.1" | ||
"@mcma/client": "0.16.2", | ||
"@mcma/core": "0.16.2", | ||
"@mcma/data": "0.16.2", | ||
"@mcma/worker-invoker": "0.16.2" | ||
}, | ||
"devDependencies": { | ||
"@mcma/client": "0.16.1", | ||
"@mcma/core": "0.16.1", | ||
"@mcma/data": "0.16.1", | ||
"@mcma/worker-invoker": "0.16.1", | ||
"@mcma/client": "0.16.2", | ||
"@mcma/core": "0.16.2", | ||
"@mcma/data": "0.16.2", | ||
"@mcma/worker-invoker": "0.16.2", | ||
"@types/node": "^18.15.10", | ||
@@ -52,0 +52,0 @@ "@types/pluralize": "0.0.29", |
62166
53
1360