Skip to content

Commit 0e1ed6a

Browse files
authored
Merge pull request #19 from graycraft/feature/2
Feature v0.4.0-alpha
2 parents b2a3d5c + 9193e20 commit 0e1ed6a

29 files changed

+438
-99
lines changed

README.md

+25-3
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,14 @@ $ node response --aggregate
191191
Aggregated "2024-01-01T00:00:00.000Z.json" to "../collection/coinbase/currency_network_all".
192192
```
193193
194+
```bash
195+
$ node response --vali
196+
$ node response --validate
197+
AJV: validation of Bybit API response snapshot 2025-02-01T23:38:48.058Z.json passed.
198+
AJV: validation of Bybit API response snapshot 2025-02-01T23:38:48.532Z.json passed.
199+
AJV: validation of Coinbase API response snapshot 2025-02-10T21:08:43.831Z.json passed.
200+
```
201+
194202
To run a flow related to an API response, change directory:
195203
196204
```bash
@@ -200,15 +208,29 @@ $ cd response
200208
Run flow for **all responses** of a **specific API**:
201209
202210
```bash
203-
$ node bybit
211+
response$ node bybit --aggr
212+
response$ node bybit --aggregate
204213
Aggregated "2024-01-01T00:00:00.000Z.json" to "../collection/bybit/currency_all".
205214
Aggregated "2024-01-01T00:00:00.000Z.json" to "../collection/bybit/currency_network_all".
206215
```
207216
217+
```bash
218+
response$ node bybit --vali
219+
response$ node bybit --validate
220+
AJV: validation of Bybit API response snapshot 2025-02-01T23:38:48.058Z.json passed.
221+
AJV: validation of Bybit API response snapshot 2025-02-01T23:38:48.532Z.json passed.
222+
```
223+
208224
Run **single response** aggregation (option `--aggr[egate]` is required if not enabled in settings):
209225
210226
```bash
211-
$ node bybit currencyAll --aggr
212-
$ node bybit currencyAll --aggregate
227+
response$ node bybit currencyAll --aggr
228+
response$ node bybit currencyAll --aggregate
213229
Aggregated "2024-01-01T00:00:00.000Z.json" to "../collection/bybit/currency_all".
214230
```
231+
232+
```bash
233+
response$ node bybit currencyAll --vali
234+
response$ node bybit currencyAll --validate
235+
AJV: validation of Bybit API response snapshot 2025-02-01T23:38:48.058Z.json passed.
236+
```

library/fetch.mjs

+12
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/**!
22
* Fetch data from a REST API endpoint with parameters.
33
*
4+
* @typedef {import("#types/response/snapshot.d.js").default} Snapshot
45
* @typedef {import("./constants.mjs").HttpStatusCode} HttpStatusCode
56
* @typedef {import("./constants.mjs").HttpStatusText} HttpStatusText
67
* @typedef {Object<string, string>} Dict
@@ -157,3 +158,14 @@ export const stringifyQuery = (template, data) => {
157158

158159
return query;
159160
};
161+
162+
/**
163+
* Obtain JSON data from successful response snapshot.
164+
* @param {Snapshot} snapshot All data from response snapshot.
165+
* @returns {{}} JSON data from response snapshot.
166+
*/
167+
export const successfulJson = (snapshot) => {
168+
const json = snapshot.CREATED?.json ?? snapshot.OK?.json;
169+
170+
return json;
171+
};

library/string.mjs

+5
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,11 @@ export const supplementary = (template, expressions) => {
5656
return result;
5757
};
5858

59+
/**
60+
* Format a string to pascal case.
61+
* @param {string} string Source string in any casing.
62+
* @returns {string} String formatted in pascal case.
63+
*/
5964
export const toPascalCase = (string) => {
6065
const result = string.replace(
6166
/\w+/g,

library/utility.mjs

+9-1
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
* aggregate?: boolean;
77
* authentication?: boolean;
88
* debug?: boolean;
9-
* exit?: string;
9+
* exit?: boolean;
1010
* flow?: string;
1111
* headers?: boolean;
1212
* snapshot?: boolean;
13+
* throw?: boolean;
14+
* validate?: boolean;
1315
* verbose?: boolean;
1416
* }} Options
1517
* @module library/utility
@@ -23,6 +25,8 @@ const OPTIONS = {
2325
flow: 'flow',
2426
head: 'headers',
2527
snap: 'snapshot',
28+
thro: 'throw',
29+
vali: 'validate',
2630
verb: 'verbose',
2731
};
2832

@@ -37,6 +41,7 @@ export const obtainName = (value, dictionary) => {
3741

3842
return name;
3943
};
44+
export const obtain = obtainName;
4045

4146
/**
4247
* Parse process argument vectors from CLI to usable handler, parameters and options.
@@ -87,9 +92,12 @@ export const parseOptions = (argv) => {
8792
option.includes('--aggr') ||
8893
option.includes('--auth') ||
8994
option.includes('--debu') ||
95+
option.includes('--exit') ||
9096
option.includes('--flow') ||
9197
option.includes('--head') ||
9298
option.includes('--snap') ||
99+
option.includes('--thro') ||
100+
option.includes('--vali') ||
93101
option.includes('--verb'),
94102
),
95103
optionsExplicit = options

package-lock.json

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "api-tools",
3-
"version": "0.3.1-alpha",
3+
"version": "0.4.0-alpha",
44
"description": "Tools to work with a REST API and WebSockets.",
55
"main": "request.js",
66
"directories": {},

preferences/bybit.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"quote": "USDT"
77
},
88
"debug": false,
9-
"enabled": ["aggregate", "parse", "snapshot", "verbose"],
9+
"enabled": ["aggregate", "parse", "snapshot", "verbose", "validate"],
1010
"exit": false,
1111
"parse": [
1212
"BALANCE_ALL",
@@ -54,5 +54,6 @@
5454
"WITHDRAW_NEW",
5555
"WITHDRAW_ONE"
5656
],
57+
"validate": ["CURRENCY_ALL", "CURRENCY_NETWORK_ALL"],
5758
"verbose": ["ORDER_PLACE", "WITHDRAW_NEW"]
5859
}

preferences/coinbase.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"quote": "USDC"
77
},
88
"debug": false,
9-
"enabled": ["aggregate", "parse", "snapshot", "verbose"],
9+
"enabled": ["aggregate", "parse", "snapshot", "verbose", "validate"],
1010
"exit": false,
1111
"parse": ["CURRENCY_ALL", "CURRENCY_ONE", "WITHDRAW_ALL"],
1212
"snapshot": [
@@ -37,5 +37,6 @@
3737
"WITHDRAW_ALL",
3838
"WITHDRAW_NEW"
3939
],
40+
"validate": ["ADDRESS_ALL", "ADDRESS_NEW", "ADDRESS_ONE", "ADDRESS_TRANSACTIONS", "CURRENCY_ALL"],
4041
"verbose": ["ORDER_PLACE", "WITHDRAW_NEW"]
4142
}

request/index.mjs

+3-4
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,8 @@
1616

1717
import { fetchData } from '#lib/fetch.mjs';
1818
import { obtainName } from '#lib/utility.mjs';
19-
20-
import analyze from '../response/analyze.mjs';
21-
import validate from '../response/validate.mjs';
19+
import analyze from '#res/analyze.mjs';
20+
import validate from '#res/validate.mjs';
2221

2322
/**
2423
* Analyze, validate, parse and snapshot response data.
@@ -40,7 +39,7 @@ const request = async (method, api, url, template, headers, schema, callback, da
4039
{ json } = response,
4140
{ jsonParsed, statusText } = responseParsed,
4241
{ isKnown, isSaved, isSuccessful } = analyze(api, responseParsed),
43-
isValid = validate(response.json, schema);
42+
isValid = validate(response.json, schema, api.name);
4443

4544
global.apiTools.output[statusText] = {
4645
headers: response.headers,

response/aggregate.mjs

+9-7
Original file line numberDiff line numberDiff line change
@@ -20,22 +20,22 @@ import nodePath from 'node:path';
2020
* @param {string} endpoint Endpoint name.
2121
* @param {{}[]} data An array from JSON response to map and sort.
2222
* @param {Callback} callback Callbacks for mapping and sorting arrays.
23+
* @param {string} fileName Name of snapshot response file.
2324
* @returns {RSnapshot} Aggregated file data to write in the collection directory.
2425
*/
25-
const aggregateJson = (name, endpoint, data, callback) => {
26+
const aggregateJson = (name, endpoint, data, callback, fileName) => {
2627
const dirName = import.meta.dirname,
27-
fileName = new Date().toISOString() + '.json',
2828
path = `../collection/${name}/${endpoint.toLowerCase()}`,
2929
filePath = nodePath.join(dirName, path),
3030
filePathFull = nodePath.join(filePath, fileName);
31+
3132
let array, fileData;
3233

3334
if (endpoint === 'CURRENCY_ALL') {
3435
const currencies = data.map(callback.currencies);
3536

3637
array = currencies.sort(callback.sort);
37-
}
38-
if (endpoint === 'CURRENCY_NETWORK_ALL') {
38+
} else if (endpoint === 'CURRENCY_NETWORK_ALL') {
3939
const networks = data.map(callback.networks),
4040
arrayDupes = networks.reduce((accum, chain) => accum.concat(chain), []),
4141
arrayUnique = arrayDupes.filter(
@@ -44,6 +44,7 @@ const aggregateJson = (name, endpoint, data, callback) => {
4444

4545
array = arrayUnique.sort((a, b) => a.chain.localeCompare(b.chain));
4646
}
47+
4748
fileData = JSON.stringify(array, null, 2);
4849
nodeFs.mkdirSync(filePath, { recursive: true });
4950
nodeFs.writeFileSync(filePathFull, fileData);
@@ -58,9 +59,10 @@ const aggregateJson = (name, endpoint, data, callback) => {
5859
* @param {string} endpoint Endpoint name.
5960
* @param {{}[]} data An array from JSON response to map.
6061
* @param {Callback} callback Callbacks for mapping and sorting arrays.
62+
* @param {string} fileName Name of snapshot response file.
6163
* @returns {RSnapshot} Aggregated file data to write in the collection directory.
6264
*/
63-
const responseAggregate = (api, endpoint, data, callback) => {
65+
const responseAggregate = (api, endpoint, data, callback, fileName) => {
6466
const { options } = global.apiTools,
6567
{ prefs } = api,
6668
isEnabled = prefs.enabled.includes('aggregate'),
@@ -69,14 +71,14 @@ const responseAggregate = (api, endpoint, data, callback) => {
6971

7072
if (typeof aggregate === 'boolean') {
7173
if (aggregate) {
72-
const { fileData, fileName } = aggregateJson(api.name, endpoint, data, callback);
74+
const { fileData } = aggregateJson(api.name, endpoint, data, callback, fileName);
7375

7476
return { fileData, fileName };
7577
}
7678
} else {
7779
if (isEnabled) {
7880
if (isAggregate) {
79-
const { fileData, fileName } = aggregateJson(api.name, endpoint, data, callback);
81+
const { fileData } = aggregateJson(api.name, endpoint, data, callback, fileName);
8082

8183
return { fileData, fileName };
8284
} else console.info(`Aggregate: endpoint "${endpoint}" is not enabled is settings.`);

response/bybit/aggregate.mjs

+27-19
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@
55
* @module response/bybit/aggregate
66
*/
77

8+
import { successfulJson } from '#lib/fetch.mjs';
89
import { fileReadJson } from '#lib/file_system.mjs';
9-
import responseAggregate from '../aggregate.mjs';
10+
11+
import aggregate from '../aggregate.mjs';
1012

1113
/**
1214
* Aggregate a Bybit API response snapshot.
@@ -17,24 +19,30 @@ import responseAggregate from '../aggregate.mjs';
1719
const bybitAggregate = (directory, fileName) => {
1820
const { bybit } = global.apiTools,
1921
endpoint = directory.toUpperCase(),
20-
json = fileReadJson('response/bybit/snapshot/' + directory, fileName),
21-
data = json.OK.json.result.rows,
22-
file = responseAggregate(bybit, endpoint, data, {
23-
currencies: (item) => ({
24-
chains: item.chains.map((item) => ({
25-
chain: item.chain,
26-
chainType: item.chainType,
27-
})),
28-
coin: item.coin,
29-
name: item.name,
30-
}),
31-
networks: (row) =>
32-
row.chains.map((item) => ({
33-
chain: item.chain,
34-
chainType: item.chainType,
35-
})),
36-
sort: (item1, item2) => item1['coin'].localeCompare(item2['coin']),
37-
});
22+
fileData = fileReadJson('response/bybit/snapshot/' + directory, fileName),
23+
json = /** @type {{ result: { rows: object }}} */ (successfulJson(fileData)).result.rows,
24+
file = aggregate(
25+
bybit,
26+
endpoint,
27+
json,
28+
{
29+
currencies: (item) => ({
30+
chains: item.chains.map((item) => ({
31+
chain: item.chain,
32+
chainType: item.chainType,
33+
})),
34+
coin: item.coin,
35+
name: item.name,
36+
}),
37+
networks: (row) =>
38+
row.chains.map((item) => ({
39+
chain: item.chain,
40+
chainType: item.chainType,
41+
})),
42+
sort: (item1, item2) => item1['coin'].localeCompare(item2['coin']),
43+
},
44+
fileName,
45+
);
3846

3947
return file;
4048
};

response/bybit/currency/all.mjs

+12-10
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,31 @@
11
/**
2-
* Handle Bybit API currency all response aggregation.
2+
* Handle Bybit API currency all response operations.
33
*
44
* @see https://bybit-exchange.github.io/docs/v5/asset/coin-info
55
* @typedef {import("#res/snapshot.mjs").RSnapshot} RSnapshot
66
* @module response/bybit/currency/all
77
*/
88

9-
import { fileNewest } from '#lib/file_system.mjs';
10-
import { obtainName } from '#lib/utility.mjs';
11-
import aggregate from '../aggregate.mjs';
9+
import { obtain } from '#lib/utility.mjs';
10+
import { currencyAll as schema } from '#res/bybit/currency/schema.mjs';
11+
12+
import operate from '../operate.mjs';
1213

1314
/**
14-
* @returns {RSnapshot} Aggregated file data written in the collection directory.
15+
* Perform an operation on a specific response snapshot file data or latest created.
16+
* @param {string} [snapshot] Response snapshot file name without `.json` extension.
17+
* @returns {RSnapshot} File data has been operated.
1518
*/
16-
const currencyAll = () => {
19+
const currencyAll = (snapshot) => {
1720
const { config } = global.apiTools.bybit,
1821
{
1922
PATH,
2023
PATH: { CURRENCY_ALL },
2124
} = config,
22-
directory = obtainName(CURRENCY_ALL, PATH).toLowerCase(),
23-
file = fileNewest('response/bybit/snapshot/' + directory),
24-
fileAggregated = aggregate(directory, file.name);
25+
endpoint = obtain(CURRENCY_ALL, PATH),
26+
data = operate(endpoint, snapshot, schema);
2527

26-
return fileAggregated;
28+
return data;
2729
};
2830

2931
export default currencyAll;

0 commit comments

Comments
 (0)