Skip to content

Commit

Permalink
Merge pull request #333 from senacor/feature/execute-post-function-if…
Browse files Browse the repository at this point in the history
…-handler-fails

[2.1.0] Post-Execution-Functions will now be executed, even if the handler failed
  • Loading branch information
maaaNu authored Oct 21, 2023
2 parents 9d6976a + e831dc7 commit 22ccdda
Show file tree
Hide file tree
Showing 30 changed files with 494 additions and 235 deletions.
1 change: 0 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
node_modules
dist
*.test.ts
18 changes: 6 additions & 12 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,11 @@
module.exports = {
parser: "@typescript-eslint/parser", // Specifies the ESLint parser
parser: '@typescript-eslint/parser', // Specifies the ESLint parser
parserOptions: {
ecmaVersion: 2020, // Allows for the parsing of modern ECMAScript features
sourceType: "module", // Allows for the use of imports
ecmaFeatures: {
}
},
settings: {
},
extends: [
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
rules: {
sourceType: 'module', // Allows for the use of imports
ecmaFeatures: {},
},
settings: {},
extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended'],
rules: {},
};
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ jobs:

strategy:
matrix:
node-version: [ 15.x, 16.x ]
node-version: [ 16.x, 18.x ]

steps:
- uses: actions/checkout@v4
Expand Down
1 change: 0 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,4 @@ package-lock.json
*.ejs
dist
coverage
dist/
config/*
5 changes: 4 additions & 1 deletion .prettierrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ module.exports = {
trailingComma: "all",
singleQuote: true,
printWidth: 120,
tabWidth: 4
tabWidth: 4,
importOrder: ["^[./]"],
importOrderSeparation: true,
importOrderSortSpecifiers: true
};
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# CHANGELOG

## 2.1.0 (19.09.2023)
+ Post-Execution-Functions will now be executed, even if the handler failed
- The middlewareWithErrorHandling was removed. To regain the same functionality you can now pass an option-flag, called disableErrorHandling

## 2.0.0 (01.04.2023)
- Added auto-logging functionality to the library that enhances searchability of saved log statements in Azure AppInsights by storing context properties in commonProperties.
- Removed the MiddlewareFunction-Type in favor for the azure-built-in one
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Context } from '@azure/functions';
import middleware from '../../src/middleware';

import { middleware } from '../../src';
import headerAuthentication from '../../src/headerAuthentication';

const functionHandler = async (context: Context): Promise<void> => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Context, ContextBindingData } from '@azure/functions';
import middleware from '../../src/middleware';

import { middleware } from '../../src';
import authorization from '../../src/jwtAuthorization';

const functionHandler = async (context: Context): Promise<void> => {
Expand Down
5 changes: 3 additions & 2 deletions example/test-validation-function/test-validation-function.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { Context, HttpRequest } from '@azure/functions';
import middleware from '../../src/middleware';
import * as Joi from 'joi';
import validation from '../../src/validation';
import { ObjectSchema } from 'joi';

import { middleware } from '../../src';
import validation from '../../src/validation';

const schema: ObjectSchema = Joi.object({
name: Joi.string().min(3).max(30).required(),
}).required();
Expand Down
1 change: 1 addition & 0 deletions integration-test/header-authentication-test.integration.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import axios from 'axios';

import waitTillFunctionReady from './waitTillFunctionReady';

describe('The example azure function is started and the header authentication should execute the request', () => {
Expand Down
1 change: 1 addition & 0 deletions integration-test/jwt-authorization-test.integration.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import axios from 'axios';

import waitTillFunctionReady from './waitTillFunctionReady';

// Token generated with https://jwt.io/ containing the "userId" "c8e65ca7-a008-4b1c-b52a-4ad0ee417017"
Expand Down
1 change: 1 addition & 0 deletions integration-test/validation-test.integration.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import axios from 'axios';

import waitTillFunctionReady from './waitTillFunctionReady';

describe('The example azure function is started and the JOI validation should', () => {
Expand Down
100 changes: 98 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@senacor/azure-function-middleware",
"version": "2.0.1",
"version": "2.1.0",
"description": "Middleware for azure functions to handle authentication, authorization, error handling and logging",
"main": "dist/index.js",
"types": "dist/index.d.ts",
Expand All @@ -25,6 +25,7 @@
"author": "[email protected]",
"license": "MIT",
"devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.2.0",
"@types/jest": "^27.0.3",
"@typescript-eslint/eslint-plugin": "^5.10.2",
"@typescript-eslint/parser": "^5.10.2",
Expand Down
4 changes: 2 additions & 2 deletions src/appInsights/Logger.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { SeverityLevel } from 'applicationinsights/out/Declarations/Contracts';
import { TelemetryClient } from 'applicationinsights';
import { Logger } from '@azure/functions';
import { TelemetryClient } from 'applicationinsights';
import { SeverityLevel } from 'applicationinsights/out/Declarations/Contracts';

const consoleDefaultLog = (message: string): void => {
console.log(message);
Expand Down
3 changes: 2 additions & 1 deletion src/appInsights/appInsightsWrapper.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Context, HttpRequest } from '@azure/functions';
import * as appInsights from 'applicationinsights';
import { createAppInsightsLogger, consoleLogger } from './Logger';
import { TelemetryClient } from 'applicationinsights';

import { consoleLogger, createAppInsightsLogger } from './Logger';

const telemetryClients: { [key: string]: TelemetryClient } = {};

const isDisabled =
Expand Down
File renamed without changes.
39 changes: 39 additions & 0 deletions src/error/errorHandler.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { Context } from '@azure/functions';
import { mock } from 'jest-mock-extended';

import { ApplicationError } from './ApplicationError';
import { errorHandler as sut } from './errorHandler';

describe('Error-Handler should', () => {
const contextMock = mock<Context>();

beforeEach(() => {
jest.restoreAllMocks();

contextMock.log.error = jest.fn();
});

test('return an provided ApplicationError', () => {
const res = sut(new ApplicationError('', 400, { error: 'critical' }), contextMock);
expect(res.status).toStrictEqual(400);
expect(res.body).toStrictEqual({ error: 'critical' });
});

test('return an default error-message, if no errorResponseHandler was provided', () => {
const res = sut('Error!', contextMock);
expect(res.status).toStrictEqual(500);
expect(res.body).toStrictEqual({ message: 'Internal server error' });
});

test('use the errorResponseHandler, if an errorResponseHandler was provided', () => {
const errorResponseHandler = () => ({
status: 409,
body: {
message: 'conflict!',
},
});
const res = sut('Error!', contextMock, { errorResponseHandler });
expect(res.status).toStrictEqual(409);
expect(res.body).toStrictEqual({ message: 'conflict!' });
});
});
79 changes: 79 additions & 0 deletions src/error/errorHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { Context } from '@azure/functions';

import { Options } from '../middleware';
import { ApplicationError } from './ApplicationError';

type ErrorWithMessage = {
message: string;
stack?: string;
};

const isErrorWithMessage = (error: unknown): error is ErrorWithMessage => {
return (
typeof error === 'object' &&
error !== null &&
'message' in error &&
typeof (error as Record<string, unknown>).message === 'string'
);
};

const logErrorObject = (error: object | null, context: Context) => {
if (error === null) {
context.log.error('The provided error was eq to null - unable to log a specific error-message');
return;
}

if (isErrorWithMessage(error)) {
context.log.error({ message: error.message, stack: error.stack });
} else {
try {
const errorAsJson = JSON.stringify(error);
if (errorAsJson === '{}') {
context.log.error(error.toString());
} else {
context.log.error(errorAsJson); // Log the JSON string of the error object
}
} catch (_) {
//Fallback in case there's an error stringify
context.log.error(error.toString());
}
}
};

export const errorHandler = (
error: unknown,
context: Context,
opts?: Options,
): {
[key: string]: unknown;
} => {
if (error instanceof ApplicationError) {
context.log.error(`Received application error with message ${error.message}`);
return {
status: error.status,
body: error.body,
};
}

switch (typeof error) {
case 'string':
context.log.error(error);
break;
case 'object':
logErrorObject(error, context);
break;
default:
context.log(`The error object has a type, that is not suitable for logging: ${typeof error}`);
}

if (opts?.errorResponseHandler === undefined) {
return {
status: 500,
body: {
message: 'Internal server error',
},
};
} else {
return opts.errorResponseHandler(error);
}
};
Loading

0 comments on commit 22ccdda

Please sign in to comment.