This might sound like an abuse of Promises, but here's my use case.
I'm writing a NodeJS based tool to validate an API definition stored in an OAS (Swagger) file. Part of this task involves checking the validity of the endpoints constructed by concatenating the "host" and "basepath" properties with each of the endpoint names listed in the "paths" property.
I'm using the NodeJS library 'promise' to wrap the standard 'request' library using this line
var request = require('request')
var Promise = require('promise')
// Wrap the HTTP HEAD request in a promise
request.head = Promise.denodeify(request.head)
I'm using an HTTP HEAD request because I'm not interested in actually invoking the API - I just want to make sure that the URL shown in the OAS file points to a valid destination.
This means that almost all response status codes are acceptable except 404 (Not found) and 410 (Gone).
However, in the event of an HTTP 503 (Service unavailable) response, the 'request' NodeJS library reports this as an error and this then causes the Promise wrapping the request.head() call to be rejected.
Due to the wide variability in the way APIs can be implemented, if the API Management layer sitting behind the URL I'm checking wants to explicitly block HTTP HEAD requests, then it might do so by responding with HTTP 503 rather than the more expected HTTP 405 (Method not allowed). Also, if the API has been sandboxed, then HTTP 503 is the expected status code.
Either way, HTTP 503 demonstrates that this URL is in fact valid, even though I called it using a potentially disallowed HTTP method.
I think "unrejecting" the Promise is an abuse (and probably also impossible), so I'm left with the possibility of having to handle the HTTP requests myself in such a way that 503 is not considered an error.
Anyone got a better idea?
Thanks
Chris W
I think "unrejecting" the Promise is an abuse (and probably also impossible),
"Unrejecting" a Promise is indeed impossible. The only way to change state is by forming a promise chain with .then()
or .catch()
and making appropriate returns (or throwing) from the callback(s).
so I'm left with the possibility of having to handle the HTTP requests myself in such a way that 503 is not considered an error.
This is probably the most realistic way ahead, ie a bespoke, manual promisification of request.head()
.
The question indicates the list of bad codes to be short (404 and 410), therefore it's easier to reject only if those codes are encountered.
function testUrl(url) {
const badCodes = [404, 410];
return new Promise((resolve, reject) => {
request.head(url, (error, response, body) => {
if(error) {
if(response && badCodes.indexOf(+response.statusCode) === -1) {
resolve(response.statusCode);
} else {
reject error;
}
} else {
resolve(response.statusCode);
}
});
});
}
Alternatively, if the list of error codes you want to accept was short, then the test logic would be easily inverted.
function testUrl(url) {
const goodCodes = [503];
return new Promise((resolve, reject) => {
request.head(url, (error, response, body) => {
if(error) {
if(response && goodCodes.indexOf(+response.statusCode) > -1) {
resolve(response.statusCode);
} else {
reject(error);
}
} else {
resolve(response.statusCode);
}
});
});
}
You can play with the logic to get exactly what you want.
Whatever you end up with, call :
testUrl('http://path/to/resource').then(function(statusCode) {
// pass
}, function(error) {
// fail
});