diff --git a/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.test.ts b/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.test.ts index 84e2ed104c..d0de341a6e 100644 --- a/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.test.ts +++ b/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.test.ts @@ -209,7 +209,7 @@ describe('HttpPostIngressEventPublisher', () => { expect.objectContaining({ error: { message: - 'Unsupported media type: text/plain. You need to provide a custom body parser for this media type using events extension.', + 'Unsupported media type: text/plain. You need to provide a custom body parser for this media type using the EventsExtensionPoint.', name: 'UnsupportedMediaTypeError', statusCode: 415, }, @@ -259,6 +259,47 @@ describe('HttpPostIngressEventPublisher', () => { expect(response.status).toBe(202); }); + it('with a invalid media type', async () => { + const config = new ConfigReader({ + events: { + http: { + topics: ['testA'], + }, + }, + }); + + const router = Router(); + const app = express().use(router); + const events = new TestEventsService(); + + const publisher = HttpPostIngressEventPublisher.fromConfig({ + config, + events, + logger, + }); + publisher.bind(router); + router.use(middleware.error()); + + const response = await request(app) + .post('/http/testA') + .type('not-valid-content/plain') + .timeout(1000) + .send('Textual information'); + expect(response.status).toBe(415); + expect(response.body).toEqual( + expect.objectContaining({ + error: { + message: + 'Unsupported media type: not-valid-content/plain. You need to provide a custom body parser for this media type using the EventsExtensionPoint.', + name: 'UnsupportedMediaTypeError', + statusCode: 415, + }, + request: { method: 'POST', url: '/http/testA' }, + response: { statusCode: 415 }, + }), + ); + }); + it('with a custom application/json body parser implementation', async () => { const config = new ConfigReader({ events: { @@ -273,7 +314,11 @@ describe('HttpPostIngressEventPublisher', () => { const events = new TestEventsService(); const customParse = jest.fn(); - const bodyParser: HttpBodyParser = async (req, _topic) => { + const bodyParser: HttpBodyParser = async ( + req, + _parsedMediaType, + _topic, + ) => { customParse(); return { bodyParsed: JSON.parse(req.body.toString('utf-8')), diff --git a/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.ts b/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.ts index 4ddcb2a291..01d07e64d6 100644 --- a/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.ts +++ b/plugins/events-backend/src/service/http/HttpPostIngressEventPublisher.ts @@ -103,7 +103,7 @@ export class HttpPostIngressEventPublisher { router.post(path, async (request, response) => { const requestContentType = contentType.parse(request); - const bodyParser = this.bodyParsers[requestContentType.type]; + const bodyParser = this.bodyParsers[requestContentType.type ?? '']; if (!bodyParser) { throw new UnsupportedMediaTypeError(requestContentType.type); @@ -111,6 +111,7 @@ export class HttpPostIngressEventPublisher { const { bodyParsed, bodyBuffer, encoding } = await bodyParser( request, + requestContentType, topic, ); diff --git a/plugins/events-backend/src/service/http/body-parser/HttpApplicationJsonBodyParser.ts b/plugins/events-backend/src/service/http/body-parser/HttpApplicationJsonBodyParser.ts index b253d1babf..fa716a5db0 100644 --- a/plugins/events-backend/src/service/http/body-parser/HttpApplicationJsonBodyParser.ts +++ b/plugins/events-backend/src/service/http/body-parser/HttpApplicationJsonBodyParser.ts @@ -14,11 +14,11 @@ * limitations under the License. */ import { HttpBodyParser } from '@backstage/plugin-events-node'; -import contentType from 'content-type'; import { UnsupportedCharsetError } from '../errors'; export const HttpApplicationJsonBodyParser: HttpBodyParser = async ( request, + parsedMediaType, topic, ) => { const requestBody = request.body; @@ -29,16 +29,15 @@ export const HttpApplicationJsonBodyParser: HttpBodyParser = async ( } const bodyBuffer: Buffer = requestBody; - const parsedContentType = contentType.parse(request); - const encoding = parsedContentType.parameters.charset ?? 'utf-8'; + const encoding = parsedMediaType.parameters.charset ?? 'utf-8'; if (!Buffer.isEncoding(encoding)) { throw new UnsupportedCharsetError(encoding); } const bodyString = bodyBuffer.toString(encoding); const bodyParsed = - parsedContentType.type === 'application/json' + parsedMediaType.type === 'application/json' ? JSON.parse(bodyString) : bodyString; return { bodyParsed, bodyBuffer, encoding }; diff --git a/plugins/events-node/package.json b/plugins/events-node/package.json index 068c0d55f9..c98ae64499 100644 --- a/plugins/events-node/package.json +++ b/plugins/events-node/package.json @@ -56,6 +56,7 @@ "@backstage/errors": "workspace:^", "@backstage/types": "workspace:^", "@types/express": "^4.17.6", + "content-type": "^1.0.5", "cross-fetch": "^4.0.0", "express": "^4.17.1", "uri-template": "^2.0.0" diff --git a/plugins/events-node/report.api.md b/plugins/events-node/report.api.md index d68e321e8f..705083ce16 100644 --- a/plugins/events-node/report.api.md +++ b/plugins/events-node/report.api.md @@ -9,6 +9,7 @@ import { AuthService } from '@backstage/backend-plugin-api'; import { DiscoveryService } from '@backstage/backend-plugin-api'; import { LifecycleService } from '@backstage/backend-plugin-api'; import { LoggerService } from '@backstage/backend-plugin-api'; +import { ParsedMediaType } from 'content-type'; import { Request as Request_2 } from 'express'; import { RootConfigService } from '@backstage/backend-plugin-api'; import { ServiceFactory } from '@backstage/backend-plugin-api'; @@ -122,6 +123,7 @@ export type HttpBodyParsed = { // @public (undocumented) export type HttpBodyParser = ( request: Request_2, + parsedMediaType: ParsedMediaType, topic: string, ) => Promise; diff --git a/plugins/events-node/src/api/http/body-parser/HttpBodyParser.ts b/plugins/events-node/src/api/http/body-parser/HttpBodyParser.ts index cfd65a6871..c0b6a46b95 100644 --- a/plugins/events-node/src/api/http/body-parser/HttpBodyParser.ts +++ b/plugins/events-node/src/api/http/body-parser/HttpBodyParser.ts @@ -15,6 +15,7 @@ */ import { Request } from 'express'; +import { ParsedMediaType } from 'content-type'; /** * @public */ @@ -29,5 +30,6 @@ export type HttpBodyParsed = { */ export type HttpBodyParser = ( request: Request, + parsedMediaType: ParsedMediaType, topic: string, ) => Promise; diff --git a/yarn.lock b/yarn.lock index 8d7ec6b3bf..1b4880e8b1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6728,6 +6728,7 @@ __metadata: "@backstage/errors": "workspace:^" "@backstage/types": "workspace:^" "@types/express": ^4.17.6 + content-type: ^1.0.5 cross-fetch: ^4.0.0 express: ^4.17.1 msw: ^1.0.0