@theshelf/http 0.3.1 → 0.4.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
@@ -1,7 +1,7 @@
1
1
 
2
- # HTTP | The Shelf
2
+ # HTTP core | The Shelf
3
3
 
4
- The HTTP package provides a universal interaction layer with an HTTP client inplementation and adds additional caching.
4
+ This package contains the definition of the http clients.
5
5
 
6
6
  ## Installation
7
7
 
@@ -9,49 +9,22 @@ The HTTP package provides a universal interaction layer with an HTTP client inpl
9
9
  npm install @theshelf/http
10
10
  ```
11
11
 
12
- ## Drivers
13
-
14
- Currently, there is only one driver available:
15
-
16
- * **Fetch** - Node.js fetch implementation.
17
-
18
12
  ## How to use
19
13
 
20
14
  The basic set up looks like this.
21
15
 
22
16
  ```ts
23
- import Http, { FetchDriver as SelectedDriver } from '@theshelf/http';
17
+ import Http, { FetchDriver } from '@theshelf/http';
24
18
 
25
- const driver = new SelectedDriver(/* configuration */);
19
+ const driver = new FetchDriver();
26
20
  const http = new Http(driver);
27
21
 
28
22
  // Perform operations with the http instance
29
23
  ```
30
24
 
31
- ### Configuration
32
-
33
- #### Fetch driver
34
-
35
- No configuration options.
36
-
37
- ### Operations
25
+ ## Operations
38
26
 
39
27
  ```ts
40
- import { HTTP_METHODS } from '@theshelf/http';
41
-
42
- // Set a cached response
43
- const response: Response = new Response();
44
- http.setCache(HTTP_METHODS.GET, url, response);
45
-
46
- // Get a cached response
47
- const response: Response | undefined = http.getCache(HTTP_METHODS.GET, url);
48
-
49
- // Remove a cached response
50
- http.removeCache(HTTP_METHODS.GET, url)
51
-
52
- // Clear all cache
53
- http.clearCache()
54
-
55
28
  // Perform a GET request
56
29
  const response: Response = await http.get(url);
57
30
 
@@ -80,10 +53,32 @@ const headers: Record<string, string> = { };
80
53
  const response: Response = await http.head(url, headers);
81
54
  ```
82
55
 
83
- ### Response model
56
+ ## Response model
84
57
 
85
58
  The result of every request is a standard [ECMAScript Response](https://developer.mozilla.org/en-US/docs/Web/API/Response) object.
86
59
 
87
- ### Caching mechanism
60
+ ## Drivers
61
+
62
+ There are two drivers available.
63
+
64
+ ### Fetch
65
+
66
+ This driver implements the [Node.js fetch](https://nodejs.org/api/globals.html#fetch) API.
67
+
68
+ ### Mapped
69
+
70
+ In-memory http client (suited for testing). It doesn't have any configuration options, but has an additional operation.
88
71
 
89
- All requests are cached by URL. To prevent this behavior, the cache for the URL must be deleted before performing the request.
72
+ ```ts
73
+ // Add a mapping
74
+ driver.setMapping(method, url, response);
75
+
76
+ // Get a mapping
77
+ const response: Response | undefined = driver.getMapping(method, url);
78
+
79
+ // Remove a mapping
80
+ driver.removeMapping(method, url);
81
+
82
+ // Remove all mappings
83
+ driver.clearMappings();
84
+ ```
package/dist/Http.d.ts CHANGED
@@ -1,11 +1,8 @@
1
+ import type Logger from '@theshelf/logging';
1
2
  import type { Driver } from './definitions/interfaces.js';
2
- export default class Http implements Driver {
3
+ export default class Http {
3
4
  #private;
4
- constructor(driver: Driver);
5
- setCache(method: string, url: string, response: Response): void;
6
- getCache(method: string, url: string): Response | undefined;
7
- removeCache(method: string, url: string): void;
8
- clearCache(): void;
5
+ constructor(driver: Driver, logger?: Logger);
9
6
  get(url: string, headers?: Record<string, string> | undefined): Promise<Response>;
10
7
  post(url: string, body: unknown, headers?: Record<string, string> | undefined): Promise<Response>;
11
8
  put(url: string, body: unknown, headers?: Record<string, string> | undefined): Promise<Response>;
package/dist/Http.js CHANGED
@@ -1,50 +1,70 @@
1
- import { HttpMethods } from './definitions/constants.js';
2
1
  export default class Http {
3
2
  #driver;
4
- #cache = new Map();
5
- constructor(driver) {
3
+ #logger;
4
+ #logPrefix;
5
+ constructor(driver, logger) {
6
6
  this.#driver = driver;
7
- }
8
- setCache(method, url, response) {
9
- const id = this.#createCacheId(method, url);
10
- this.#cache.set(id, response);
11
- }
12
- getCache(method, url) {
13
- const id = this.#createCacheId(method, url);
14
- return this.#cache.get(id);
15
- }
16
- removeCache(method, url) {
17
- const id = this.#createCacheId(method, url);
18
- this.#cache.delete(id);
19
- }
20
- clearCache() {
21
- this.#cache.clear();
7
+ this.#logger = logger?.for(Http.name);
8
+ this.#logPrefix = `${this.#driver.name} ->`;
22
9
  }
23
10
  async get(url, headers) {
24
- return this.getCache(HttpMethods.GET, url)
25
- ?? this.#driver.get(url, headers);
11
+ this.#logger?.debug(this.#logPrefix, 'Getting', url);
12
+ try {
13
+ return await this.#driver.get(url, headers);
14
+ }
15
+ catch (error) {
16
+ this.#logger?.error(this.#logPrefix, 'Get', url, 'failed with error', error);
17
+ throw error;
18
+ }
26
19
  }
27
20
  async post(url, body, headers) {
28
- return this.getCache(HttpMethods.POST, url)
29
- ?? this.#driver.post(url, body, headers);
21
+ this.#logger?.debug(this.#logPrefix, 'Posting', url);
22
+ try {
23
+ return await this.#driver.post(url, body, headers);
24
+ }
25
+ catch (error) {
26
+ this.#logger?.error(this.#logPrefix, 'Post', url, 'failed with error', error);
27
+ throw error;
28
+ }
30
29
  }
31
30
  async put(url, body, headers) {
32
- return this.getCache(HttpMethods.PUT, url)
33
- ?? this.#driver.put(url, body, headers);
31
+ this.#logger?.debug(this.#logPrefix, 'Putting', url);
32
+ try {
33
+ return await this.#driver.put(url, body, headers);
34
+ }
35
+ catch (error) {
36
+ this.#logger?.error(this.#logPrefix, 'Put', url, 'failed with error', error);
37
+ throw error;
38
+ }
34
39
  }
35
40
  async patch(url, body, headers) {
36
- return this.getCache(HttpMethods.PATCH, url)
37
- ?? this.#driver.patch(url, body, headers);
41
+ this.#logger?.debug(this.#logPrefix, 'Patching', url);
42
+ try {
43
+ return await this.#driver.patch(url, body, headers);
44
+ }
45
+ catch (error) {
46
+ this.#logger?.error(this.#logPrefix, 'Patch', url, 'failed with error', error);
47
+ throw error;
48
+ }
38
49
  }
39
50
  async delete(url, headers) {
40
- return this.getCache(HttpMethods.DELETE, url)
41
- ?? this.#driver.delete(url, headers);
51
+ this.#logger?.debug(this.#logPrefix, 'Deleting', url);
52
+ try {
53
+ return await this.#driver.delete(url, headers);
54
+ }
55
+ catch (error) {
56
+ this.#logger?.error(this.#logPrefix, 'Delete', url, 'failed with error', error);
57
+ throw error;
58
+ }
42
59
  }
43
60
  async head(url, headers) {
44
- return this.getCache(HttpMethods.HEAD, url)
45
- ?? this.#driver.head(url, headers);
46
- }
47
- #createCacheId(method, url) {
48
- return `${method.toUpperCase()} ${url.toLowerCase()}`;
61
+ this.#logger?.debug(this.#logPrefix, 'Heading', url);
62
+ try {
63
+ return await this.#driver.head(url, headers);
64
+ }
65
+ catch (error) {
66
+ this.#logger?.error(this.#logPrefix, 'Head', url, 'failed with error', error);
67
+ throw error;
68
+ }
49
69
  }
50
70
  }
@@ -1,4 +1,5 @@
1
1
  export interface Driver {
2
+ get name(): string;
2
3
  get(url: string, headers?: Record<string, string>): Promise<Response>;
3
4
  post(url: string, body: unknown, headers?: Record<string, string>): Promise<Response>;
4
5
  put(url: string, body: unknown, headers?: Record<string, string>): Promise<Response>;
@@ -1,5 +1,6 @@
1
1
  import type { Driver } from '../definitions/interfaces.js';
2
2
  export default class Fetch implements Driver {
3
+ get name(): string;
3
4
  get(url: string, headers?: Record<string, string> | undefined): Promise<Response>;
4
5
  post(url: string, body: unknown, headers?: Record<string, string> | undefined): Promise<Response>;
5
6
  put(url: string, body: unknown, headers?: Record<string, string> | undefined): Promise<Response>;
@@ -1,5 +1,6 @@
1
1
  import { HttpMethods } from '../definitions/constants.js';
2
2
  export default class Fetch {
3
+ get name() { return Fetch.name; }
3
4
  get(url, headers) {
4
5
  return fetch(url, { method: HttpMethods.GET, headers });
5
6
  }
@@ -0,0 +1,16 @@
1
+ import type { HttpMethod } from '../definitions/constants.js';
2
+ import type { Driver } from '../definitions/interfaces.js';
3
+ export default class Mapped implements Driver {
4
+ #private;
5
+ get name(): string;
6
+ setMapping(method: HttpMethod, url: string, response: Response): void;
7
+ getMapping(method: HttpMethod, url: string): Response | undefined;
8
+ removeMapping(method: HttpMethod, url: string): void;
9
+ get(url: string, headers?: Record<string, string> | undefined): Promise<Response>;
10
+ post(url: string, body: unknown, headers?: Record<string, string> | undefined): Promise<Response>;
11
+ put(url: string, body: unknown, headers?: Record<string, string> | undefined): Promise<Response>;
12
+ patch(url: string, body: unknown, headers?: Record<string, string> | undefined): Promise<Response>;
13
+ delete(url: string, headers?: Record<string, string> | undefined): Promise<Response>;
14
+ head(url: string, headers?: Record<string, string> | undefined): Promise<Response>;
15
+ clear(): void;
16
+ }
@@ -0,0 +1,51 @@
1
+ /* eslint @typescript-eslint/no-unused-vars: "off" */
2
+ import { HttpMethods } from '../definitions/constants.js';
3
+ export default class Mapped {
4
+ #mappings = new Map();
5
+ get name() { return Mapped.name; }
6
+ setMapping(method, url, response) {
7
+ const id = this.#createCacheId(method, url);
8
+ this.#mappings.set(id, response);
9
+ }
10
+ getMapping(method, url) {
11
+ const id = this.#createCacheId(method, url);
12
+ return this.#mappings.get(id)?.clone();
13
+ }
14
+ removeMapping(method, url) {
15
+ const id = this.#createCacheId(method, url);
16
+ this.#mappings.delete(id);
17
+ }
18
+ async get(url, headers) {
19
+ return this.getMapping(HttpMethods.GET, url)
20
+ ?? this.#createNotMappedResponse();
21
+ }
22
+ async post(url, body, headers) {
23
+ return this.getMapping(HttpMethods.POST, url)
24
+ ?? this.#createNotMappedResponse();
25
+ }
26
+ async put(url, body, headers) {
27
+ return this.getMapping(HttpMethods.PUT, url)
28
+ ?? this.#createNotMappedResponse();
29
+ }
30
+ async patch(url, body, headers) {
31
+ return this.getMapping(HttpMethods.PATCH, url)
32
+ ?? this.#createNotMappedResponse();
33
+ }
34
+ async delete(url, headers) {
35
+ return this.getMapping(HttpMethods.DELETE, url)
36
+ ?? this.#createNotMappedResponse();
37
+ }
38
+ async head(url, headers) {
39
+ return this.getMapping(HttpMethods.HEAD, url)
40
+ ?? this.#createNotMappedResponse();
41
+ }
42
+ clear() {
43
+ this.#mappings.clear();
44
+ }
45
+ #createCacheId(method, url) {
46
+ return `${method.toUpperCase()} ${url}`;
47
+ }
48
+ #createNotMappedResponse() {
49
+ return new Response('Not mapped', { status: 404 });
50
+ }
51
+ }
package/dist/index.d.ts CHANGED
@@ -2,5 +2,6 @@ export * from './definitions/constants.js';
2
2
  export type * from './definitions/constants.js';
3
3
  export type * from './definitions/interfaces.js';
4
4
  export { default as HttpError } from './errors/HttpError.js';
5
+ export { default as MappedDriver } from './drivers/Mapped.js';
5
6
  export { default as FetchDriver } from './drivers/Fetch.js';
6
7
  export { default } from './Http.js';
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from './definitions/constants.js';
2
2
  export { default as HttpError } from './errors/HttpError.js';
3
+ export { default as MappedDriver } from './drivers/Mapped.js';
3
4
  export { default as FetchDriver } from './drivers/Fetch.js';
4
5
  export { default } from './Http.js';
package/package.json CHANGED
@@ -1,24 +1,26 @@
1
1
  {
2
2
  "name": "@theshelf/http",
3
3
  "private": false,
4
- "version": "0.3.1",
4
+ "version": "0.4.0",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "url": "git+https://github.com/MaskingTechnology/theshelf.git"
8
8
  },
9
+ "license": "MIT",
9
10
  "scripts": {
10
11
  "build": "tsc",
11
12
  "clean": "rimraf dist",
12
- "test": "vitest run",
13
- "test-coverage": "vitest run --coverage",
14
13
  "lint": "eslint",
15
- "review": "npm run build && npm run lint && npm run test",
14
+ "review": "npm run build && npm run lint",
16
15
  "prepublishOnly": "npm run clean && npm run build"
17
16
  },
18
17
  "files": [
19
18
  "README.md",
20
19
  "dist"
21
20
  ],
22
- "types": "dist/index.d.ts",
23
- "exports": "./dist/index.js"
21
+ "types": "./dist/index.d.ts",
22
+ "exports": "./dist/index.js",
23
+ "peerDependencies": {
24
+ "@theshelf/logging": "^0.4.0"
25
+ }
24
26
  }