Skip to content

Commit a2b4002

Browse files
authored
init 2.0.0 - migrate from request to axios (#35)
1 parent d0662a6 commit a2b4002

23 files changed

+1521
-1830
lines changed

.circleci/config.yml

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,12 @@ jobs: # a collection of steps
33
build: # runs not using Workflows must have a `build` job as entry point
44
working_directory: ~/elasticio-rest-node # directory where steps will run
55
docker: # run the steps with Docker
6-
- image: circleci/node:12 # ...with this image as the primary container; this is where all `steps` will run
6+
- image: cimg/node:14.21.3 # ...with this image as the primary container; this is where all `steps` will run
77
steps: # a collection of executable commands
88
- checkout # special step to check out source code to working directory
9-
- run:
10-
name: update-npm
11-
command: 'sudo npm install -g npm@latest'
12-
- restore_cache: # special step to restore the dependency cache
13-
# Read about caching dependencies: https://circleci.com/docs/2.0/caching/
14-
key: dependency-cache-{{ checksum "package-lock.json" }}
159
- run:
1610
name: install-npm-wee
1711
command: npm install
18-
- save_cache: # special step to save the dependency cache
19-
key: dependency-cache-{{ checksum "package-lock.json" }}
20-
paths:
21-
- ./node_modules
2212
- run:
2313
name: "Running audit"
2414
command: npm audit --production --audit-level=high

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
## 2.0.0 (July 31, 2025)
2+
* Migrated from deprecated `[email protected]` library to `[email protected]`
3+
* Removed `q` and `lodash` libraries
4+
* Implemented custom retry logic

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Documentation is available at http://api.elastic.io/docs/
1414

1515

1616
````js
17-
var client = require('elasticio-rest-node')('YOUR_EMAIL', 'YOUR_API_KEY');
17+
const client = require('elasticio-rest-node')('YOUR_EMAIL', 'YOUR_API_KEY');
1818
````
1919

2020

lib/client.js

Lines changed: 39 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,58 +1,53 @@
1-
const _ = require('lodash');
2-
const { STRATEGIES } = require('./retryStrategies');
3-
41
module.exports = ElasticIO;
2+
const { axiosReqWithRetryOnServerError } = require('./utils');
3+
module.exports.axiosReqWithRetryOnServerError = axiosReqWithRetryOnServerError;
4+
55

66
ElasticIO.API_DEFAULT_BASE_URL = 'https://api.elastic.io';
7-
ElasticIO.RETRY_STRATEGIES = STRATEGIES;
8-
9-
var resources = {
10-
accounts: require('./resources/accounts'),
11-
components: require('./resources/components'),
12-
exec: require('./resources/exec'),
13-
recipes: require('./resources/recipes'),
14-
resources: {
15-
s3: require('./resources/s3'),
16-
storage: require('./resources/storage')
17-
},
18-
repos: require('./resources/repos'),
19-
sshkeys: require('./resources/sshkeys'),
20-
tasks: require('./resources/tasks'),
21-
teams: require('./resources/teams'),
22-
users: require('./resources/users'),
23-
secrets: require('./resources/secrets'),
24-
topics: require('./resources/topics')
7+
8+
const resources = {
9+
accounts: require('./resources/accounts'),
10+
components: require('./resources/components'),
11+
exec: require('./resources/exec'),
12+
recipes: require('./resources/recipes'),
13+
resources: {
14+
s3: require('./resources/s3'),
15+
storage: require('./resources/storage')
16+
},
17+
repos: require('./resources/repos'),
18+
sshkeys: require('./resources/sshkeys'),
19+
tasks: require('./resources/tasks'),
20+
teams: require('./resources/teams'),
21+
users: require('./resources/users'),
22+
secrets: require('./resources/secrets'),
23+
topics: require('./resources/topics')
2524
};
2625

2726
function ElasticIO(user, password, options = {}) {
28-
var self = this;
29-
var baseUri = process.env.ELASTICIO_API_URI || ElasticIO.API_DEFAULT_BASE_URL;
30-
baseUri = baseUri.replace(/\/+$/, '');
31-
this._api = {
32-
user: user || process.env.ELASTICIO_API_USERNAME,
33-
password: password || process.env.ELASTICIO_API_KEY,
34-
basePath: baseUri
35-
};
27+
const baseUri = (process.env.ELASTICIO_API_URI || ElasticIO.API_DEFAULT_BASE_URL).replace(/\/+$/, '');
28+
this._api = {
29+
user: user || process.env.ELASTICIO_API_USERNAME,
30+
password: password || process.env.ELASTICIO_API_KEY,
31+
basePath: baseUri
32+
};
3633

34+
this.options = { ...options };
3735

38-
prepareResources(self, resources, options);
36+
prepareResources(this, resources, this.options);
3937

40-
return this;
38+
return this;
4139
}
4240

4341
function prepareResources(container, resources, options = {}) {
44-
var self = this;
45-
46-
for (var next in resources) {
47-
var name = next.toLowerCase();
48-
var value = resources[next];
49-
50-
if (typeof value === 'object') {
51-
container[name] = {};
52-
prepareResources(container[name], value);
53-
} else {
54-
self.options = _.defaults(self.options || {}, options);
55-
container[name] = new resources[next](self);
56-
}
42+
for (const key of Object.keys(resources)) {
43+
const name = key.toLowerCase();
44+
const value = resources[key];
45+
46+
if (typeof value === 'object' && !('prototype' in value)) {
47+
container[name] = {};
48+
prepareResources(container[name], value, options);
49+
} else {
50+
container[name] = new value(this);
5751
}
52+
}
5853
}

lib/resource.js

Lines changed: 92 additions & 136 deletions
Original file line numberDiff line numberDiff line change
@@ -1,152 +1,108 @@
1-
const util = require('util');
2-
const httpRequest = require('requestretry');
1+
const { axiosReqWithRetryOnServerError } = require('./utils');
32
const API_VERSION = 'v1';
4-
const _ = require('lodash');
5-
const {
6-
MAP: RETRY_STRATEGIES_MAP,
7-
STRATEGIES: { ON_HTTP_OR_NETWORK_ERROR }
8-
} = require('./retryStrategies');
93

10-
module.exports = ApiResource;
11-
12-
13-
function ApiResource(client) {
4+
class ApiResource {
5+
constructor(client) {
146
this.client = client;
15-
}
16-
17-
var hasOwn = {}.hasOwnProperty;
18-
19-
ApiResource.extend = function extend(sub) {
20-
var self = this;
21-
22-
function Constructor() {
23-
self.apply(this, arguments);
24-
}
25-
26-
Constructor.prototype = Object.create(self.prototype);
27-
28-
for (var i in sub) {
29-
if (hasOwn.call(sub, i)) {
30-
Constructor.prototype[i] = sub[i];
31-
}
32-
}
33-
for (i in self) {
34-
if (hasOwn.call(self, i)) {
35-
Constructor[i] = self[i];
36-
}
37-
}
7+
}
388

39-
return Constructor;
40-
};
9+
static extend(sub) {
10+
class SubResource extends this { }
11+
Object.assign(SubResource.prototype, sub);
12+
Object.assign(SubResource, this);
13+
return SubResource;
14+
}
4115

42-
ApiResource.method = function method(spec) {
16+
static method(spec) {
4317
const verb = spec.method || ApiResource.GET;
44-
async function sendRequest(...args) {
45-
const defaultOptions = this.client.options || {};
46-
var self = this;
47-
var api = self.client._api;
48-
var path = self.path || '';
49-
50-
function provideOptions() {
51-
if (!path) {
52-
throw new Error("A resource must define 'path'");
53-
}
54-
55-
if (spec.path) {
56-
path = path + spec.path;
57-
path = (spec.apiVersion || API_VERSION) + '/' + path;
58-
}
59-
path = interpolatePath(path, args);
60-
const [requestBody, options] = args;
61-
62-
return createRequestOptions(verb, api, path, requestBody, _.defaults(options || {}, defaultOptions));
63-
}
64-
65-
66-
const response = await httpRequest(provideOptions());
67-
const { body, statusCode } = response;
68-
69-
if (statusCode >= 400) {
70-
var message = typeof body === 'object' ? JSON.stringify(body) : body;
71-
72-
var error = new Error(message);
73-
error.statusCode = statusCode;
74-
75-
throw error;
76-
}
77-
78-
if (spec.prepareResponse) {
79-
return spec.prepareResponse(response, body);
80-
}
81-
82-
return body;
83-
}
84-
85-
function interpolatePath(path, args) {
86-
var parameters = path.match(/{(\w+)}/g);
87-
88-
if (!parameters) {
89-
return path;
90-
}
91-
92-
for (var index in parameters) {
93-
var param = parameters[index];
94-
95-
var value = args.shift();
96-
97-
if (!value) {
98-
throw new Error(util.format(
99-
"Missing value for parameter '%s'. Please provide argument: %s",
100-
param, index));
101-
}
102-
103-
path = path.replace(param, value);
18+
return async function (...args) {
19+
const client = this.client;
20+
const defaultOptions = client.options || {};
21+
const api = client._api;
22+
let path = this.path || '';
23+
24+
if (!path) throw new Error("A resource must define 'path'");
25+
26+
if (spec.path) {
27+
path += spec.path;
28+
path = `${spec.apiVersion || API_VERSION}/${path}`;
29+
}
30+
path = interpolatePath(path, args);
31+
32+
const [requestBody, options] = args;
33+
const mergedOptions = { ...defaultOptions, ...(options || {}) };
34+
35+
const requestOptions = createAxiosRequestOptions(verb, api, path, requestBody, mergedOptions);
36+
37+
let response
38+
try {
39+
response = await axiosReqWithRetryOnServerError(requestOptions);
40+
} catch (error) {
41+
if (error.response) {
42+
const { data, status } = error.response;
43+
const message = typeof data === 'object' ? JSON.stringify(data) : (data || '');
44+
const e = new Error(message);
45+
e.statusCode = status;
46+
throw e;
10447
}
48+
error.req = { path: requestOptions.url };
49+
throw error;
50+
}
51+
52+
const { data, status } = response;
53+
54+
if (status >= 400) {
55+
const message = typeof data === 'object' ? JSON.stringify(data) : (data || '');
56+
const error = new Error(message);
57+
error.statusCode = status;
58+
throw error;
59+
}
60+
61+
if (spec.prepareResponse) {
62+
return spec.prepareResponse(response, data);
63+
}
64+
return data;
65+
};
66+
}
67+
}
10568

106-
return path;
69+
function interpolatePath(path, args) {
70+
const parameters = path.match(/{(\w+)}/g);
71+
if (!parameters) return path;
72+
73+
let newPath = path;
74+
for (const [index, param] of parameters.entries()) {
75+
const value = args.shift();
76+
if (value === undefined) {
77+
throw new Error(
78+
`Missing value for parameter '${param}'. Please provide argument: ${index}`
79+
);
10780
}
81+
newPath = newPath.replace(param, value);
82+
}
83+
return newPath;
84+
}
10885

109-
function createRequestOptions(
110-
verb,
111-
api,
112-
path,
113-
body,
114-
{
115-
retryCount,
116-
retryDelay,
117-
retryStrategy
118-
} = {}
119-
) {
120-
const defaultRetryStrategy = RETRY_STRATEGIES_MAP[ON_HTTP_OR_NETWORK_ERROR];
121-
var options = {
122-
url: util.format("%s/%s", api.basePath, path),
123-
method: verb,
124-
json: true,
125-
forever: true,
126-
headers: {
127-
Connection: 'Keep-Alive'
128-
},
129-
auth: {
130-
username: api.user,
131-
password: api.password
132-
},
133-
maxAttempts: retryCount || 3,
134-
retryDelay: retryDelay || 100,
135-
retryStrategy: retryStrategy ? RETRY_STRATEGIES_MAP[retryStrategy] : defaultRetryStrategy,
136-
fullResponse: true
137-
};
138-
139-
if (body) {
140-
options.body = body;
141-
}
142-
143-
return options;
144-
}
86+
function createAxiosRequestOptions(verb, api, path, body, options = {}) {
87+
return {
88+
url: `${api.basePath}/${path}`,
89+
method: verb,
90+
headers: {
91+
Connection: 'Keep-Alive',
92+
...(options?.headers || {})
93+
},
94+
auth: {
95+
username: api.user,
96+
password: api.password
97+
},
98+
...(body ? { data: body } : {})
99+
};
100+
}
145101

146-
return sendRequest;
147-
};
148102

149103
ApiResource.GET = 'get';
150104
ApiResource.POST = 'post';
151105
ApiResource.PUT = 'put';
152106
ApiResource.DELETE = 'delete';
107+
108+
module.exports = ApiResource;

lib/resources/accounts.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
var Resource = require("../resource.js");
1+
const Resource = require("../resource.js");
22

33
module.exports = Resource.extend({
44
path: 'accounts',

lib/resources/components.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
var Resource = require("../resource.js");
1+
const Resource = require("../resource.js");
22

33
module.exports = Resource.extend({
44
path: 'components',

0 commit comments

Comments
 (0)