Skip to content

Commit cfbb93d

Browse files
author
Dmitry Dutikov
committed
interceptor implemented
1 parent 7093ecb commit cfbb93d

11 files changed

+2281
-0
lines changed

.github/workflows/npm-publish.yml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2+
# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
3+
4+
name: Node.js Package
5+
6+
on:
7+
workflow_dispatch:
8+
release:
9+
types: [created]
10+
11+
jobs:
12+
build:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v2
16+
- uses: actions/setup-node@v2
17+
with:
18+
node-version: 16
19+
- run: npm ci
20+
- run: npm test
21+
22+
publish-npm:
23+
needs: build
24+
runs-on: ubuntu-latest
25+
steps:
26+
- uses: actions/checkout@v2
27+
- uses: actions/setup-node@v2
28+
with:
29+
node-version: 16
30+
registry-url: https://registry.npmjs.org/
31+
- run: npm ci
32+
- run: npm publish
33+
env:
34+
NODE_AUTH_TOKEN: ${{secrets.npm_token}}

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
node_modules
2+
.idea

.npmignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
node_modules
2+
test
3+
.github

README.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# JSON22-Axios
2+
Axios interceptor providing support to [JSON22](https://github.com/dancecoder/json22#readme) data format in your applications.
3+
4+
## Features
5+
* Ready to use [Axios](https://axios-http.com/) interceptor
6+
* Parse [JSON22](https://github.com/dancecoder/json22#readme) body content
7+
* Serialize data to JSON22
8+
* Support for global interceptor as well as request level transformation
9+
* Both CJS/ESM modules support
10+
11+
## Installation
12+
```shell
13+
npm install json22-axios
14+
```
15+
16+
Add interceptor at your client setup
17+
```javascript
18+
import axios from 'axios';
19+
import { Json22RequestInterceptor } from 'json22-axios';
20+
21+
axios.interceptors.request.use(Json22RequestInterceptor());
22+
```
23+
24+
For old-fashioned javascript
25+
26+
```javascript
27+
const axios = require('axios');
28+
const { Json22RequestInterceptor } = require('json22-axios');
29+
30+
axios.interceptors.request.use(Json22RequestInterceptor());
31+
```
32+
33+
## Options
34+
35+
Both stringify and parse methods of JSON22 accepts options. You may be interested to define such options at global level as well as with isolated client instance.
36+
37+
`Json22RequestInterceptor` accepts the next options structure
38+
39+
```typescript
40+
interface Json22AxiosOptions {
41+
json22ParseOptions?: Json22ParseOptions;
42+
json22StringifyOptions?: Json22StringifyOptions;
43+
}
44+
```
45+
See also `Json22ParseOptions` and `Json22StringifyOptions` at [JSON22 API description](https://github.com/dancecoder/json22#api)
46+
47+
### Define global level options
48+
```javascript
49+
import axios from 'axios';
50+
import { Json22RequestInterceptor } from 'json22-axios';
51+
import { TypedModel } from './models/typed-model.js';
52+
53+
axios.interceptors.request.use(Json22RequestInterceptor({
54+
json22ParseOptions: { context: { TypedModel } },
55+
}));
56+
```
57+
58+
### Define isolated client options
59+
```javascript
60+
import axios from 'axios';
61+
import { Json22RequestInterceptor } from 'json22-axios';
62+
import { TypedModel } from './models/typed-model.js';
63+
64+
const client = axios.create();
65+
client.interceptors.request.use(Json22RequestInterceptor({
66+
json22ParseOptions: { context: { TypedModel } },
67+
}));
68+
```
69+
70+
## Request level data transformation
71+
In same rare cases you might be interested to set up data transformation for a specific query.
72+
This case you shall not use the interceptor. Instead, you'll have to use data transformers functions.
73+
Data transformers do not accept options, so you'll need to define it on query configuration at `json22Options`.
74+
```javascript
75+
import axios from 'axios';
76+
import { transformJson22StringToData, transformDataToJson22String } from 'json22-axios';
77+
import { TypedModel } from './models/typed-model.js';
78+
79+
export async function postData(data) {
80+
const resp = await axios.request({
81+
method: 'POST',
82+
baseURL: 'https://example.com',
83+
url: '/api/data',
84+
transformResponse: transformJson22StringToData,
85+
transformRequest: transformDataToJson22String,
86+
json22Options: { json22ParseOptions: { context: { TypedModel } } },
87+
data
88+
});
89+
return resp.data;
90+
}
91+
```
92+
__Note: `json22Options` configuration field is not defined by axios.__
93+
That is the reason we do not recommend to use json22 data transformers directly.
94+
Please, use interceptor instead.

index.cjs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
/*
2+
MIT License
3+
4+
Copyright (c) 2022 Dmitry Dutikov
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in all
14+
copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
SOFTWARE.
23+
*/
24+
25+
const { JSON22 } = require('json22');
26+
const utils = require('axios/lib/utils');
27+
28+
const PARSE_OPTIONS = Symbol('Json22ParseOptions');
29+
const STRINGIFY_OPTIONS = Symbol('Json22StringifyOptions');
30+
31+
/**
32+
* @param {any} data
33+
* @param {AxiosRequestHeaders} headers
34+
* */
35+
function transformDataToJson22String(data, headers) {
36+
const unsupported = [
37+
utils.isFormData,
38+
utils.isArrayBuffer,
39+
utils.isBuffer,
40+
utils.isStream,
41+
utils.isFile,
42+
utils.isBlob,
43+
utils.isArrayBufferView,
44+
utils.isURLSearchParams,
45+
];
46+
47+
if (!unsupported.some(fn => fn(data))) {
48+
const contentType = headers['Content-Type'] ?? headers['content-type'];
49+
if (contentType !== 'multipart/form-data' && contentType !== 'application/json') {
50+
const isObjectPayload = utils.isObject(data);
51+
if (isObjectPayload) {
52+
/** @type {AxiosRequestConfig} */
53+
const config = this;
54+
headers['Content-Type'] = JSON22.mimeType;
55+
// TODO: combine both stringify options objects
56+
return JSON22.stringify(data, config[STRINGIFY_OPTIONS] ?? config.json22Options?.json22StringifyOptions);
57+
}
58+
}
59+
}
60+
return data;
61+
}
62+
63+
/**
64+
* @param {any} data
65+
* @param {AxiosResponseHeaders} [headers]
66+
* */
67+
function transformJson22StringToData(data, headers) {
68+
/** @type {AxiosRequestConfig} */
69+
const config = this;
70+
71+
if ((headers?.['content-type'] ?? headers?.['Content-Type']) === JSON22.mimeType) {
72+
// TODO: combine both parse options objects
73+
return JSON22.parse(data, config[PARSE_OPTIONS] ?? config.json22Options?.json22ParseOptions);
74+
}
75+
76+
return data;
77+
}
78+
79+
/**
80+
* @param {Json22AxiosOptions} [options={}]
81+
* */
82+
function Json22RequestInterceptor(options = {}) {
83+
/**
84+
* @param {AxiosRequestConfig} config
85+
* */
86+
function json22RqIntercept(config) {
87+
config[PARSE_OPTIONS] = options.json22ParseOptions;
88+
config[STRINGIFY_OPTIONS] = options.json22StringifyOptions;
89+
config.transformRequest.unshift(transformDataToJson22String);
90+
config.transformResponse.unshift(transformJson22StringToData);
91+
return config;
92+
}
93+
return json22RqIntercept;
94+
}
95+
96+
module.exports = { transformDataToJson22String, transformJson22StringToData, Json22RequestInterceptor };

index.d.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
MIT License
3+
4+
Copyright (c) 2022 Dmitry Dutikov
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy
7+
of this software and associated documentation files (the "Software"), to deal
8+
in the Software without restriction, including without limitation the rights
9+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
copies of the Software, and to permit persons to whom the Software is
11+
furnished to do so, subject to the following conditions:
12+
13+
The above copyright notice and this permission notice shall be included in all
14+
copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
SOFTWARE.
23+
*/
24+
25+
import { AxiosRequestConfig, AxiosRequestHeaders, AxiosResponseHeaders } from 'axios';
26+
import { Json22ParseOptions, Json22StringifyOptions } from 'json22';
27+
28+
export interface Json22AxiosOptions {
29+
json22ParseOptions?: Json22ParseOptions;
30+
json22StringifyOptions?: Json22StringifyOptions;
31+
}
32+
33+
export function transformDataToJson22String(data: any, headers: AxiosRequestHeaders): any;
34+
35+
export function transformJson22StringToData(data: any, headers: AxiosResponseHeaders): any;
36+
37+
export function Json22RequestInterceptor(options?: Json22AxiosOptions): (config: AxiosRequestConfig) => AxiosRequestConfig;

index.js

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import { JSON22 } from 'json22';
2+
import utils from 'axios/lib/utils.js';
3+
4+
const PARSE_OPTIONS = Symbol('Json22ParseOptions');
5+
const STRINGIFY_OPTIONS = Symbol('Json22StringifyOptions');
6+
7+
/**
8+
* @param {any} data
9+
* @param {AxiosRequestHeaders} headers
10+
* */
11+
export function transformDataToJson22String(data, headers) {
12+
const unsupported = [
13+
utils.isFormData,
14+
utils.isArrayBuffer,
15+
utils.isBuffer,
16+
utils.isStream,
17+
utils.isFile,
18+
utils.isBlob,
19+
utils.isArrayBufferView,
20+
utils.isURLSearchParams,
21+
];
22+
23+
if (!unsupported.some(fn => fn(data))) {
24+
const contentType = headers['Content-Type'] ?? headers['content-type'];
25+
if (contentType !== 'multipart/form-data' && contentType !== 'application/json') {
26+
const isObjectPayload = utils.isObject(data);
27+
if (isObjectPayload) {
28+
/** @type {AxiosRequestConfig} */
29+
const config = this;
30+
headers['Content-Type'] = JSON22.mimeType;
31+
// TODO: combine both stringify options objects
32+
return JSON22.stringify(data, config[STRINGIFY_OPTIONS] ?? config.json22Options?.json22StringifyOptions);
33+
}
34+
}
35+
}
36+
return data;
37+
}
38+
39+
/**
40+
* @param {any} data
41+
* @param {AxiosResponseHeaders} [headers]
42+
* */
43+
export function transformJson22StringToData(data, headers) {
44+
/** @type {AxiosRequestConfig} */
45+
const config = this;
46+
47+
if ((headers?.['content-type'] ?? headers?.['Content-Type']) === JSON22.mimeType) {
48+
// TODO: combine both parse options objects
49+
return JSON22.parse(data, config[PARSE_OPTIONS] ?? config.json22Options?.json22ParseOptions);
50+
}
51+
52+
return data;
53+
}
54+
55+
/**
56+
* @param {Json22AxiosOptions} [options={}]
57+
* */
58+
export function Json22RequestInterceptor(options = {}) {
59+
/**
60+
* @param {AxiosRequestConfig} config
61+
* */
62+
function json22RqIntercept(config) {
63+
config[PARSE_OPTIONS] = options.json22ParseOptions;
64+
config[STRINGIFY_OPTIONS] = options.json22StringifyOptions;
65+
config.transformRequest.unshift(transformDataToJson22String);
66+
config.transformResponse.unshift(transformJson22StringToData);
67+
return config;
68+
}
69+
return json22RqIntercept;
70+
}

0 commit comments

Comments
 (0)