Skip to content

Commit

Permalink
Merge pull request #6 from evertdespiegeleer/linting
Browse files Browse the repository at this point in the history
style: 🎨 setup linting
  • Loading branch information
evertdespiegeleer authored Jan 7, 2024
2 parents 6209980 + 95103af commit 75637cb
Show file tree
Hide file tree
Showing 17 changed files with 2,002 additions and 470 deletions.
23 changes: 23 additions & 0 deletions .eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": "standard-with-typescript",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"rules": {
"@typescript-eslint/explicit-function-return-type": "off"
},
"overrides": [
{
"files": ["*.test.ts"],
"rules": {
// Disable on test files so we can usu chai `...to.be.undefined` style syntax
"@typescript-eslint/no-unused-expressions": "off"
}
}
]
}
22 changes: 22 additions & 0 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@ on:
- main

jobs:
lint:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3
with:
fetch-depth: 0
ref: ${{ github.event.pull_request.head.ref }}

- name: Set Node.js 20.x
uses: actions/setup-node@v3
with:
node-version: 20.x

- name: Install Dependencies
run: yarn install --frozen-lockfile

- name: Lint
run: yarn lint

build:
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -68,7 +88,9 @@ jobs:
if: github.event_name == 'pull_request' && github.event.action != 'closed'
runs-on: ubuntu-latest
needs:
- lint
- build
- test
steps:
- name: Checkout code
uses: actions/checkout@v3
Expand Down
14 changes: 11 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"type": "git",
"url": "https://github.com/evertdespiegeleer/zhttp.git"
},
"version": "0.0.3",
"version": "0.0.4-linting.0",
"type": "module",
"main": "./dist/main.js",
"types": "./dist/main.d.ts",
Expand All @@ -22,7 +22,9 @@
"scripts": {
"build": "tsc -p tsconfig.build.json",
"test": "node --test --test-reporter spec --test-reporter-destination stdout --test-reporter junit --test-reporter-destination=./test-report.xml --loader ts-node/esm ./src/**/*.test.ts",
"prepare": "husky install"
"prepare": "husky install",
"lint": "eslint ./src/**/*.ts",
"lint:fix": "yarn run lint --fix"
},
"dependencies": {
"@asteasolutions/zod-to-openapi": "^6.3.1",
Expand All @@ -43,14 +45,20 @@
"@types/sinon": "^17.0.2",
"@types/supertest": "^6.0.2",
"@types/uuid": "^9.0.7",
"@typescript-eslint/eslint-plugin": "^6.4.0",
"chai": "^5.0.0",
"devmoji": "^2.3.0",
"eslint": "^8.0.1",
"eslint-config-standard-with-typescript": "^43.0.0",
"eslint-plugin-import": "^2.25.2",
"eslint-plugin-n": "^15.0.0 || ^16.0.0 ",
"eslint-plugin-promise": "^6.0.0",
"husky": "^8.0.0",
"macha": "^0.0.0",
"sinon": "^17.0.1",
"supertest": "^6.3.3",
"ts-node": "^10.9.2",
"typescript": "^5.3.3"
"typescript": "*"
},
"peerDependencies": {
"zod": "^3.22.4"
Expand Down
31 changes: 16 additions & 15 deletions src/controllers/openapi.integration.test.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,27 @@
import { Server } from '../app.js';
import supertest from 'supertest';
import { describe, it, before, after } from 'node:test';
import { openapiController } from './openapi.js';
/* eslint-disable @typescript-eslint/no-floating-promises */
import { Server } from '../app.js'
import supertest from 'supertest'
import { describe, it, before, after } from 'node:test'
import { openapiController } from './openapi.js'

describe('openapiController', () => {
let http: Server;
let http: Server
before(async () => {
http = new Server({
controllers: [openapiController]
}, { port: undefined });
await http.start();
});
}, { port: undefined })
await http.start()
})

after(async () => {
await http.stop();
});
await http.stop()
})

it('Serves a open api spec', (t, done) => {
supertest(http.expressInstance).get('/openapi.json').expect(200, done);
});
supertest(http.expressInstance).get('/openapi.json').expect(200, done)
})

it('Serves api docs', (t, done) => {
supertest(http.expressInstance).get('/api.html').expect(200, done);
});
});
supertest(http.expressInstance).get('/api.html').expect(200, done)
})
})
12 changes: 6 additions & 6 deletions src/controllers/openapi.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { controller } from '../util/controller.js';
import { get } from '../util/endpoint.js';
import { oasInstance } from '../app.js';
import { controller } from '../util/controller.js'
import { get } from '../util/endpoint.js'
import { oasInstance } from '../app.js'

export const openapiController = controller('OpenAPI')
.description('Meta information about the application')
.endpoints([
get('/openapi.json', 'getOpenAPISpec')
.responseContentType('application/json')
.handler(async () => {
return oasInstance.getJsonSpec();
return oasInstance.getJsonSpec()
}),

get('/api.html', 'API usage')
Expand All @@ -34,6 +34,6 @@ export const openapiController = controller('OpenAPI')
/>
</body>
</html>
`,
`
)
]);
])
40 changes: 20 additions & 20 deletions src/middleware/errorHandler.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,43 @@
import { Request, Response, NextFunction } from 'express';
import { MiddlewareTypes, middleware } from '../util/middleware.js';
import { apiResponse } from '../util/apiResponse.js';
import { ConflictError, ZHTTPError, InternalServerError } from '../util/errors.js';
import { ILogger } from '../util/logger.js';
import { type Request, type Response, type NextFunction } from 'express'
import { MiddlewareTypes, middleware } from '../util/middleware.js'
import { apiResponse } from '../util/apiResponse.js'
import { ConflictError, ZHTTPError, InternalServerError } from '../util/errors.js'
import { type ILogger } from '../util/logger.js'

export const makeErrorHandlerMiddleware = (logger: ILogger) => middleware({
name: 'ErrorHandler',
type: MiddlewareTypes.AFTER,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
handler(
handler (
originalError: Error,
req: Request,
res: Response,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
next: NextFunction,
next: NextFunction
) {
let status = 500;
let parsedError = new InternalServerError();
let status = 500
let parsedError = new InternalServerError()

const log = logger('errorHandler');
const log = logger('errorHandler')

if (originalError.name === 'UniqueViolationError') {
status = 409;
parsedError = new ConflictError(parsedError.message);
status = 409
parsedError = new ConflictError(parsedError.message)
}

if (originalError instanceof ZHTTPError) {
status = originalError.http;
parsedError = originalError;
status = originalError.http
parsedError = originalError
}

// log.error(originalError);
if (status >= 500) {
log.error(`🔴 FAIL ${req.method} ${req.originalUrl}`, parsedError);
log.error(`🔴 FAIL ${req.method} ${req.originalUrl}`, parsedError)
} else {
log.warn(`⚠️ FAIL ${req.method} ${req.originalUrl}`, parsedError);
log.warn(`⚠️ FAIL ${req.method} ${req.originalUrl}`, parsedError)
}

res.status(status).json(apiResponse({}, { error: parsedError }));
return res.end();
},
});
res.status(status).json(apiResponse({}, { error: parsedError }))
return res.end()
}
})
67 changes: 34 additions & 33 deletions src/middleware/errorHandler.unit.test.ts
Original file line number Diff line number Diff line change
@@ -1,65 +1,66 @@
import { Server } from '../app.js';
import supertest, { type Response } from 'supertest';
import { expect } from 'chai';
import { describe, it, before, after } from 'node:test';
import { controller, get } from '../main.js';
import { BadRequestError, ConflictError } from '../util/errors.js';
/* eslint-disable @typescript-eslint/no-floating-promises */
import { Server } from '../app.js'
import supertest, { type Response } from 'supertest'
import { expect } from 'chai'
import { describe, it, before, after } from 'node:test'
import { controller, get } from '../main.js'
import { BadRequestError, ConflictError } from '../util/errors.js'

describe('errorHandler', () => {
let http: Server;
let http: Server
before(async () => {
const testController = controller('test-controller').endpoints([
get('/unhandled-error').handler(() => {
throw new Error('Unhandled error');
throw new Error('Unhandled error')
}),

get('/bad-request').handler(() => {
throw new BadRequestError('Something went wrong :(');
throw new BadRequestError('Something went wrong :(')
}),

get('/unique-violation').handler(() => {
throw new ConflictError('Something went wrong :(');
}),
]);
throw new ConflictError('Something went wrong :(')
})
])

http = new Server(
{
controllers: [testController],
controllers: [testController]
},
{ port: undefined },
);
{ port: undefined }
)

await http.start();
});
await http.start()
})

after(async () => {
await http.stop();
});
await http.stop()
})

it('Can handle unhandled errors', (t, done) => {
supertest(http.expressInstance).get('/unhandled-error').expect(500, done);
});
supertest(http.expressInstance).get('/unhandled-error').expect(500, done)
})

it('Can handle bad requests', (t, done) => {
supertest(http.expressInstance)
.get('/bad-request')
.expect(400)
.end((err: any, res: Response) => {
if (err) return done(err);
expect(res.body).to.have.property('meta');
expect(res.body.meta.error).to.have.property('code', 'BadRequestError');
return done();
});
});
if (err != null) { done(err); return }
expect(res.body).to.have.property('meta')
expect(res.body.meta.error).to.have.property('code', 'BadRequestError')
done()
})
})

it('Can handle unique violations', (t, done) => {
supertest(http.expressInstance)
.get('/unique-violation')
.expect(409)
.end((err: any, res: Response) => {
if (err) return done(err);
expect(res.body.meta.error).to.have.property('code', 'ConflictError');
return done();
});
});
});
if (err != null) { done(err); return }
expect(res.body.meta.error).to.have.property('code', 'ConflictError')
done()
})
})
})
26 changes: 13 additions & 13 deletions src/middleware/metrics.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
import { NextFunction, Request, Response } from 'express';
import { Counter } from 'prom-client';
import { MiddlewareTypes, middleware } from '../util/middleware.js';
import { type NextFunction, type Request, type Response } from 'express'
import { Counter } from 'prom-client'
import { MiddlewareTypes, middleware } from '../util/middleware.js'

const metrics = {
httpRequests: new Counter({
name: 'http_requests_total',
help: 'Total number of HTTP requests',
labelNames: ['method', 'path', 'status'],
}),
};
labelNames: ['method', 'path', 'status']
})
}

export const metricMiddleware = middleware({
name: 'metricMiddleware',
type: MiddlewareTypes.BEFORE,
handler(req: Request, res: Response, next: NextFunction) {
handler (req: Request, res: Response, next: NextFunction) {
res.once('finish', () => {
metrics.httpRequests.inc({
method: req.method,
path: req.originalUrl,
status: res.statusCode,
});
});
return next();
},
});
status: res.statusCode
})
})
next()
}
})
Loading

0 comments on commit 75637cb

Please sign in to comment.