@contentstack/cli-utilities 0.1.1-beta.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/.eslintignore +2 -0
- package/.eslintrc +40 -0
- package/.mocharc.json +12 -0
- package/.nycrc.json +5 -0
- package/LICENSE +21 -0
- package/lib/cli-error.js +16 -0
- package/lib/cli-ux.js +59 -0
- package/lib/config-handler.js +128 -0
- package/lib/flag-deprecation-check.js +40 -0
- package/lib/http-client/client.js +313 -0
- package/lib/http-client/http-response.js +46 -0
- package/lib/http-client/index.js +7 -0
- package/lib/index.js +17 -0
- package/lib/interfaces/index.js +2 -0
- package/lib/logger.js +92 -0
- package/lib/message-handler.js +43 -0
- package/lib/selectors/index.js +376 -0
- package/lib/selectors/interfaces.js +2 -0
- package/lib/selectors/validations.js +9 -0
- package/messages/auth.json +46 -0
- package/messages/config.json +9 -0
- package/messages/core.json +6 -0
- package/package.json +68 -0
- package/src/cli-error.ts +15 -0
- package/src/cli-ux.ts +78 -0
- package/src/config-handler.ts +142 -0
- package/src/flag-deprecation-check.ts +41 -0
- package/src/http-client/client.ts +369 -0
- package/src/http-client/http-response.ts +55 -0
- package/src/http-client/index.ts +7 -0
- package/src/index.ts +7 -0
- package/src/interfaces/index.ts +61 -0
- package/src/logger.ts +101 -0
- package/src/message-handler.ts +45 -0
- package/src/selectors/index.ts +381 -0
- package/src/selectors/interfaces.ts +39 -0
- package/src/selectors/validations.ts +5 -0
- package/test/helpers/init.js +4 -0
- package/test/tsconfig.json +6 -0
- package/tsconfig.json +20 -0
- package/types/cli-error.d.ts +4 -0
- package/types/cli-ux.d.ts +22 -0
- package/types/config-handler.d.ts +17 -0
- package/types/flag-deprecation-check.d.ts +7 -0
- package/types/http-client.d.ts +54 -0
- package/types/index.d.ts +7 -0
- package/types/interfaces/index.d.ts +50 -0
- package/types/logger.d.ts +17 -0
- package/types/message-handler.d.ts +12 -0
- package/types/selectors/index.d.ts +12 -0
- package/types/selectors/interfaces.d.ts +32 -0
- package/types/selectors/validations.d.ts +1 -0
package/package.json
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@contentstack/cli-utilities",
|
|
3
|
+
"version": "0.1.1-beta.1",
|
|
4
|
+
"description": "Utilities for contentstack projects",
|
|
5
|
+
"main": "lib/index.js",
|
|
6
|
+
"types": "./types/index.d.ts",
|
|
7
|
+
"scripts": {
|
|
8
|
+
"prepack": "npm run clean && npm run compile",
|
|
9
|
+
"clean": "rm -rf ./lib && rm -rf tsconfig.build.tsbuildinfo",
|
|
10
|
+
"compile": "tsc -b tsconfig.json",
|
|
11
|
+
"test:report": "tsc -p test && nyc --reporter=lcov --extension .ts mocha --forbid-only \"test/**/*.test.ts\"",
|
|
12
|
+
"pretest": "tsc -p test",
|
|
13
|
+
"test": "nyc --extension .ts mocha --forbid-only \"test/**/*.test.ts\"",
|
|
14
|
+
"posttest": "npm run lint",
|
|
15
|
+
"lint": "eslint src/**/*.ts",
|
|
16
|
+
"format": "eslint src/**/*.ts --fix"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": ""
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"contentstack",
|
|
24
|
+
"utilities"
|
|
25
|
+
],
|
|
26
|
+
"author": "contentstack",
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"dependencies": {
|
|
29
|
+
"@contentstack/management": "^1.3.0",
|
|
30
|
+
"@oclif/core": "^1.8.2",
|
|
31
|
+
"axios": "^0.27.2",
|
|
32
|
+
"chalk": "^4.0.0",
|
|
33
|
+
"conf": "^10.1.2",
|
|
34
|
+
"debug": "^4.1.1",
|
|
35
|
+
"inquirer": "^8.0.0",
|
|
36
|
+
"ora": "^5.4.0",
|
|
37
|
+
"winston": "^3.7.2"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@oclif/plugin-help": "^5.1.12",
|
|
41
|
+
"@oclif/test": "^1.2.8",
|
|
42
|
+
"@types/axios": "^0.14.0",
|
|
43
|
+
"@types/chai": "^4.2.18",
|
|
44
|
+
"@types/conf": "^3.0.0",
|
|
45
|
+
"@types/dot-prop": "^5.0.1",
|
|
46
|
+
"@types/inquirer": "^7.3.1",
|
|
47
|
+
"@types/mkdirp": "^1.0.1",
|
|
48
|
+
"@types/mocha": "^8.2.2",
|
|
49
|
+
"@types/nock": "^11.1.0",
|
|
50
|
+
"@types/node": "^14.14.32",
|
|
51
|
+
"@types/sinon": "^10.0.2",
|
|
52
|
+
"@types/tar": "^4.0.3",
|
|
53
|
+
"@types/winston": "^2.4.4",
|
|
54
|
+
"chai": "^4.3.4",
|
|
55
|
+
"eslint": "^8.18.0",
|
|
56
|
+
"eslint-config-oclif": "^3.1.0",
|
|
57
|
+
"eslint-config-oclif-typescript": "^0.1.0",
|
|
58
|
+
"globby": "^10.0.2",
|
|
59
|
+
"mocha": "^9.2.2",
|
|
60
|
+
"nock": "^13.1.0",
|
|
61
|
+
"nyc": "^15.1.0",
|
|
62
|
+
"rimraf": "^2.7.1",
|
|
63
|
+
"sinon": "^11.1.1",
|
|
64
|
+
"tmp": "^0.2.1",
|
|
65
|
+
"ts-node": "^8.10.2",
|
|
66
|
+
"typescript": "^4.7.4"
|
|
67
|
+
}
|
|
68
|
+
}
|
package/src/cli-error.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export default class CLIError extends Error {
|
|
2
|
+
suggestions?: string[];
|
|
3
|
+
|
|
4
|
+
constructor(message, suggestions?: string[]) {
|
|
5
|
+
super(message);
|
|
6
|
+
this.suggestions = suggestions;
|
|
7
|
+
this.message = message;
|
|
8
|
+
|
|
9
|
+
if (typeof Error.captureStackTrace === 'function') {
|
|
10
|
+
Error.captureStackTrace(this, this.constructor);
|
|
11
|
+
} else {
|
|
12
|
+
this.stack = new Error(message).stack;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
package/src/cli-ux.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import inquirer from 'inquirer';
|
|
3
|
+
import { ux as cliux, Table } from '@oclif/core/lib/cli-ux'
|
|
4
|
+
|
|
5
|
+
import messageHandler from './message-handler';
|
|
6
|
+
import { PrintOptions, InquirePayload, CliUXPromptOptions } from './interfaces';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* CLI Interface
|
|
10
|
+
*/
|
|
11
|
+
class CLIInterface {
|
|
12
|
+
private loading: boolean;
|
|
13
|
+
|
|
14
|
+
constructor() {
|
|
15
|
+
this.loading = false;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get uxTable(): typeof Table.table {
|
|
19
|
+
return cliux.table
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
init(context) {}
|
|
23
|
+
|
|
24
|
+
print(message: string, opts?: PrintOptions): void {
|
|
25
|
+
if (opts && opts.color) {
|
|
26
|
+
cliux.log(chalk[opts.color](messageHandler.parse(message)));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
cliux.log(messageHandler.parse(message));
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
success(message: string): void {
|
|
34
|
+
cliux.log(chalk.green(messageHandler.parse(message)));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
error(message: string, ...params: any): void {
|
|
38
|
+
cliux.log(chalk.red(messageHandler.parse(message) + (params && params.length > 0 ? ': ' : '')), ...params);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
loader(message: string = ''): void {
|
|
42
|
+
if (!this.loading) {
|
|
43
|
+
cliux.action.start(messageHandler.parse(message));
|
|
44
|
+
} else {
|
|
45
|
+
cliux.action.stop(messageHandler.parse(message));
|
|
46
|
+
}
|
|
47
|
+
this.loading = !this.loading;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
table(
|
|
51
|
+
data: Record<string, unknown>[],
|
|
52
|
+
columns: Table.table.Columns<Record<string, unknown>>,
|
|
53
|
+
options?: Table.table.Options
|
|
54
|
+
): void {
|
|
55
|
+
cliux.table(data, columns, options);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async inquire<T>(inquirePayload: InquirePayload): Promise<T> {
|
|
59
|
+
inquirePayload.message = messageHandler.parse(inquirePayload.message);
|
|
60
|
+
const result = await inquirer.prompt(inquirePayload as inquirer.QuestionCollection<T>);
|
|
61
|
+
|
|
62
|
+
return result[inquirePayload.name] as T;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
prompt(name: string, options?: CliUXPromptOptions): Promise<any> {
|
|
66
|
+
return cliux.prompt(name, options)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
confirm(message?: string): Promise<boolean> {
|
|
70
|
+
return cliux.confirm(message)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
progress(options?: any): any {
|
|
74
|
+
return cliux.progress(options)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export default new CLIInterface();
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import Conf from 'conf';
|
|
2
|
+
import { v4 as uuid } from 'uuid';
|
|
3
|
+
import { existsSync, unlinkSync } from 'fs'
|
|
4
|
+
|
|
5
|
+
const ENC_KEY = 'encryptionKey';
|
|
6
|
+
const ENCRYPT_CONF: boolean = true
|
|
7
|
+
const CONFIG_NAME = 'contentstack_cli';
|
|
8
|
+
const ENC_CONFIG_NAME = 'contentstack_cli_obfuscate';
|
|
9
|
+
|
|
10
|
+
class Config {
|
|
11
|
+
private config: Conf;
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
this.init()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
init() {
|
|
18
|
+
return ENCRYPT_CONF === true
|
|
19
|
+
? this.getEncryptedConfig()
|
|
20
|
+
: this.getDecryptedConfig()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
private fallbackInit(): Conf {
|
|
24
|
+
return new Conf({ configName: CONFIG_NAME, encryptionKey: ENC_KEY })
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
private getObfuscationKey() {
|
|
28
|
+
const obfuscationKeyName = 'obfuscation_key'
|
|
29
|
+
const encConfig = new Conf({ configName: ENC_CONFIG_NAME })
|
|
30
|
+
let obfuscationKey: any = encConfig.get(obfuscationKeyName)
|
|
31
|
+
|
|
32
|
+
if (!obfuscationKey) {
|
|
33
|
+
encConfig.set(obfuscationKeyName, uuid())
|
|
34
|
+
obfuscationKey = encConfig.get(obfuscationKeyName)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return obfuscationKey
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
private getConfigDataAndUnlinkConfigFile(config: Conf) {
|
|
41
|
+
let configData
|
|
42
|
+
|
|
43
|
+
if (config?.path) {
|
|
44
|
+
if (existsSync(config.path)) {
|
|
45
|
+
configData = JSON.parse(JSON.stringify(config?.store || {})) // NOTE convert prototype object to plain object
|
|
46
|
+
unlinkSync(config.path) // NOTE remove old config file
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return configData
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
private getEncryptedConfig(configData?: Record<string, unknown>, skip = false) {
|
|
54
|
+
const getEncryptedDataElseFallBack = () => {
|
|
55
|
+
try {
|
|
56
|
+
// NOTE reading current code base encrypted file if exist
|
|
57
|
+
const encryptionKey: any = this.getObfuscationKey()
|
|
58
|
+
this.config = new Conf({ configName: CONFIG_NAME, encryptionKey })
|
|
59
|
+
|
|
60
|
+
if (Object.keys(configData || {})?.length) {
|
|
61
|
+
this.config.set(configData) // NOTE set config data if passed any
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
// NOTE reading old code base encrypted file if exist
|
|
65
|
+
try {
|
|
66
|
+
const config = this.fallbackInit()
|
|
67
|
+
const configData = this.getConfigDataAndUnlinkConfigFile(config)
|
|
68
|
+
this.getEncryptedConfig(configData, true)
|
|
69
|
+
} catch (error) {
|
|
70
|
+
// console.trace(error.message)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
if (skip === false) {
|
|
77
|
+
const config = new Conf({ configName: CONFIG_NAME })
|
|
78
|
+
const configData = this.getConfigDataAndUnlinkConfigFile(config)
|
|
79
|
+
this.getEncryptedConfig(configData, true)
|
|
80
|
+
} else {
|
|
81
|
+
getEncryptedDataElseFallBack()
|
|
82
|
+
}
|
|
83
|
+
} catch (error) {
|
|
84
|
+
// console.trace(error.message)
|
|
85
|
+
// NOTE reading current code base encrypted file if exist
|
|
86
|
+
getEncryptedDataElseFallBack()
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return this.config
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
private getDecryptedConfig(configData?: Record<string, unknown>) {
|
|
93
|
+
try {
|
|
94
|
+
this.config = new Conf({ configName: CONFIG_NAME })
|
|
95
|
+
|
|
96
|
+
if (Object.keys(configData || {})?.length) {
|
|
97
|
+
this.config.set(configData) // NOTE set config data if passed any
|
|
98
|
+
}
|
|
99
|
+
} catch (error) {
|
|
100
|
+
// console.trace(error.message)
|
|
101
|
+
|
|
102
|
+
try {
|
|
103
|
+
const encryptionKey: any = this.getObfuscationKey()
|
|
104
|
+
let config = new Conf({ configName: CONFIG_NAME, encryptionKey })
|
|
105
|
+
const configData = this.getConfigDataAndUnlinkConfigFile(config)
|
|
106
|
+
this.getDecryptedConfig(configData) // NOTE NOTE reinitialize the config with old data and new decrypted file
|
|
107
|
+
} catch (error) {
|
|
108
|
+
// console.trace(error.message)
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const config = this.fallbackInit()
|
|
112
|
+
const configData = this.getConfigDataAndUnlinkConfigFile(config)
|
|
113
|
+
this.getDecryptedConfig(configData) // NOTE reinitialize the config with old data and new decrypted file
|
|
114
|
+
} catch (error) {
|
|
115
|
+
// console.trace(error.message)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return this.config
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
get(key): string | any {
|
|
124
|
+
return this.config.get(key);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
async set(key, value) {
|
|
128
|
+
this.config.set(key, value);
|
|
129
|
+
return this.config;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
delete(key) {
|
|
133
|
+
this.config.delete(key);
|
|
134
|
+
return this.config;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
clear() {
|
|
138
|
+
this.config.clear()
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export default new Config();
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import cliux from './cli-ux';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* checks the deprecation and prints it
|
|
5
|
+
* @param {Array} deprecatedFlags flags to be deprecated
|
|
6
|
+
* @param {String} customMessage [optional] a custom message
|
|
7
|
+
* @returns flag parser
|
|
8
|
+
*/
|
|
9
|
+
export default function (deprecatedFlags = [], suggestions = [], customMessage?: string) {
|
|
10
|
+
return (input, command) => {
|
|
11
|
+
const { context: { flagWarningPrintState = {} } = {} } = command
|
|
12
|
+
let isCommandHasDeprecationFlag = false;
|
|
13
|
+
deprecatedFlags.forEach((item) => {
|
|
14
|
+
if (command.argv.indexOf(item) !== -1) {
|
|
15
|
+
if (flagWarningPrintState[command.id + item]) {
|
|
16
|
+
return input
|
|
17
|
+
}
|
|
18
|
+
flagWarningPrintState[command.id + item] = true
|
|
19
|
+
isCommandHasDeprecationFlag = true;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
if (isCommandHasDeprecationFlag) {
|
|
24
|
+
let depreactionMessage = '';
|
|
25
|
+
if (customMessage) {
|
|
26
|
+
depreactionMessage = customMessage;
|
|
27
|
+
} else {
|
|
28
|
+
depreactionMessage = `WARNING!!! You're using the old (soon to be deprecated) Contentstack CLI flags (${deprecatedFlags.join(
|
|
29
|
+
', ',
|
|
30
|
+
)}).`;
|
|
31
|
+
|
|
32
|
+
if (suggestions.length > 0) {
|
|
33
|
+
depreactionMessage += ` We recommend you to use the updated flags (${suggestions.join(', ')}).`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
cliux.print(depreactionMessage, { color: 'yellow' });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return input;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
import Axios, { AxiosInstance, AxiosRequestConfig, AxiosResponse } from 'axios';
|
|
2
|
+
import { HttpResponse } from './http-response';
|
|
3
|
+
|
|
4
|
+
export class HttpClient {
|
|
5
|
+
/**
|
|
6
|
+
* The request configuration.
|
|
7
|
+
*/
|
|
8
|
+
private request: AxiosRequestConfig;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* The request configuration.
|
|
12
|
+
*/
|
|
13
|
+
private readonly axiosInstance: AxiosInstance;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The payload format for a JSON or form-url-encoded request.
|
|
17
|
+
*/
|
|
18
|
+
private bodyFormat: BodyFormat = 'json';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Createa new pending HTTP request instance.
|
|
22
|
+
*/
|
|
23
|
+
constructor() {
|
|
24
|
+
this.request = {};
|
|
25
|
+
this.axiosInstance = Axios.create();
|
|
26
|
+
|
|
27
|
+
// Sets payload format as json by default
|
|
28
|
+
this.asJson();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Create a reusable HttpClient instance.
|
|
33
|
+
*
|
|
34
|
+
* @returns {HttpClient}
|
|
35
|
+
*/
|
|
36
|
+
static create(): HttpClient {
|
|
37
|
+
return new this();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Returns the Axios request config.
|
|
42
|
+
*
|
|
43
|
+
* @returns {AxiosRequestConfig}
|
|
44
|
+
*/
|
|
45
|
+
requestConfig(): AxiosRequestConfig {
|
|
46
|
+
return this.request;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Resets the request config.
|
|
51
|
+
*
|
|
52
|
+
* @returns {AxiosRequestConfig}
|
|
53
|
+
*/
|
|
54
|
+
resetConfig(): HttpClient {
|
|
55
|
+
this.request = {};
|
|
56
|
+
return this;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Use the given `baseUrl` for all requests.
|
|
61
|
+
*
|
|
62
|
+
* @param {String} baseUrl
|
|
63
|
+
*
|
|
64
|
+
* @returns {HttpClient}
|
|
65
|
+
*/
|
|
66
|
+
baseUrl(baseUrl: string): HttpClient {
|
|
67
|
+
if (typeof baseUrl !== 'string') {
|
|
68
|
+
throw new Error(`The base URL must be a string. Received "${typeof baseUrl}"`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
this.request.baseURL = baseUrl;
|
|
72
|
+
|
|
73
|
+
return this;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Add request headers.
|
|
78
|
+
* @returns {HttpClient}
|
|
79
|
+
*/
|
|
80
|
+
headers(headers: any): HttpClient {
|
|
81
|
+
this.request.headers = { ...this.request.headers, ...headers };
|
|
82
|
+
|
|
83
|
+
return this;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Add query parameters to the request.
|
|
88
|
+
*
|
|
89
|
+
* @param {Object} queryParams
|
|
90
|
+
*
|
|
91
|
+
* @returns {HttpClient}
|
|
92
|
+
*/
|
|
93
|
+
queryParams(queryParams: object): HttpClient {
|
|
94
|
+
this.request.params = { ...this.request.params, ...queryParams };
|
|
95
|
+
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Add basic authentication via `username` and `password` to the request.
|
|
101
|
+
*
|
|
102
|
+
* @param {String} username
|
|
103
|
+
* @param {String} password
|
|
104
|
+
*
|
|
105
|
+
* @returns {HttpClient}
|
|
106
|
+
*/
|
|
107
|
+
basicAuth(username: string, password: string): HttpClient {
|
|
108
|
+
this.request.auth = { username, password };
|
|
109
|
+
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Add an authorization `token` to the request.
|
|
115
|
+
*
|
|
116
|
+
* @param {String} token
|
|
117
|
+
* @param {String} type
|
|
118
|
+
*
|
|
119
|
+
* @returns {HttpClient}
|
|
120
|
+
*/
|
|
121
|
+
token(token: string, type: string = 'Bearer'): HttpClient {
|
|
122
|
+
return this.headers({
|
|
123
|
+
Authorization: `${type} ${token}`.trim(),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Merge your own custom Axios options into the request.
|
|
129
|
+
*
|
|
130
|
+
* @param {Object} options
|
|
131
|
+
*
|
|
132
|
+
* @returns {HttpClient}
|
|
133
|
+
*/
|
|
134
|
+
options(options: AxiosRequestConfig = {}): HttpClient {
|
|
135
|
+
Object.assign(this.request, options);
|
|
136
|
+
|
|
137
|
+
return this;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Add a request payload.
|
|
142
|
+
*
|
|
143
|
+
* @param {*} data
|
|
144
|
+
*
|
|
145
|
+
* @returns {HttpClient}
|
|
146
|
+
*/
|
|
147
|
+
payload(data: any): HttpClient {
|
|
148
|
+
this.request.data = data;
|
|
149
|
+
|
|
150
|
+
return this;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Define the request `timeout` in milliseconds.
|
|
155
|
+
*
|
|
156
|
+
* @param {Number} timeout
|
|
157
|
+
*
|
|
158
|
+
* @returns {HttpClient}
|
|
159
|
+
*/
|
|
160
|
+
timeout(timeout: number): HttpClient {
|
|
161
|
+
this.request.timeout = timeout;
|
|
162
|
+
|
|
163
|
+
return this;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Tell HttpClient to send the request as JSON payload.
|
|
168
|
+
*
|
|
169
|
+
* @returns {HttpClient}
|
|
170
|
+
*/
|
|
171
|
+
asJson(): HttpClient {
|
|
172
|
+
return this.payloadFormat('json').contentType('application/json');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Tell HttpClient to send the request as form parameters,
|
|
177
|
+
* encoded as URL query parameters.
|
|
178
|
+
*
|
|
179
|
+
* @returns {HttpClient}
|
|
180
|
+
*/
|
|
181
|
+
asFormParams(): HttpClient {
|
|
182
|
+
return this.payloadFormat('formParams').contentType('application/x-www-form-urlencoded');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Set the request payload format.
|
|
187
|
+
*
|
|
188
|
+
* @param {String} format
|
|
189
|
+
*
|
|
190
|
+
* @returns {HttpClient}
|
|
191
|
+
*/
|
|
192
|
+
payloadFormat(format: BodyFormat): HttpClient {
|
|
193
|
+
this.bodyFormat = format;
|
|
194
|
+
|
|
195
|
+
return this;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Set the `Accept` request header. This indicates what
|
|
200
|
+
* content type the server should return.
|
|
201
|
+
*
|
|
202
|
+
* @param {String} accept
|
|
203
|
+
*
|
|
204
|
+
* @returns {HttpClient}
|
|
205
|
+
*/
|
|
206
|
+
accept(accept: string): HttpClient {
|
|
207
|
+
return this.headers({ Accept: accept });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Set the `Accept` request header to JSON. This indicates
|
|
212
|
+
* that the server should return JSON data.
|
|
213
|
+
*
|
|
214
|
+
* @param {String} accept
|
|
215
|
+
*
|
|
216
|
+
* @returns {HttpClient}
|
|
217
|
+
*/
|
|
218
|
+
acceptJson(): HttpClient {
|
|
219
|
+
return this.accept('application/json');
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Set the `Content-Type` request header.
|
|
224
|
+
*
|
|
225
|
+
* @param {String} contentType
|
|
226
|
+
*
|
|
227
|
+
* @returns {HttpClient}
|
|
228
|
+
*/
|
|
229
|
+
contentType(contentType: string): HttpClient {
|
|
230
|
+
return this.headers({ 'Content-Type': contentType });
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Send an HTTP GET request, optionally with the given `queryParams`.
|
|
235
|
+
*
|
|
236
|
+
* @param {String} url
|
|
237
|
+
* @param {Object} queryParams
|
|
238
|
+
*
|
|
239
|
+
* @returns {HttpResponse}
|
|
240
|
+
*
|
|
241
|
+
* @throws
|
|
242
|
+
*/
|
|
243
|
+
async get<R>(url: string, queryParams: object = {}): Promise<HttpResponse<R>> {
|
|
244
|
+
this.queryParams(queryParams);
|
|
245
|
+
|
|
246
|
+
return this.send<R>('GET', url);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Send an HTTP POST request, optionally with the given `payload`.
|
|
251
|
+
*
|
|
252
|
+
* @param {String} url
|
|
253
|
+
* @param {Object} payload
|
|
254
|
+
*
|
|
255
|
+
* @returns {HttpResponse}
|
|
256
|
+
*
|
|
257
|
+
* @throws
|
|
258
|
+
*/
|
|
259
|
+
async post<R>(url: string, payload?: any): Promise<HttpResponse<R>> {
|
|
260
|
+
if (payload) {
|
|
261
|
+
this.payload(payload);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return this.send<R>('POST', url);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Send an HTTP PUT request, optionally with the given `payload`.
|
|
269
|
+
*
|
|
270
|
+
* @param {String} url
|
|
271
|
+
* @param {Object} payload
|
|
272
|
+
*
|
|
273
|
+
* @returns {HttpResponse}
|
|
274
|
+
*
|
|
275
|
+
* @throws
|
|
276
|
+
*/
|
|
277
|
+
async put<R>(url: string, payload?: any): Promise<HttpResponse<R>> {
|
|
278
|
+
if (payload) {
|
|
279
|
+
this.payload(payload);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return this.send<R>('PUT', url);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Send an HTTP PATCH request, optionally with the given `payload`.
|
|
287
|
+
*
|
|
288
|
+
* @param {String} url
|
|
289
|
+
* @param {Object} payload
|
|
290
|
+
*
|
|
291
|
+
* @returns {HttpResponse}
|
|
292
|
+
*
|
|
293
|
+
* @throws
|
|
294
|
+
*/
|
|
295
|
+
async patch<R>(url: string, payload?: any): Promise<HttpResponse<R>> {
|
|
296
|
+
if (payload) {
|
|
297
|
+
this.payload(payload);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
return this.send<R>('PATCH', url);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
/**
|
|
304
|
+
* Send an HTTP DELETE request, optionally with the given `queryParams`.
|
|
305
|
+
*
|
|
306
|
+
* @param {String} url
|
|
307
|
+
* @param {Object} queryParams
|
|
308
|
+
*
|
|
309
|
+
* @returns {HttpResponse}
|
|
310
|
+
*
|
|
311
|
+
* @throws
|
|
312
|
+
*/
|
|
313
|
+
async delete<R>(url: string, queryParams: object = {}): Promise<HttpResponse<R>> {
|
|
314
|
+
this.queryParams(queryParams);
|
|
315
|
+
|
|
316
|
+
return this.send<R>('DELETE', url);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Send the HTTP request.
|
|
321
|
+
*
|
|
322
|
+
* @param {String} method
|
|
323
|
+
* @param {String} url
|
|
324
|
+
*
|
|
325
|
+
* @returns {HttpResponse}
|
|
326
|
+
*
|
|
327
|
+
* @throws
|
|
328
|
+
*/
|
|
329
|
+
async send<R>(method: HttpMethod, url: string): Promise<HttpResponse<R>> {
|
|
330
|
+
try {
|
|
331
|
+
return new HttpResponse<R>(await this.createAndSendRequest(method, url));
|
|
332
|
+
} catch (error: any) {
|
|
333
|
+
if (error.response) {
|
|
334
|
+
return new HttpResponse(error.response);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
throw error;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Create and send the HTTP request.
|
|
343
|
+
*
|
|
344
|
+
* @param {String} method
|
|
345
|
+
* @param {String} url
|
|
346
|
+
*
|
|
347
|
+
* @returns {Request}
|
|
348
|
+
*/
|
|
349
|
+
async createAndSendRequest(method: HttpMethod, url: string): Promise<AxiosResponse> {
|
|
350
|
+
return await this.axiosInstance({
|
|
351
|
+
url,
|
|
352
|
+
method,
|
|
353
|
+
withCredentials: true,
|
|
354
|
+
...this.request,
|
|
355
|
+
data: this.prepareRequestPayload(),
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Returns the request payload depending on the selected request payload format.
|
|
361
|
+
*/
|
|
362
|
+
prepareRequestPayload(): any {
|
|
363
|
+
return this.bodyFormat === 'formParams' ? new URLSearchParams(this.request.data).toString() : this.request.data;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
type BodyFormat = 'json' | 'formParams';
|
|
368
|
+
|
|
369
|
+
type HttpMethod = 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'PATCH' | 'OPTIONS';
|