Skip to content

Commit 6aa8869

Browse files
committed
feat: handle grpc errors
1 parent 7459c60 commit 6aa8869

File tree

2 files changed

+122
-83
lines changed

2 files changed

+122
-83
lines changed

app.js

+115-83
Original file line numberDiff line numberDiff line change
@@ -2,126 +2,158 @@
22
* The application entry point
33
*/
44

5-
require('./app-bootstrap')
6-
7-
const _ = require('lodash')
8-
const config = require('config')
9-
const express = require('express')
10-
const bodyParser = require('body-parser')
11-
const cors = require('cors')
12-
const HttpStatus = require('http-status-codes')
13-
const logger = require('./src/common/logger')
14-
const interceptor = require('express-interceptor')
15-
const fileUpload = require('express-fileupload')
16-
const YAML = require('yamljs')
17-
const swaggerUi = require('swagger-ui-express')
18-
const challengeAPISwaggerDoc = YAML.load('./docs/swagger.yaml')
19-
const { ForbiddenError } = require('./src/common/errors')
20-
21-
const AWSXRay = require('aws-xray-sdk')
5+
require("./app-bootstrap");
6+
7+
const _ = require("lodash");
8+
const config = require("config");
9+
const express = require("express");
10+
const bodyParser = require("body-parser");
11+
const cors = require("cors");
12+
const HttpStatus = require("http-status-codes");
13+
const logger = require("./src/common/logger");
14+
const interceptor = require("express-interceptor");
15+
const fileUpload = require("express-fileupload");
16+
const YAML = require("yamljs");
17+
const swaggerUi = require("swagger-ui-express");
18+
const challengeAPISwaggerDoc = YAML.load("./docs/swagger.yaml");
19+
const { ForbiddenError } = require("./src/common/errors");
20+
21+
const AWSXRay = require("aws-xray-sdk");
22+
const { grpcErrorToHTTPCode } = require("./src/common/helper");
2223

2324
// setup express app
24-
const app = express()
25+
const app = express();
2526

26-
app.use(AWSXRay.express.openSegment('v5-challenge-api'))
27+
app.use(AWSXRay.express.openSegment("v5-challenge-api"));
2728

2829
// Disable POST, PUT, PATCH, DELETE operations if READONLY is set to true
2930
app.use((req, res, next) => {
30-
if (config.READONLY === true && ['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method)) {
31-
throw new ForbiddenError('Action is temporarely not allowed!')
31+
if (
32+
config.READONLY === true &&
33+
["POST", "PUT", "PATCH", "DELETE"].includes(req.method)
34+
) {
35+
throw new ForbiddenError("Action is temporarely not allowed!");
3236
}
33-
next()
34-
})
37+
next();
38+
});
3539

3640
// serve challenge V5 API swagger definition
37-
app.use('/v5/challenges/docs', swaggerUi.serve, swaggerUi.setup(challengeAPISwaggerDoc))
38-
39-
app.use(cors({
40-
exposedHeaders: [
41-
'X-Prev-Page',
42-
'X-Next-Page',
43-
'X-Page',
44-
'X-Per-Page',
45-
'X-Total',
46-
'X-Total-Pages',
47-
'Link'
48-
]
49-
}))
50-
app.use(fileUpload({
51-
limits: { fileSize: config.FILE_UPLOAD_SIZE_LIMIT }
52-
}))
53-
app.use(bodyParser.json())
54-
app.use(bodyParser.urlencoded({ extended: true }))
55-
app.set('port', config.PORT)
41+
app.use(
42+
"/v5/challenges/docs",
43+
swaggerUi.serve,
44+
swaggerUi.setup(challengeAPISwaggerDoc)
45+
);
46+
47+
app.use(
48+
cors({
49+
exposedHeaders: [
50+
"X-Prev-Page",
51+
"X-Next-Page",
52+
"X-Page",
53+
"X-Per-Page",
54+
"X-Total",
55+
"X-Total-Pages",
56+
"Link",
57+
],
58+
})
59+
);
60+
app.use(
61+
fileUpload({
62+
limits: { fileSize: config.FILE_UPLOAD_SIZE_LIMIT },
63+
})
64+
);
65+
app.use(bodyParser.json());
66+
app.use(bodyParser.urlencoded({ extended: true }));
67+
app.set("port", config.PORT);
5668

5769
// intercept the response body from jwtAuthenticator
58-
app.use(interceptor((req, res) => {
59-
return {
60-
isInterceptable: () => {
61-
return res.statusCode === 403
62-
},
63-
64-
intercept: (body, send) => {
65-
let obj
66-
try {
67-
obj = JSON.parse(body)
68-
} catch (e) {
69-
logger.error('Invalid response body.')
70-
}
71-
if (obj && obj.result && obj.result.content && obj.result.content.message) {
72-
const ret = { message: obj.result.content.message }
73-
res.statusCode = 401
74-
send(JSON.stringify(ret))
75-
} else {
76-
send(body)
77-
}
78-
}
79-
}
80-
}))
70+
app.use(
71+
interceptor((req, res) => {
72+
return {
73+
isInterceptable: () => {
74+
return res.statusCode === 403;
75+
},
76+
77+
intercept: (body, send) => {
78+
let obj;
79+
try {
80+
obj = JSON.parse(body);
81+
} catch (e) {
82+
logger.error("Invalid response body.");
83+
}
84+
if (
85+
obj &&
86+
obj.result &&
87+
obj.result.content &&
88+
obj.result.content.message
89+
) {
90+
const ret = { message: obj.result.content.message };
91+
res.statusCode = 401;
92+
send(JSON.stringify(ret));
93+
} else {
94+
send(body);
95+
}
96+
},
97+
};
98+
})
99+
);
81100

82101
// Register routes
83-
require('./app-routes')(app)
102+
require("./app-routes")(app);
84103

85-
app.use(AWSXRay.express.closeSegment())
104+
app.use(AWSXRay.express.closeSegment());
86105

87106
// The error handler
88107
// eslint-disable-next-line no-unused-vars
89108
app.use((err, req, res, next) => {
90-
logger.logFullError(err, req.signature || `${req.method} ${req.url}`)
91-
const errorResponse = {}
92-
const status = err.isJoi ? HttpStatus.BAD_REQUEST : (err.httpStatus || _.get(err, 'response.status') || HttpStatus.INTERNAL_SERVER_ERROR)
109+
logger.logFullError(err, req.signature || `${req.method} ${req.url}`);
110+
const errorResponse = {};
111+
112+
const status =
113+
err.code != null && [2].indexOf(err.code) != -1
114+
? grpcErrorToHTTPCode(err.code)
115+
: err.isJoi
116+
? HttpStatus.BAD_REQUEST
117+
: err.httpStatus ||
118+
_.get(err, "response.status") ||
119+
HttpStatus.INTERNAL_SERVER_ERROR;
93120

94121
if (_.isArray(err.details)) {
95122
if (err.isJoi) {
96123
_.map(err.details, (e) => {
97124
if (e.message) {
98125
if (_.isUndefined(errorResponse.message)) {
99-
errorResponse.message = e.message
126+
errorResponse.message = e.message;
100127
} else {
101-
errorResponse.message += `, ${e.message}`
128+
errorResponse.message += `, ${e.message}`;
102129
}
103130
}
104-
})
131+
});
105132
}
106133
}
107-
if (_.get(err, 'response.status')) {
134+
if (err.code != null && [2].indexOf(err.code) != -1) {
135+
errorResponse.message = err.details;
136+
}
137+
if (_.get(err, "response.status")) {
108138
// extra error message from axios http response(v4 and v5 tc api)
109-
errorResponse.message = _.get(err, 'response.data.result.content.message') || _.get(err, 'response.data.message')
139+
errorResponse.message =
140+
_.get(err, "response.data.result.content.message") ||
141+
_.get(err, "response.data.message");
110142
}
111143

112144
if (_.isUndefined(errorResponse.message)) {
113145
if (err.message && status !== HttpStatus.INTERNAL_SERVER_ERROR) {
114-
errorResponse.message = err.message
146+
errorResponse.message = err.message;
115147
} else {
116-
errorResponse.message = 'Internal server error'
148+
errorResponse.message = "Internal server error";
117149
}
118150
}
119151

120-
res.status(status).json(errorResponse)
121-
})
152+
res.status(status).json(errorResponse);
153+
});
122154

123-
app.listen(app.get('port'), () => {
124-
logger.info(`Express server listening on port ${app.get('port')}`)
125-
})
155+
app.listen(app.get("port"), () => {
156+
logger.info(`Express server listening on port ${app.get("port")}`);
157+
});
126158

127-
module.exports = app
159+
module.exports = app;

src/common/helper.js

+7
Original file line numberDiff line numberDiff line change
@@ -1468,6 +1468,12 @@ function setToInternalCache(key, value) {
14681468
internalCache.set(key, value);
14691469
}
14701470

1471+
function grpcErrorToHTTPCode(grpcErrorCode) {
1472+
if (grpcErrorCode == 2) return HttpStatus.NOT_FOUND;
1473+
1474+
return HttpStatus.INTERNAL_SERVER_ERROR;
1475+
}
1476+
14711477
module.exports = {
14721478
wrapExpress,
14731479
autoWrapExpress,
@@ -1524,6 +1530,7 @@ module.exports = {
15241530
updateSelfServiceProjectInfo,
15251531
getFromInternalCache,
15261532
setToInternalCache,
1533+
grpcErrorToHTTPCode,
15271534
};
15281535

15291536
logger.buildService(module.exports, {

0 commit comments

Comments
 (0)