api 4.3.0 → 4.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -4
- package/package.json +4 -3
- package/src/cache.js +35 -19
- package/src/index.js +8 -7
- package/src/lib/prepareAuth.js +35 -38
package/README.md
CHANGED
|
@@ -23,9 +23,9 @@ npm install api --save
|
|
|
23
23
|
Using `api` is as simple as supplying it an OpenAPI and using the SDK as you would any other!
|
|
24
24
|
|
|
25
25
|
```js
|
|
26
|
-
const sdk = require('api')('https://raw.githubusercontent.com/readmeio/oas/
|
|
26
|
+
const sdk = require('api')('https://raw.githubusercontent.com/readmeio/oas-examples/main/3.0/json/petstore.json');
|
|
27
27
|
|
|
28
|
-
sdk.listPets().then(res =>
|
|
28
|
+
sdk.listPets().then(res => {
|
|
29
29
|
console.log(`My pets name is ${res[0].name}!`);
|
|
30
30
|
});
|
|
31
31
|
```
|
|
@@ -33,10 +33,11 @@ sdk.listPets().then(res => res.json()).then(res => {
|
|
|
33
33
|
The OpenAPI definition is automatically downloaded, cached, and transformed into a chainable [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) Promise that you can use to make API requests.
|
|
34
34
|
|
|
35
35
|
### Authentication
|
|
36
|
-
`api` supports API authentication through an `.auth()` method
|
|
36
|
+
`api` supports API authentication through an `.auth()` method:
|
|
37
37
|
|
|
38
38
|
```js
|
|
39
|
-
sdk.auth('myApiToken')
|
|
39
|
+
sdk.auth('myApiToken');
|
|
40
|
+
sdk.listPets().then(...);
|
|
40
41
|
```
|
|
41
42
|
|
|
42
43
|
With the exception of OpenID, it supports all forms of authentication supported by the OpenAPI specification! Just give `.auth()` your credentials and it'll figure out how to use it according to the API you're using.
|
|
@@ -47,6 +48,8 @@ For example:
|
|
|
47
48
|
* Bearer tokens (HTTP or OAuth 2): `sdk.auth('myBearerToken')`
|
|
48
49
|
* API Keys: `sdk.auth('myApiKey')`
|
|
49
50
|
|
|
51
|
+
> ℹ️ Note that `sdk.auth()` is not chainable.
|
|
52
|
+
|
|
50
53
|
### Parameters and Payloads
|
|
51
54
|
When supplying parameters and/or request body payloads to an API request, you don't need to explicitly define what goes where since the API definition contains all that information. All you need to do is supply either one or two objects:
|
|
52
55
|
|
|
@@ -161,3 +164,17 @@ By default we parse the response based on the `content-type` header for you. You
|
|
|
161
164
|
```js
|
|
162
165
|
sdk.config({ parseResponse: false });
|
|
163
166
|
```
|
|
167
|
+
|
|
168
|
+
#### Where is the cache stored?
|
|
169
|
+
|
|
170
|
+
By default the cache is configured with the [find-cache-dir](https://npm.im/find-cache-dir) library so the cache will be in `node_modules/.cache/api`. If placing this cache within the `node_modules/` directory is a problem for your environment (maybe you use `npm prune`) you can configure this by supplying an additional argument to the SDK instantiator:
|
|
171
|
+
|
|
172
|
+
```js
|
|
173
|
+
const sdk = require('api')('https://raw.githubusercontent.com/readmeio/oas-examples/main/3.0/json/petstore.json', {
|
|
174
|
+
cacheDir: './path/to/my/custom/cache/dir',
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
sdk.listPets().then(res => {
|
|
178
|
+
console.log(`My pets name is ${res[0].name}!`);
|
|
179
|
+
});
|
|
180
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "api",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.1",
|
|
4
4
|
"description": "Generate an SDK from an OpenAPI definition",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -48,8 +48,9 @@
|
|
|
48
48
|
"prettier": "@readme/eslint-config/prettier",
|
|
49
49
|
"jest": {
|
|
50
50
|
"testPathIgnorePatterns": [
|
|
51
|
-
"__tests__/__fixtures__/"
|
|
51
|
+
"__tests__/__fixtures__/",
|
|
52
|
+
".api-test/"
|
|
52
53
|
]
|
|
53
54
|
},
|
|
54
|
-
"gitHead": "
|
|
55
|
+
"gitHead": "5c3490291498c1ac7f2f9dfe2ad99afff9010689"
|
|
55
56
|
}
|
package/src/cache.js
CHANGED
|
@@ -9,18 +9,8 @@ const os = require('os');
|
|
|
9
9
|
const path = require('path');
|
|
10
10
|
const makeDir = require('make-dir');
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
// The `find-cache-dir` module returns `undefined` if the `node_modules/` directory isn't writable, or there's no
|
|
15
|
-
// `package.json` in the root-most directory. If this happens, we can instead adhoc create a cache directory in the
|
|
16
|
-
// users OS temp directory and store our data there.
|
|
17
|
-
//
|
|
18
|
-
// @link https://github.com/avajs/find-cache-dir/issues/29
|
|
19
|
-
cacheDir = makeDir.sync(path.join(os.tmpdir(), pkg.name));
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
class SdkCache {
|
|
23
|
-
constructor(uri) {
|
|
12
|
+
class Cache {
|
|
13
|
+
constructor(uri, cacheDir = false) {
|
|
24
14
|
/**
|
|
25
15
|
* Resolve OpenAPI definition shorthand accessors from within the ReadMe API Registry.
|
|
26
16
|
*
|
|
@@ -37,8 +27,23 @@ class SdkCache {
|
|
|
37
27
|
: u;
|
|
38
28
|
|
|
39
29
|
this.uri = resolveReadMeRegistryAccessor(uri);
|
|
40
|
-
this.uriHash =
|
|
41
|
-
|
|
30
|
+
this.uriHash = Cache.getCacheHash(this.uri);
|
|
31
|
+
|
|
32
|
+
if (cacheDir) {
|
|
33
|
+
this.dir = cacheDir;
|
|
34
|
+
} else {
|
|
35
|
+
this.dir = findCacheDir({ name: pkg.name });
|
|
36
|
+
if (typeof this.dir === 'undefined') {
|
|
37
|
+
// The `find-cache-dir` module returns `undefined` if the `node_modules/` directory isn't
|
|
38
|
+
// writable, or there's no `package.json` in the root-most directory. If this happens, we
|
|
39
|
+
// can instead adhoc create a cache directory in the users OS temp directory and store our
|
|
40
|
+
// data there.
|
|
41
|
+
//
|
|
42
|
+
// @link https://github.com/avajs/find-cache-dir/issues/29
|
|
43
|
+
this.dir = makeDir.sync(path.join(os.tmpdir(), pkg.name));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
42
47
|
this.cacheStore = path.join(this.dir, 'cache.json');
|
|
43
48
|
this.specsCache = path.join(this.dir, 'specs');
|
|
44
49
|
|
|
@@ -87,7 +92,18 @@ class SdkCache {
|
|
|
87
92
|
}
|
|
88
93
|
|
|
89
94
|
const cache = this.getCache();
|
|
90
|
-
|
|
95
|
+
|
|
96
|
+
// Prior to v4.5.0 we were putting a fully resolved path to the API definition in the cache
|
|
97
|
+
// store but if you had specified a custom caching directory and would generate the cache on
|
|
98
|
+
// your system, that filepath would obviously not be the same in other environments. For this
|
|
99
|
+
// reason the `path` was removed from the cache store in favor of storing the `hash` instead.
|
|
100
|
+
//
|
|
101
|
+
// If we still have `path` in the config cache for backwards compatibility we should use it.
|
|
102
|
+
if ('path' in cache[this.uriHash]) {
|
|
103
|
+
return JSON.parse(fs.readFileSync(cache[this.uriHash].path, 'utf8'));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return JSON.parse(fs.readFileSync(path.join(this.specsCache, `${cache[this.uriHash].hash}.json`)));
|
|
91
107
|
}
|
|
92
108
|
|
|
93
109
|
async load() {
|
|
@@ -154,16 +170,16 @@ class SdkCache {
|
|
|
154
170
|
const cache = self.getCache();
|
|
155
171
|
if (!(self.uriHash in cache)) {
|
|
156
172
|
const saved = JSON.stringify(spec, null, 2);
|
|
157
|
-
const
|
|
173
|
+
const fileHash = crypto.createHash('md5').update(saved).digest('hex');
|
|
158
174
|
|
|
159
175
|
cache[self.uriHash] = {
|
|
160
|
-
|
|
176
|
+
hash: fileHash,
|
|
161
177
|
original: self.uri,
|
|
162
178
|
title: 'title' in spec.info ? spec.info.title : undefined,
|
|
163
179
|
version: 'version' in spec.info ? spec.info.version : undefined,
|
|
164
180
|
};
|
|
165
181
|
|
|
166
|
-
fs.writeFileSync(
|
|
182
|
+
fs.writeFileSync(path.join(self.specsCache, `${fileHash}.json`), saved);
|
|
167
183
|
fs.writeFileSync(self.cacheStore, JSON.stringify(cache, null, 2));
|
|
168
184
|
|
|
169
185
|
self.cache = cache;
|
|
@@ -206,4 +222,4 @@ class SdkCache {
|
|
|
206
222
|
}
|
|
207
223
|
}
|
|
208
224
|
|
|
209
|
-
module.exports =
|
|
225
|
+
module.exports = Cache;
|
package/src/index.js
CHANGED
|
@@ -13,9 +13,11 @@ global.Headers = fetch.Headers;
|
|
|
13
13
|
global.FormData = require('form-data');
|
|
14
14
|
|
|
15
15
|
class Sdk {
|
|
16
|
-
constructor(uri) {
|
|
16
|
+
constructor(uri, opts = {}) {
|
|
17
17
|
this.uri = uri;
|
|
18
18
|
this.userAgent = `${pkg.name} (node)/${pkg.version}`;
|
|
19
|
+
|
|
20
|
+
this.cacheDir = opts.cacheDir ? opts.cacheDir : false;
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
static getOperations(spec) {
|
|
@@ -29,8 +31,8 @@ class Sdk {
|
|
|
29
31
|
}
|
|
30
32
|
|
|
31
33
|
load() {
|
|
32
|
-
|
|
33
|
-
const cache = new Cache(this.uri);
|
|
34
|
+
let authKeys = [];
|
|
35
|
+
const cache = new Cache(this.uri, this.cacheDir);
|
|
34
36
|
const self = this;
|
|
35
37
|
let config = { parseResponse: true };
|
|
36
38
|
let server = false;
|
|
@@ -150,8 +152,7 @@ class Sdk {
|
|
|
150
152
|
|
|
151
153
|
sdk = {
|
|
152
154
|
auth: (...values) => {
|
|
153
|
-
authKeys
|
|
154
|
-
return new Proxy(sdk, sdkProxy);
|
|
155
|
+
authKeys = values;
|
|
155
156
|
},
|
|
156
157
|
config: opts => {
|
|
157
158
|
// Downside to having `opts` be merged into the existing `config` is that there isn't a clean way to reset your
|
|
@@ -171,6 +172,6 @@ class Sdk {
|
|
|
171
172
|
}
|
|
172
173
|
}
|
|
173
174
|
|
|
174
|
-
module.exports = uri => {
|
|
175
|
-
return new Sdk(uri).load();
|
|
175
|
+
module.exports = (uri, opts = {}) => {
|
|
176
|
+
return new Sdk(uri, opts).load();
|
|
176
177
|
};
|
package/src/lib/prepareAuth.js
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
* @param {Array} authKeys
|
|
8
8
|
* @param {Operation} operation
|
|
9
9
|
*/
|
|
10
|
-
module.exports = (
|
|
11
|
-
if (
|
|
10
|
+
module.exports = (authKey, operation) => {
|
|
11
|
+
if (authKey.length === 0) {
|
|
12
12
|
return {};
|
|
13
13
|
}
|
|
14
14
|
|
|
@@ -21,31 +21,20 @@ module.exports = (authKeys, operation) => {
|
|
|
21
21
|
return {};
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const scheme = schemes[0];
|
|
33
|
-
if (scheme.type === 'http') {
|
|
34
|
-
if (scheme.scheme === 'basic') {
|
|
35
|
-
prepared[scheme._key] = {
|
|
36
|
-
user: authKey[0],
|
|
37
|
-
pass: authKey.length === 2 ? authKey[1] : '',
|
|
38
|
-
};
|
|
39
|
-
} else if (scheme.scheme === 'bearer') {
|
|
40
|
-
if (authKey.length > 1) {
|
|
41
|
-
throw new Error(
|
|
42
|
-
'Multiple auth tokens were supplied for the auth on this endpoint, but only a single token is needed.'
|
|
43
|
-
);
|
|
44
|
-
}
|
|
24
|
+
const securityType = securitySchemes[0];
|
|
25
|
+
const schemes = security[securityType];
|
|
26
|
+
if (schemes.length > 1) {
|
|
27
|
+
throw new Error("Sorry, this API currently requires multiple forms of authentication which we don't yet support.");
|
|
28
|
+
}
|
|
45
29
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
30
|
+
const scheme = schemes[0];
|
|
31
|
+
if (scheme.type === 'http') {
|
|
32
|
+
if (scheme.scheme === 'basic') {
|
|
33
|
+
prepared[scheme._key] = {
|
|
34
|
+
user: authKey[0],
|
|
35
|
+
pass: authKey.length === 2 ? authKey[1] : '',
|
|
36
|
+
};
|
|
37
|
+
} else if (scheme.scheme === 'bearer') {
|
|
49
38
|
if (authKey.length > 1) {
|
|
50
39
|
throw new Error(
|
|
51
40
|
'Multiple auth tokens were supplied for the auth on this endpoint, but only a single token is needed.'
|
|
@@ -53,20 +42,28 @@ module.exports = (authKeys, operation) => {
|
|
|
53
42
|
}
|
|
54
43
|
|
|
55
44
|
prepared[scheme._key] = authKey[0];
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
45
|
+
}
|
|
46
|
+
} else if (scheme.type === 'oauth2') {
|
|
47
|
+
if (authKey.length > 1) {
|
|
48
|
+
throw new Error(
|
|
49
|
+
'Multiple auth tokens were supplied for the auth on this endpoint, but only a single token is needed.'
|
|
50
|
+
);
|
|
51
|
+
}
|
|
62
52
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
53
|
+
prepared[scheme._key] = authKey[0];
|
|
54
|
+
} else if (scheme.type === 'apiKey') {
|
|
55
|
+
if (authKey.length > 1) {
|
|
56
|
+
throw new Error(
|
|
57
|
+
'Multiple auth keys were supplied for the auth on this endpoint, but only a single key is needed.'
|
|
58
|
+
);
|
|
68
59
|
}
|
|
69
|
-
|
|
60
|
+
|
|
61
|
+
if (scheme.in === 'query' || scheme.in === 'header') {
|
|
62
|
+
prepared[scheme._key] = authKey[0];
|
|
63
|
+
}
|
|
64
|
+
} else {
|
|
65
|
+
throw new Error(`Sorry, this API currently supports a scheme, ${scheme.type}, that we don't yet support.`);
|
|
66
|
+
}
|
|
70
67
|
|
|
71
68
|
return prepared;
|
|
72
69
|
};
|