api 4.2.1 → 4.5.0
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 +7 -6
- package/src/cache.js +20 -15
- package/src/index.js +8 -7
- package/src/lib/getSchema.js +34 -0
- package/src/lib/prepareAuth.js +35 -38
- package/src/lib/prepareParams.js +1 -3
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.0",
|
|
4
4
|
"description": "Generate an SDK from an OpenAPI definition",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"node": "^12 || ^14 || ^16"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@readme/oas-to-har": "^
|
|
27
|
-
"@readme/openapi-parser": "^2.
|
|
26
|
+
"@readme/oas-to-har": "^16.1.0",
|
|
27
|
+
"@readme/openapi-parser": "^2.1.0",
|
|
28
28
|
"datauri": "^4.1.0",
|
|
29
29
|
"fetch-har": "^5.0.5",
|
|
30
30
|
"find-cache-dir": "^3.3.1",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"make-dir": "^3.1.0",
|
|
35
35
|
"mimer": "^2.0.2",
|
|
36
36
|
"node-fetch": "^2.6.0",
|
|
37
|
-
"oas": "^
|
|
37
|
+
"oas": "^18.1.0"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@readme/eslint-config": "^8.0.2",
|
|
@@ -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": "2cbe023cb5d2c1ff1f8b46a4c90d49bb7af0c5f1"
|
|
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
|
|
|
@@ -206,4 +211,4 @@ class SdkCache {
|
|
|
206
211
|
}
|
|
207
212
|
}
|
|
208
213
|
|
|
209
|
-
module.exports =
|
|
214
|
+
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
|
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const {
|
|
2
|
+
utils: { findSchemaDefinition },
|
|
3
|
+
} = require('oas');
|
|
4
|
+
|
|
5
|
+
// Gets the schema of the first media type defined in the `content` of the path operation
|
|
6
|
+
// or returns the ref if there's no Request Body Object.
|
|
7
|
+
//
|
|
8
|
+
// If the ref looks like a `requestBodies` reference, then do a lookup for the actual schema
|
|
9
|
+
// https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#fixed-fields-8
|
|
10
|
+
module.exports = function getSchema(pathOperation, api) {
|
|
11
|
+
try {
|
|
12
|
+
if (pathOperation.requestBody.content) {
|
|
13
|
+
const type = Object.keys(pathOperation.requestBody.content)[0];
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
type,
|
|
17
|
+
schema: pathOperation.requestBody.content[type],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
if (pathOperation.requestBody && pathOperation.requestBody.$ref.match(/^#\/components\/requestBodies\/.*$/)) {
|
|
22
|
+
return getSchema({
|
|
23
|
+
requestBody: findSchemaDefinition(pathOperation.requestBody.$ref, api),
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
type: 'application/json',
|
|
29
|
+
schema: pathOperation.requestBody,
|
|
30
|
+
};
|
|
31
|
+
} catch (e) {} // eslint-disable-line no-empty
|
|
32
|
+
|
|
33
|
+
return undefined;
|
|
34
|
+
};
|
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
|
};
|
package/src/lib/prepareParams.js
CHANGED
|
@@ -4,9 +4,7 @@ const stream = require('stream');
|
|
|
4
4
|
const mimer = require('mimer');
|
|
5
5
|
const getStream = require('get-stream');
|
|
6
6
|
const datauri = require('datauri');
|
|
7
|
-
const
|
|
8
|
-
utils: { getSchema },
|
|
9
|
-
} = require('oas');
|
|
7
|
+
const getSchema = require('./getSchema');
|
|
10
8
|
|
|
11
9
|
function digestParameters(parameters) {
|
|
12
10
|
return parameters.reduce((prev, param) => {
|