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 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/master/packages/examples/3.0/json/petstore.json');
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 => res.json()).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 that you can chain to your requests, as such:
36
+ `api` supports API authentication through an `.auth()` method:
37
37
 
38
38
  ```js
39
- sdk.auth('myApiToken').listPets().then(...);
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.2.1",
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": "^14.0.5",
27
- "@readme/openapi-parser": "^2.0.0",
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": "^17.4.1"
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": "0cdb386e34d37de51ce8909f5dc9c4cd338ef4de"
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
- let cacheDir = findCacheDir({ name: pkg.name });
13
- if (typeof cacheDir === 'undefined') {
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 = SdkCache.getCacheHash(this.uri);
41
- this.dir = cacheDir;
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 = SdkCache;
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
- const authKeys = [];
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.push(values);
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
+ };
@@ -7,8 +7,8 @@
7
7
  * @param {Array} authKeys
8
8
  * @param {Operation} operation
9
9
  */
10
- module.exports = (authKeys, operation) => {
11
- if (authKeys.length === 0) {
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
- authKeys.forEach((authKey, idx) => {
25
- const schemes = security[securitySchemes[idx]];
26
- if (schemes.length > 1) {
27
- throw new Error(
28
- `Sorry, this API currently requires multiple forms of authentication which we don't yet support.`
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
- prepared[scheme._key] = authKey[0];
47
- }
48
- } else if (scheme.type === 'oauth2') {
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
- } else if (scheme.type === 'apiKey') {
57
- if (authKey.length > 1) {
58
- throw new Error(
59
- 'Multiple auth keys were supplied for the auth on this endpoint, but only a single key is needed.'
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
- if (scheme.in === 'query' || scheme.in === 'header') {
64
- prepared[scheme._key] = authKey[0];
65
- }
66
- } else {
67
- throw new Error(`Sorry, this API currently supports a scheme, ${scheme.type}, that we don't yet support.`);
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
  };
@@ -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) => {