Skip to content

Commit

Permalink
Add types
Browse files Browse the repository at this point in the history
  • Loading branch information
mantoni committed Jan 30, 2024
1 parent 5f871b1 commit ab1c7cd
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 24 deletions.
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ jobs:
- name: Install
run: npm ci
- name: Lint
if: matrix.node-version == '20.x'
run: npm run lint
- name: Types
if: matrix.node-version == '20.x'
run: node_modules/.bin/tsc
- name: Test
run: npm test
93 changes: 76 additions & 17 deletions lib/request.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,29 @@ const https = require('https');
const logger = require('@studio/log');
const { failure, E_FAILED } = require('@studio/fail');

/**
* @typedef {import('http').RequestOptions} RequestOptions
* @typedef {import('http').ClientRequest} ClientRequest
* @typedef {import('http').IncomingMessage} IncomingMessage
* @typedef {import('stream').Readable} Readable
* @typedef {import('@studio/fail').Failure} Failure
* @typedef {import('@studio/log').Logger} Logger
*/

/**
* @typedef {Object} JsonRequestOptions
* @property {number} [timeout]
* @property {number | number[]} [expect]
* @property {boolean} [stream]
* @property {Logger} [log]
*/

/**
* @typedef {undefined | null | boolean | number | string | JsonArray | JsonObject} JsonValue
* @typedef {JsonValue[]} JsonArray
* @typedef {{ [k: string]: JsonValue }} JsonObject
*/

const logger_name = 'Request';
const default_log = logger(logger_name);

Expand All @@ -19,6 +42,11 @@ const PROTOCOLS = {
'https:': https
};

/**
* @param {string | number} expect
* @param {number} status
* @returns {Failure}
*/
function expectError(expect, status) {
return failure(
`Expected response statusCode to be ${expect}, but was ${status}`,
Expand All @@ -27,11 +55,32 @@ function expectError(expect, status) {
);
}

/**
* @template T
* @param {T} obj
* @returns {T}
*/
function copy(obj) {
return { ...obj };
}

module.exports = function fetch(options, data, callback) {
module.exports = fetch;

/**
* @callback FetchCallback
* @param {Error | null} err
* @param {Object | null} [json]
* @param {IncomingMessage} [res]
*/

/**
* @param {RequestOptions & JsonRequestOptions} options
* @param {null | string | JsonObject | Readable | FetchCallback} data
* @param {FetchCallback} [callback]
* @returns {ClientRequest}
*/
// eslint-disable-next-line complexity
function fetch(options, data, callback) {
if (typeof data === 'function') {
callback = data;
data = null;
Expand Down Expand Up @@ -63,7 +112,7 @@ module.exports = function fetch(options, data, callback) {
delete opts.log;
}

if (data && !data.pipe) {
if (data && typeof data !== 'string' && !data.pipe) {
data = JSON.stringify(data);
if (opts.headers) {
opts.headers = copy(opts.headers);
Expand All @@ -86,7 +135,7 @@ module.exports = function fetch(options, data, callback) {
if (options.port) {
request.port = options.port;
}
if (data && !data.pipe) {
if (typeof data === 'string') {
request.body = data;
}

Expand Down Expand Up @@ -123,7 +172,9 @@ module.exports = function fetch(options, data, callback) {
response.body = body;
const ms_body = Date.now() - ts_head;
log.warn({ request, ms_head, ms_body, response }, err.message);
callback(err, null, res);
if (callback) {
callback(err, null, res);
}
});
return;
}
Expand Down Expand Up @@ -154,15 +205,19 @@ module.exports = function fetch(options, data, callback) {
const e = failure('Response failure', res_err, E_FAILED);
const ms_body = Date.now() - ts_head;
log.error({ request, ms_head, ms_body, response }, res_err);
callback(e);
if (callback) {
callback(e);
}
});
if (opts.stream) {
log.fetch({ request, ms_head, response });
res.on('end', () => {
const ms_body = Date.now() - ts_head;
log.finish({ ms_body });
});
callback(null, res);
if (callback) {
callback(null, res);
}
return;
}

Expand All @@ -186,13 +241,17 @@ module.exports = function fetch(options, data, callback) {
);
response.body = body;
log.error({ request, ms_head, ms_body, response });
callback(json_err, body, res);
if (callback) {
callback(json_err, body, res);
}
return;
}
response.json = json;
}
log.fetch({ request, response, ms_head, ms_body });
callback(null, json, res);
if (callback) {
callback(null, json, res);
}
});
});

Expand All @@ -201,17 +260,17 @@ module.exports = function fetch(options, data, callback) {
req.destroy();
const err = failure('Request timeout', 'E_TIMEOUT');
log.warn({ ms: Date.now() - ts_start, request });
callback(err);
callback = null;
if (callback) {
callback(err);
}
callback = undefined;
}, timeout);
}

if (data) {
if (data.pipe) {
data.pipe(req);
} else {
req.end(data);
}
if (typeof data === 'string') {
req.end(data);
} else if (data && typeof data.pipe === 'function') {
data.pipe(req);
} else {
req.end();
}
Expand All @@ -227,4 +286,4 @@ module.exports = function fetch(options, data, callback) {
}
});
return req;
};
}
67 changes: 65 additions & 2 deletions package-lock.json

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

18 changes: 15 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,11 @@
"lint": "eslint .",
"test": "mocha",
"watch": "mocha --watch",
"preversion": "npm run lint && npm test",
"build": "tsc --project tsconfig.pack.json",
"clean": "rimraf --glob 'lib/*.d.ts'",
"prepack": "npm run build",
"postpack": "npm run clean",
"preversion": "npm run lint && tsc && npm test",
"version": "changes --commits --footer",
"postversion": "git push --follow-tags && npm publish"
},
Expand All @@ -29,19 +33,27 @@
},
"dependencies": {
"@studio/fail": "^1.8.0",
"@studio/log": "^2.1.2"
"@studio/log": "^2.1.2",
"@types/node": "^16.18.76"
},
"devDependencies": {
"@sinonjs/referee-sinon": "^12.0.0",
"@studio/changes": "^3.0.0",
"@studio/eslint-config": "^6.0.0",
"@studio/log-x": "^1.3.1",
"@studio/tsconfig": "^1.3.0",
"eslint": "^8.56.0",
"mocha": "^10.2.0"
"mocha": "^10.2.0",
"typescript": "^5.3.3"
},
"repository": {
"type": "git",
"url": "https://github.com/javascript-studio/studio-json-request.git"
},
"files": [
"lib",
"LICENSE",
"README.md"
],
"license": "MIT"
}
7 changes: 5 additions & 2 deletions test/integration-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ const { assert, refute, match, sinon } = require('@sinonjs/referee-sinon');
const http = require('http');
const request = require('..');

/**
* @typedef {import('http').IncomingMessage} IncomingMessage
*/

describe('integration', () => {
let server;
Expand All @@ -24,7 +27,7 @@ describe('integration', () => {
timeout: 10
}, (err) => {
refute.isNull(err);
assert.equals(err.message, 'Request timeout');
assert.equals(/** @type {Error} */ (err).message, 'Request timeout');
setTimeout(() => {
assert.calledOnceWith(onError, match({ message: 'socket hang up' }));
done();
Expand Down Expand Up @@ -57,7 +60,7 @@ describe('integration', () => {
expect: [200, 302]
}, (err, json, res) => {
assert.isNull(err);
assert.equals(res.statusCode, 200);
assert.equals(/** @type {IncomingMessage} */ (res).statusCode, 200);
assert.equals(json, { hello: 'redirect' });
done();
});
Expand Down
Loading

0 comments on commit ab1c7cd

Please sign in to comment.