Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[#IP-86] tslint to eslint migration #16

Merged
merged 8 commits into from
Apr 19, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 21 additions & 33 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,22 @@
module.exports = {
"env": {
"browser": true,
"es6": true,
"node": true
},
"ignorePatterns": [
"node_modules",
"generated",
"**/__tests__/*",
"**/__mocks__/*",
"Dangerfile.*",
"*.d.ts"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"project": "tsconfig.json",
"sourceType": "module"
},
"extends": [
"@pagopa/eslint-config/strong",
],
"rules": {
"import/order": "off",
"@typescript-eslint/array-type": "off",
"@typescript-eslint/explicit-member-accessibility": "off",
"functional/prefer-readonly-type": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"no-invalid-this": "off",
"prefer-arrow/prefer-arrow-functions": "off",
"@typescript-eslint/prefer-optional-chain": "off",
"no-underscore-dangle": "off"
michaeldisaro marked this conversation as resolved.
Show resolved Hide resolved
}
}
env: {
browser: true,
es6: true,
node: true
},
ignorePatterns: [
"node_modules",
"generated",
"**/__tests__/*",
"**/__mocks__/*",
"Dangerfile.*",
"*.d.ts"
],
parser: "@typescript-eslint/parser",
parserOptions: {
project: "tsconfig.json",
sourceType: "module"
},
extends: ["@pagopa/eslint-config/strong"],
rules: {}
};
44 changes: 23 additions & 21 deletions src/ExpressAdapter.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Context } from "@azure/functions";
import EventEmitter = require("events");
import { Context } from "@azure/functions";
import { Application } from "express";

import IncomingMessage from "./IncomingMessage";
Expand All @@ -21,7 +21,7 @@ const isValidContext = (context: any): context is Context =>
context.bindings.req.originalUrl &&
typeof context.bindings.req.originalUrl === "string";

export type RequestListener = (...args: readonly unknown[]) => void;
export type RequestListener = (...args: ReadonlyArray<unknown>) => void;

/**
* Express adapter allowing to handle Azure Function requests by wrapping in request events.
Expand All @@ -33,7 +33,7 @@ export default class ExpressAdapter extends EventEmitter {
/**
* @param {Object=} application Request listener (typically an express/connect instance)
*/
public constructor(application: Application) {
constructor(application: Application) {
super();

if (application !== undefined) {
Expand All @@ -53,25 +53,27 @@ export default class ExpressAdapter extends EventEmitter {
/**
* Create function ready to be exposed to Azure Function for request handling.
*/
public createAzureFunctionHandler = () => (context: Context) => {
if (!isValidContext(context)) {
return;
}
public createAzureFunctionHandler() {
return (context: Context): void => {
if (!isValidContext(context)) {
return;
}

const updateResponse = (
updater: (
prev: NonNullable<Context["res"]>
) => NonNullable<Context["res"]>
) => {
// eslint-disable-next-line functional/immutable-data
context.res = updater(context.res || {});
};
const updateResponse = (
updater: (
prev: NonNullable<Context["res"]>
) => NonNullable<Context["res"]>
): void => {
// eslint-disable-next-line functional/immutable-data
context.res = updater(context.res || {});
};

// 2. Wrapping
const req = new IncomingMessage(context);
const res = new OutgoingMessage(updateResponse, context.done);
// 2. Wrapping
const req = new IncomingMessage(context);
const res = new OutgoingMessage(updateResponse, context.done);

// 3. Synchronously calls each of the listeners registered for the event
this.emit("request", req, res);
};
// 3. Synchronously calls each of the listeners registered for the event
this.emit("request", req, res);
};
}
}
34 changes: 15 additions & 19 deletions src/IncomingMessage.ts
Original file line number Diff line number Diff line change
@@ -1,34 +1,32 @@
import { Context } from "@azure/functions";
import { Socket } from "net";
import { Readable } from "stream";
import { TLSSocket } from "tls";
import { Context } from "@azure/functions";

const NOOP = () => true;
const NOOP = (): true => true;

function removePortFromAddress(address: string): string {
return address ? address.replace(/:[0-9]*$/, "") : address;
}
const removePortFromAddress = (address: string): string =>
address ? address.replace(/:[0-9]*$/, "") : address;

/**
* Create a fake connection object
*
* @param {Object} context Raw Azure context object for a single HTTP request
* @returns {object} Connection object
*/
function createConnectionObject(
const createConnectionObject = (
context: Context
): Pick<Socket, "remoteAddress"> & Pick<TLSSocket, "encrypted"> {
): Pick<Socket, "remoteAddress"> & Pick<TLSSocket, "encrypted"> => {
const { req } = context.bindings;
const xForwardedFor = req.headers
? req.headers["x-forwarded-for"]
: undefined;

return {
encrypted:
req.originalUrl && req.originalUrl.toLowerCase().startsWith("https"),
encrypted: req.originalUrl?.toLowerCase().startsWith("https"),
remoteAddress: removePortFromAddress(xForwardedFor)
};
}
};

/**
* Copy useful context properties from the native context provided by the Azure
Expand All @@ -41,15 +39,13 @@ function createConnectionObject(
* @param {Object} context Raw Azure context object for a single HTTP request
* @returns {Object} Filtered context
*/
function sanitizeContext(context: Context): Context {
return {
...context,
// We don't want the developer to mess up express flow
// See https://github.com/yvele/azure-function-express/pull/12#issuecomment-336733540
done: NOOP,
log: context.log.bind(context)
};
}
const sanitizeContext = (context: Context): Context => ({
...context,
// We don't want the developer to mess up express flow
// See https://github.com/yvele/azure-function-express/pull/12#issuecomment-336733540
done: NOOP,
log: context.log.bind(context)
});

/**
* Request object wrapper
Expand Down
31 changes: 19 additions & 12 deletions src/OutgoingMessage.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
// eslint-disable camelcase

import { Context } from "@azure/functions";
import {
OutgoingHttpHeaders,
OutgoingMessage as NativeOutgoingMessage,
ServerResponse
} from "http";
import { Context } from "@azure/functions";

import { statusCodes } from "./statusCodes";

Expand All @@ -17,12 +17,13 @@ import { statusCodes } from "./statusCodes";
* @private
*/
export default class OutgoingMessage extends NativeOutgoingMessage {
public _hasBody = true;
public _headerNames = {};
public _headers = null;
public _removedHeader = {};
public readonly _hasBody = true;
public readonly _headerNames = {};
public readonly _headers = null;
public readonly _removedHeader = {};
// eslint-disable-next-line functional/prefer-readonly-type
public statusMessage!: string;
public statusCode!: number;
public readonly statusCode!: number;

/**
* Original implementation: https://github.com/nodejs/node/blob/v6.x/lib/_http_outgoing.js#L48
Expand All @@ -40,27 +41,29 @@ export default class OutgoingMessage extends NativeOutgoingMessage {

// Those methods cannot be prototyped because express explicitelly overrides __proto__
// See https://github.com/expressjs/express/blob/master/lib/middleware/init.js#L29
public end: NativeOutgoingMessage["end"] = (
// eslint-disable-next-line @typescript-eslint/no-explicit-any
chunkOrCb?: any
public readonly end: NativeOutgoingMessage["end"] = (
chunkOrCb: Parameters<NativeOutgoingMessage["end"]>[0]
michaeldisaro marked this conversation as resolved.
Show resolved Hide resolved
) => {
// 1. Write head
// eslint-disable-next-line no-invalid-this
michaeldisaro marked this conversation as resolved.
Show resolved Hide resolved
this.writeHead(this.statusCode); // Make jshttp/on-headers able to trigger

// 2. Return raw body to Azure Function runtime
// eslint-disable-next-line no-invalid-this
this.updateResponse(res => ({
...res,
body: chunkOrCb,
isRaw: true
}));
// eslint-disable-next-line no-invalid-this
this.done();
};

/**
* https://nodejs.org/api/http.html#http_response_writehead_statuscode_statusmessage_headers
* Original implementation: https://github.com/nodejs/node/blob/v6.x/lib/_http_server.js#L160
*/
public writeHead: ServerResponse["writeHead"] = (
public readonly writeHead: ServerResponse["writeHead"] = (
statusCode: number,
reasonOrHeaders?: string | OutgoingHttpHeaders,
headersOrUndefined?: OutgoingHttpHeaders
Expand All @@ -72,7 +75,7 @@ export default class OutgoingMessage extends NativeOutgoingMessage {
}

// 2. Status message
// eslint-disable-next-line functional/immutable-data
// eslint-disable-next-line functional/immutable-data, no-invalid-this
this.statusMessage =
typeof reasonOrHeaders === "string"
? reasonOrHeaders
Expand All @@ -85,24 +88,28 @@ export default class OutgoingMessage extends NativeOutgoingMessage {
? reasonOrHeaders
: headersOrUndefined;

// eslint-disable-next-line no-underscore-dangle, no-invalid-this
if (this._headers && headers !== undefined) {
// Slow-case: when progressive API and header fields are passed.
Object.keys(headers).forEach(k => {
const v = headers[k];
if (v) {
// eslint-disable-next-line no-invalid-this
this.setHeader(k, v);
}
});
}

// 4. Sets everything
// eslint-disable-next-line no-invalid-this
this.updateResponse(res => ({
...res,
// In order to uniformize node 6 behaviour with node 8 and 10,
// we want to never have undefined headers, but instead empty object
headers:
// eslint-disable-next-line no-underscore-dangle, no-invalid-this
this._headers && headers === undefined
? // eslint-disable-next-line @typescript-eslint/no-explicit-any
? // eslint-disable-next-line no-underscore-dangle, @typescript-eslint/no-explicit-any, no-invalid-this
(this as any)._renderHeaders()
michaeldisaro marked this conversation as resolved.
Show resolved Hide resolved
: headers !== undefined
? headers
Expand Down
8 changes: 5 additions & 3 deletions src/createAzureFunctionsHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@ import ExpressAdapter from "./ExpressAdapter";
* @param {Object} requestListener Request listener (typically an express/connect instance)
* @returns {function(context: Object)} Azure Function handle
*/
export default function createAzureFunctionHandler(
const createAzureFunctionHandler = (
application: Application
): (context: Context) => void {
): ((context: Context) => void) => {
const adapter = new ExpressAdapter(application);
return adapter.createAzureFunctionHandler();
}
};

export default createAzureFunctionHandler;
2 changes: 1 addition & 1 deletion src/statusCodes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const statusCodes: { [key: number]: string | undefined } = {
export const statusCodes: { readonly [key: number]: string | undefined } = {
100: "Continue",
101: "Switching Protocols",
102: "Processing", // RFC 2518, obsoleted by RFC 4918
Expand Down