@redocly/cli 1.33.1 → 1.34.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/CHANGELOG.md +17 -0
- package/README.md +1 -1
- package/lib/__tests__/utils.test.js +90 -33
- package/lib/utils/miscellaneous.d.ts +4 -2
- package/lib/utils/miscellaneous.js +28 -16
- package/package.json +3 -3
- package/src/__tests__/utils.test.ts +106 -40
- package/src/utils/miscellaneous.ts +33 -16
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @redocly/cli
|
|
2
2
|
|
|
3
|
+
## 1.34.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Improved OpenTelemetry data serialization.
|
|
8
|
+
- Updated @redocly/respect-core to v1.34.1.
|
|
9
|
+
|
|
10
|
+
## 1.34.0
|
|
11
|
+
|
|
12
|
+
### Minor Changes
|
|
13
|
+
|
|
14
|
+
- Added global execution timeout timer to `respect` command execution to prevent infinite test runs. You can configure this timer using the `RESPECT_TIMEOUT` environment variable (defaults to 1 hour).
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Updated @redocly/respect-core to v1.34.0.
|
|
19
|
+
|
|
3
20
|
## 1.33.1
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ Supports OpenAPI 3.1, 3.0 and OpenAPI 2.0 (legacy Swagger), AsyncAPI 3.0 and 2.6
|
|
|
16
16
|
### Node
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
|
-
npx @redocly/cli lint path-to-root-file.yaml
|
|
19
|
+
npx @redocly/cli@latest lint path-to-root-file.yaml
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
Alternatively, install it globally with `npm`:
|
|
@@ -441,40 +441,51 @@ describe('cleanArgs', () => {
|
|
|
441
441
|
afterEach(() => {
|
|
442
442
|
jest.clearAllMocks();
|
|
443
443
|
});
|
|
444
|
-
it('should remove potentially sensitive data from args', () => {
|
|
445
|
-
const
|
|
444
|
+
it('should remove potentially sensitive data from parsed args', () => {
|
|
445
|
+
const parsedArgs = {
|
|
446
446
|
config: './fixtures/redocly.yaml',
|
|
447
447
|
apis: ['main@v1', 'fixtures/openapi.yaml', 'http://some.url/openapi.yaml'],
|
|
448
448
|
format: 'codeframe',
|
|
449
|
+
input: 'some-input',
|
|
450
|
+
'client-cert': 'some-client-cert',
|
|
451
|
+
'client-key': 'some-client-key',
|
|
452
|
+
'ca-cert': 'some-ca-cert',
|
|
449
453
|
};
|
|
450
|
-
|
|
454
|
+
const rawArgs = [
|
|
455
|
+
'redocly',
|
|
456
|
+
'bundle',
|
|
457
|
+
'main@v1',
|
|
458
|
+
'fixtures/openapi.yaml',
|
|
459
|
+
'http://some.url/openapi.yaml',
|
|
460
|
+
'--config=fixtures/redocly.yaml',
|
|
461
|
+
'--format=codeframe',
|
|
462
|
+
'--input=some-input',
|
|
463
|
+
'--client-cert=some-client-cert',
|
|
464
|
+
'--client-key=some-client-key',
|
|
465
|
+
'--ca-cert=some-ca-cert',
|
|
466
|
+
];
|
|
467
|
+
const result = (0, miscellaneous_1.cleanArgs)(parsedArgs, rawArgs);
|
|
468
|
+
expect(result.arguments).toEqual(JSON.stringify({
|
|
451
469
|
config: 'file-yaml',
|
|
452
470
|
apis: ['api-name@api-version', 'file-yaml', 'http://url'],
|
|
453
471
|
format: 'codeframe',
|
|
454
|
-
|
|
472
|
+
input: '***',
|
|
473
|
+
'client-cert': '***',
|
|
474
|
+
'client-key': '***',
|
|
475
|
+
'ca-cert': '***',
|
|
476
|
+
}));
|
|
455
477
|
});
|
|
456
478
|
it('should remove potentially sensitive data from a push destination', () => {
|
|
457
|
-
const
|
|
479
|
+
const parsedArgs = {
|
|
458
480
|
destination: '@org/name@version',
|
|
459
481
|
};
|
|
460
|
-
|
|
482
|
+
const rawArgs = ['redocly', 'push', '--destination=@org/name@version'];
|
|
483
|
+
const result = (0, miscellaneous_1.cleanArgs)(parsedArgs, rawArgs);
|
|
484
|
+
expect(result.arguments).toEqual(JSON.stringify({
|
|
461
485
|
destination: '@organization/api-name@api-version',
|
|
462
|
-
});
|
|
486
|
+
}));
|
|
463
487
|
});
|
|
464
|
-
|
|
465
|
-
describe('cleanRawInput', () => {
|
|
466
|
-
beforeEach(() => {
|
|
467
|
-
// @ts-ignore
|
|
468
|
-
openapi_core_1.isAbsoluteUrl = jest.requireActual('@redocly/openapi-core').isAbsoluteUrl;
|
|
469
|
-
// @ts-ignore
|
|
470
|
-
fs_1.existsSync = (value) => jest.requireActual('fs').existsSync(path.resolve(__dirname, value));
|
|
471
|
-
// @ts-ignore
|
|
472
|
-
fs_1.statSync = (value) => jest.requireActual('fs').statSync(path.resolve(__dirname, value));
|
|
473
|
-
});
|
|
474
|
-
afterEach(() => {
|
|
475
|
-
jest.clearAllMocks();
|
|
476
|
-
});
|
|
477
|
-
it('should remove potentially sensitive data from raw CLI input', () => {
|
|
488
|
+
it('should remove potentially sensitive data from raw CLI input', () => {
|
|
478
489
|
const rawInput = [
|
|
479
490
|
'redocly',
|
|
480
491
|
'bundle',
|
|
@@ -484,8 +495,41 @@ describe('cleanRawInput', () => {
|
|
|
484
495
|
'--config=fixtures/redocly.yaml',
|
|
485
496
|
'--output',
|
|
486
497
|
'fixtures',
|
|
498
|
+
'--client-cert',
|
|
499
|
+
'fixtures/client-cert.pem',
|
|
500
|
+
'--client-key',
|
|
501
|
+
'fixtures/client-key.pem',
|
|
502
|
+
'--ca-cert',
|
|
503
|
+
'fixtures/ca-cert.pem',
|
|
504
|
+
'--organization',
|
|
505
|
+
'my-org',
|
|
506
|
+
'--input',
|
|
507
|
+
'timeout=10000',
|
|
508
|
+
'--input',
|
|
509
|
+
'{"apiKey":"some=111=1111"}',
|
|
487
510
|
];
|
|
488
|
-
|
|
511
|
+
const parsedArgs = {
|
|
512
|
+
apis: ['./fixtures/openapi.yaml', 'http://some.url/openapi.yaml'],
|
|
513
|
+
input: ['timeout=10000', '{"apiKey":"some=111=1111"}'],
|
|
514
|
+
organization: 'my-org',
|
|
515
|
+
'client-cert': 'fixtures/client-cert.pem',
|
|
516
|
+
'client-key': 'fixtures/client-key.pem',
|
|
517
|
+
'ca-cert': 'fixtures/ca-cert.pem',
|
|
518
|
+
config: 'fixtures/redocly.yaml',
|
|
519
|
+
output: 'fixtures',
|
|
520
|
+
};
|
|
521
|
+
const result = (0, miscellaneous_1.cleanArgs)(parsedArgs, rawInput);
|
|
522
|
+
expect(result.raw_input).toEqual('redocly bundle api-name@api-version file-yaml http://url --config=file-yaml --output folder --client-cert *** --client-key *** --ca-cert *** --organization *** --input *** --input ***');
|
|
523
|
+
expect(result.arguments).toEqual(JSON.stringify({
|
|
524
|
+
apis: ['file-yaml', 'http://url'],
|
|
525
|
+
input: '***',
|
|
526
|
+
organization: '***',
|
|
527
|
+
'client-cert': '***',
|
|
528
|
+
'client-key': '***',
|
|
529
|
+
'ca-cert': '***',
|
|
530
|
+
config: 'file-yaml',
|
|
531
|
+
output: 'folder',
|
|
532
|
+
}));
|
|
489
533
|
});
|
|
490
534
|
it('should preserve safe data from raw CLI input', () => {
|
|
491
535
|
const rawInput = [
|
|
@@ -498,18 +542,31 @@ describe('cleanRawInput', () => {
|
|
|
498
542
|
'--skip-rule',
|
|
499
543
|
'operation-4xx-response',
|
|
500
544
|
];
|
|
501
|
-
|
|
545
|
+
const parsedArgs = {
|
|
546
|
+
apis: ['./fixtures/openapi.json'],
|
|
547
|
+
format: 'stylish',
|
|
548
|
+
extends: 'minimal',
|
|
549
|
+
'skip-rule': ['operation-4xx-response'],
|
|
550
|
+
};
|
|
551
|
+
const result = (0, miscellaneous_1.cleanArgs)(parsedArgs, rawInput);
|
|
552
|
+
expect(result.raw_input).toEqual('redocly lint file-json --format stylish --extends=minimal --skip-rule operation-4xx-response');
|
|
553
|
+
expect(result.arguments).toEqual(JSON.stringify({
|
|
554
|
+
apis: ['file-json'],
|
|
555
|
+
format: 'stylish',
|
|
556
|
+
extends: 'minimal',
|
|
557
|
+
'skip-rule': ['operation-4xx-response'],
|
|
558
|
+
}));
|
|
502
559
|
});
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
560
|
+
});
|
|
561
|
+
describe('validateFileExtension', () => {
|
|
562
|
+
it('should return current file extension', () => {
|
|
563
|
+
expect((0, miscellaneous_1.getAndValidateFileExtension)('test.json')).toEqual('json');
|
|
564
|
+
});
|
|
565
|
+
it('should return yaml and print warning if file extension does not supported', () => {
|
|
566
|
+
const stderrMock = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
567
|
+
colorette_1.yellow.mockImplementation((text) => text);
|
|
568
|
+
expect((0, miscellaneous_1.getAndValidateFileExtension)('test.xml')).toEqual('yaml');
|
|
569
|
+
expect(stderrMock).toHaveBeenCalledWith(`Unsupported file extension: xml. Using yaml.\n`);
|
|
513
570
|
});
|
|
514
571
|
});
|
|
515
572
|
describe('writeToFileByExtension', () => {
|
|
@@ -75,8 +75,10 @@ export type Analytics = {
|
|
|
75
75
|
spec_keyword?: string;
|
|
76
76
|
spec_full_version?: string;
|
|
77
77
|
};
|
|
78
|
-
export declare function cleanArgs(
|
|
79
|
-
|
|
78
|
+
export declare function cleanArgs(parsedArgs: CommandOptions, rawArgv: string[]): {
|
|
79
|
+
arguments: string;
|
|
80
|
+
raw_input: string;
|
|
81
|
+
};
|
|
80
82
|
export declare function checkForDeprecatedOptions<T>(argv: T, deprecatedOptions: Array<keyof T>): void;
|
|
81
83
|
export declare function notifyAboutIncompatibleConfigOptions(themeOpenapiOptions: Record<string, unknown> | undefined): void;
|
|
82
84
|
export declare function formatPath(path: string): string;
|
|
@@ -28,7 +28,6 @@ exports.checkIfRulesetExist = checkIfRulesetExist;
|
|
|
28
28
|
exports.cleanColors = cleanColors;
|
|
29
29
|
exports.sendTelemetry = sendTelemetry;
|
|
30
30
|
exports.cleanArgs = cleanArgs;
|
|
31
|
-
exports.cleanRawInput = cleanRawInput;
|
|
32
31
|
exports.checkForDeprecatedOptions = checkForDeprecatedOptions;
|
|
33
32
|
exports.notifyAboutIncompatibleConfigOptions = notifyAboutIncompatibleConfigOptions;
|
|
34
33
|
exports.formatPath = formatPath;
|
|
@@ -458,7 +457,7 @@ async function sendTelemetry(argv, exit_code, has_config, spec_version, spec_key
|
|
|
458
457
|
event_time,
|
|
459
458
|
logged_in: logged_in ? 'yes' : 'no',
|
|
460
459
|
command: `${command}`,
|
|
461
|
-
|
|
460
|
+
...cleanArgs(args, process.argv.slice(2)),
|
|
462
461
|
node_version: process.version,
|
|
463
462
|
npm_version: (0, child_process_1.execSync)('npm -v').toString().replace('\n', ''),
|
|
464
463
|
os_platform: os.platform(),
|
|
@@ -466,7 +465,6 @@ async function sendTelemetry(argv, exit_code, has_config, spec_version, spec_key
|
|
|
466
465
|
exit_code,
|
|
467
466
|
environment: process.env.REDOCLY_ENVIRONMENT,
|
|
468
467
|
environment_ci: process.env.CI,
|
|
469
|
-
raw_input: cleanRawInput(process.argv.slice(2)),
|
|
470
468
|
has_config: has_config ? 'yes' : 'no',
|
|
471
469
|
spec_version,
|
|
472
470
|
spec_keyword,
|
|
@@ -504,27 +502,41 @@ function cleanString(value) {
|
|
|
504
502
|
}
|
|
505
503
|
return value;
|
|
506
504
|
}
|
|
507
|
-
function
|
|
508
|
-
const
|
|
509
|
-
const
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
505
|
+
function replaceArgs(commandInput, targets, replacement) {
|
|
506
|
+
const targetValues = Array.isArray(targets) ? targets : [targets];
|
|
507
|
+
for (const target of targetValues) {
|
|
508
|
+
commandInput = commandInput.replaceAll(target, replacement);
|
|
509
|
+
}
|
|
510
|
+
return commandInput;
|
|
511
|
+
}
|
|
512
|
+
function cleanArgs(parsedArgs, rawArgv) {
|
|
513
|
+
const KEYS_TO_CLEAN = ['organization', 'o', 'input', 'i', 'client-cert', 'client-key', 'ca-cert'];
|
|
514
|
+
let commandInput = rawArgv.join(' ');
|
|
515
|
+
const commandArguments = {};
|
|
516
|
+
for (const [key, value] of Object.entries(parsedArgs)) {
|
|
517
|
+
if (KEYS_TO_CLEAN.includes(key)) {
|
|
518
|
+
commandArguments[key] = '***';
|
|
519
|
+
commandInput = replaceArgs(commandInput, value, '***');
|
|
513
520
|
}
|
|
514
521
|
else if (typeof value === 'string') {
|
|
515
|
-
|
|
522
|
+
const cleanedValue = cleanString(value);
|
|
523
|
+
commandArguments[key] = cleanedValue;
|
|
524
|
+
commandInput = replaceArgs(commandInput, value, cleanedValue);
|
|
516
525
|
}
|
|
517
526
|
else if (Array.isArray(value)) {
|
|
518
|
-
|
|
527
|
+
commandArguments[key] = value.map(cleanString);
|
|
528
|
+
for (const replacedValue of value) {
|
|
529
|
+
const newValue = cleanString(replacedValue);
|
|
530
|
+
if (commandInput.includes(replacedValue)) {
|
|
531
|
+
commandInput = commandInput.replaceAll(replacedValue, newValue);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
519
534
|
}
|
|
520
535
|
else {
|
|
521
|
-
|
|
536
|
+
commandArguments[key] = value;
|
|
522
537
|
}
|
|
523
538
|
}
|
|
524
|
-
return
|
|
525
|
-
}
|
|
526
|
-
function cleanRawInput(argv) {
|
|
527
|
-
return argv.map((entry) => entry.split('=').map(cleanString).join('=')).join(' ');
|
|
539
|
+
return { arguments: JSON.stringify(commandArguments), raw_input: commandInput };
|
|
528
540
|
}
|
|
529
541
|
function checkForDeprecatedOptions(argv, deprecatedOptions) {
|
|
530
542
|
for (const option of deprecatedOptions) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redocly/cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.34.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
],
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@redocly/config": "^0.22.0",
|
|
41
|
-
"@redocly/openapi-core": "1.
|
|
42
|
-
"@redocly/respect-core": "1.
|
|
41
|
+
"@redocly/openapi-core": "1.34.1",
|
|
42
|
+
"@redocly/respect-core": "1.34.1",
|
|
43
43
|
"abort-controller": "^3.0.0",
|
|
44
44
|
"chokidar": "^3.5.1",
|
|
45
45
|
"colorette": "^1.2.0",
|
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
cleanColors,
|
|
12
12
|
HandledError,
|
|
13
13
|
cleanArgs,
|
|
14
|
-
cleanRawInput,
|
|
15
14
|
getAndValidateFileExtension,
|
|
16
15
|
writeToFileByExtension,
|
|
17
16
|
} from '../utils/miscellaneous';
|
|
@@ -28,7 +27,6 @@ import { blue, red, yellow } from 'colorette';
|
|
|
28
27
|
import { existsSync, statSync } from 'fs';
|
|
29
28
|
import * as path from 'path';
|
|
30
29
|
import * as process from 'process';
|
|
31
|
-
import { ConfigApis } from '../types';
|
|
32
30
|
|
|
33
31
|
jest.mock('os');
|
|
34
32
|
jest.mock('colorette');
|
|
@@ -540,41 +538,56 @@ describe('cleanArgs', () => {
|
|
|
540
538
|
afterEach(() => {
|
|
541
539
|
jest.clearAllMocks();
|
|
542
540
|
});
|
|
543
|
-
it('should remove potentially sensitive data from args', () => {
|
|
544
|
-
const
|
|
541
|
+
it('should remove potentially sensitive data from parsed args', () => {
|
|
542
|
+
const parsedArgs = {
|
|
545
543
|
config: './fixtures/redocly.yaml',
|
|
546
544
|
apis: ['main@v1', 'fixtures/openapi.yaml', 'http://some.url/openapi.yaml'],
|
|
547
545
|
format: 'codeframe',
|
|
546
|
+
input: 'some-input',
|
|
547
|
+
'client-cert': 'some-client-cert',
|
|
548
|
+
'client-key': 'some-client-key',
|
|
549
|
+
'ca-cert': 'some-ca-cert',
|
|
548
550
|
};
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
551
|
+
const rawArgs = [
|
|
552
|
+
'redocly',
|
|
553
|
+
'bundle',
|
|
554
|
+
'main@v1',
|
|
555
|
+
'fixtures/openapi.yaml',
|
|
556
|
+
'http://some.url/openapi.yaml',
|
|
557
|
+
'--config=fixtures/redocly.yaml',
|
|
558
|
+
'--format=codeframe',
|
|
559
|
+
'--input=some-input',
|
|
560
|
+
'--client-cert=some-client-cert',
|
|
561
|
+
'--client-key=some-client-key',
|
|
562
|
+
'--ca-cert=some-ca-cert',
|
|
563
|
+
];
|
|
564
|
+
const result = cleanArgs(parsedArgs, rawArgs);
|
|
565
|
+
expect(result.arguments).toEqual(
|
|
566
|
+
JSON.stringify({
|
|
567
|
+
config: 'file-yaml',
|
|
568
|
+
apis: ['api-name@api-version', 'file-yaml', 'http://url'],
|
|
569
|
+
format: 'codeframe',
|
|
570
|
+
input: '***',
|
|
571
|
+
'client-cert': '***',
|
|
572
|
+
'client-key': '***',
|
|
573
|
+
'ca-cert': '***',
|
|
574
|
+
})
|
|
575
|
+
);
|
|
554
576
|
});
|
|
555
577
|
it('should remove potentially sensitive data from a push destination', () => {
|
|
556
|
-
const
|
|
578
|
+
const parsedArgs = {
|
|
557
579
|
destination: '@org/name@version',
|
|
558
580
|
};
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
581
|
+
const rawArgs = ['redocly', 'push', '--destination=@org/name@version'];
|
|
582
|
+
const result = cleanArgs(parsedArgs, rawArgs);
|
|
583
|
+
expect(result.arguments).toEqual(
|
|
584
|
+
JSON.stringify({
|
|
585
|
+
destination: '@organization/api-name@api-version',
|
|
586
|
+
})
|
|
587
|
+
);
|
|
562
588
|
});
|
|
563
|
-
});
|
|
564
589
|
|
|
565
|
-
|
|
566
|
-
beforeEach(() => {
|
|
567
|
-
// @ts-ignore
|
|
568
|
-
isAbsoluteUrl = jest.requireActual('@redocly/openapi-core').isAbsoluteUrl;
|
|
569
|
-
// @ts-ignore
|
|
570
|
-
existsSync = (value) => jest.requireActual('fs').existsSync(path.resolve(__dirname, value));
|
|
571
|
-
// @ts-ignore
|
|
572
|
-
statSync = (value) => jest.requireActual('fs').statSync(path.resolve(__dirname, value));
|
|
573
|
-
});
|
|
574
|
-
afterEach(() => {
|
|
575
|
-
jest.clearAllMocks();
|
|
576
|
-
});
|
|
577
|
-
it('should remove potentially sensitive data from raw CLI input', () => {
|
|
590
|
+
it('should remove potentially sensitive data from raw CLI input', () => {
|
|
578
591
|
const rawInput = [
|
|
579
592
|
'redocly',
|
|
580
593
|
'bundle',
|
|
@@ -584,11 +597,47 @@ describe('cleanRawInput', () => {
|
|
|
584
597
|
'--config=fixtures/redocly.yaml',
|
|
585
598
|
'--output',
|
|
586
599
|
'fixtures',
|
|
600
|
+
'--client-cert',
|
|
601
|
+
'fixtures/client-cert.pem',
|
|
602
|
+
'--client-key',
|
|
603
|
+
'fixtures/client-key.pem',
|
|
604
|
+
'--ca-cert',
|
|
605
|
+
'fixtures/ca-cert.pem',
|
|
606
|
+
'--organization',
|
|
607
|
+
'my-org',
|
|
608
|
+
'--input',
|
|
609
|
+
'timeout=10000',
|
|
610
|
+
'--input',
|
|
611
|
+
'{"apiKey":"some=111=1111"}',
|
|
587
612
|
];
|
|
588
|
-
|
|
589
|
-
'
|
|
613
|
+
const parsedArgs = {
|
|
614
|
+
apis: ['./fixtures/openapi.yaml', 'http://some.url/openapi.yaml'],
|
|
615
|
+
input: ['timeout=10000', '{"apiKey":"some=111=1111"}'],
|
|
616
|
+
organization: 'my-org',
|
|
617
|
+
'client-cert': 'fixtures/client-cert.pem',
|
|
618
|
+
'client-key': 'fixtures/client-key.pem',
|
|
619
|
+
'ca-cert': 'fixtures/ca-cert.pem',
|
|
620
|
+
config: 'fixtures/redocly.yaml',
|
|
621
|
+
output: 'fixtures',
|
|
622
|
+
};
|
|
623
|
+
const result = cleanArgs(parsedArgs, rawInput);
|
|
624
|
+
expect(result.raw_input).toEqual(
|
|
625
|
+
'redocly bundle api-name@api-version file-yaml http://url --config=file-yaml --output folder --client-cert *** --client-key *** --ca-cert *** --organization *** --input *** --input ***'
|
|
626
|
+
);
|
|
627
|
+
expect(result.arguments).toEqual(
|
|
628
|
+
JSON.stringify({
|
|
629
|
+
apis: ['file-yaml', 'http://url'],
|
|
630
|
+
input: '***',
|
|
631
|
+
organization: '***',
|
|
632
|
+
'client-cert': '***',
|
|
633
|
+
'client-key': '***',
|
|
634
|
+
'ca-cert': '***',
|
|
635
|
+
config: 'file-yaml',
|
|
636
|
+
output: 'folder',
|
|
637
|
+
})
|
|
590
638
|
);
|
|
591
639
|
});
|
|
640
|
+
|
|
592
641
|
it('should preserve safe data from raw CLI input', () => {
|
|
593
642
|
const rawInput = [
|
|
594
643
|
'redocly',
|
|
@@ -600,23 +649,40 @@ describe('cleanRawInput', () => {
|
|
|
600
649
|
'--skip-rule',
|
|
601
650
|
'operation-4xx-response',
|
|
602
651
|
];
|
|
603
|
-
|
|
652
|
+
const parsedArgs = {
|
|
653
|
+
apis: ['./fixtures/openapi.json'],
|
|
654
|
+
format: 'stylish',
|
|
655
|
+
extends: 'minimal',
|
|
656
|
+
'skip-rule': ['operation-4xx-response'],
|
|
657
|
+
};
|
|
658
|
+
const result = cleanArgs(parsedArgs, rawInput);
|
|
659
|
+
|
|
660
|
+
expect(result.raw_input).toEqual(
|
|
604
661
|
'redocly lint file-json --format stylish --extends=minimal --skip-rule operation-4xx-response'
|
|
605
662
|
);
|
|
663
|
+
|
|
664
|
+
expect(result.arguments).toEqual(
|
|
665
|
+
JSON.stringify({
|
|
666
|
+
apis: ['file-json'],
|
|
667
|
+
format: 'stylish',
|
|
668
|
+
extends: 'minimal',
|
|
669
|
+
'skip-rule': ['operation-4xx-response'],
|
|
670
|
+
})
|
|
671
|
+
);
|
|
606
672
|
});
|
|
673
|
+
});
|
|
607
674
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
675
|
+
describe('validateFileExtension', () => {
|
|
676
|
+
it('should return current file extension', () => {
|
|
677
|
+
expect(getAndValidateFileExtension('test.json')).toEqual('json');
|
|
678
|
+
});
|
|
612
679
|
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
680
|
+
it('should return yaml and print warning if file extension does not supported', () => {
|
|
681
|
+
const stderrMock = jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
|
682
|
+
(yellow as jest.Mock<any, any>).mockImplementation((text: string) => text);
|
|
616
683
|
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
});
|
|
684
|
+
expect(getAndValidateFileExtension('test.xml')).toEqual('yaml');
|
|
685
|
+
expect(stderrMock).toHaveBeenCalledWith(`Unsupported file extension: xml. Using yaml.\n`);
|
|
620
686
|
});
|
|
621
687
|
});
|
|
622
688
|
|
|
@@ -575,7 +575,7 @@ export async function sendTelemetry(
|
|
|
575
575
|
event_time,
|
|
576
576
|
logged_in: logged_in ? 'yes' : 'no',
|
|
577
577
|
command: `${command}`,
|
|
578
|
-
|
|
578
|
+
...cleanArgs(args, process.argv.slice(2)),
|
|
579
579
|
node_version: process.version,
|
|
580
580
|
npm_version: execSync('npm -v').toString().replace('\n', ''),
|
|
581
581
|
os_platform: os.platform(),
|
|
@@ -583,7 +583,6 @@ export async function sendTelemetry(
|
|
|
583
583
|
exit_code,
|
|
584
584
|
environment: process.env.REDOCLY_ENVIRONMENT,
|
|
585
585
|
environment_ci: process.env.CI,
|
|
586
|
-
raw_input: cleanRawInput(process.argv.slice(2)),
|
|
587
586
|
has_config: has_config ? 'yes' : 'no',
|
|
588
587
|
spec_version,
|
|
589
588
|
spec_keyword,
|
|
@@ -627,7 +626,7 @@ function isDirectory(value: string) {
|
|
|
627
626
|
return fs.existsSync(value) && fs.statSync(value).isDirectory();
|
|
628
627
|
}
|
|
629
628
|
|
|
630
|
-
function cleanString(value
|
|
629
|
+
function cleanString(value: string): string {
|
|
631
630
|
if (!value) {
|
|
632
631
|
return value;
|
|
633
632
|
}
|
|
@@ -646,26 +645,44 @@ function cleanString(value?: string): string | undefined {
|
|
|
646
645
|
return value;
|
|
647
646
|
}
|
|
648
647
|
|
|
649
|
-
|
|
650
|
-
|
|
648
|
+
function replaceArgs(
|
|
649
|
+
commandInput: string,
|
|
650
|
+
targets: string | string[],
|
|
651
|
+
replacement: string
|
|
652
|
+
): string {
|
|
653
|
+
const targetValues = Array.isArray(targets) ? targets : [targets];
|
|
654
|
+
for (const target of targetValues) {
|
|
655
|
+
commandInput = commandInput.replaceAll(target, replacement);
|
|
656
|
+
}
|
|
657
|
+
return commandInput;
|
|
658
|
+
}
|
|
651
659
|
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
660
|
+
export function cleanArgs(parsedArgs: CommandOptions, rawArgv: string[]) {
|
|
661
|
+
const KEYS_TO_CLEAN = ['organization', 'o', 'input', 'i', 'client-cert', 'client-key', 'ca-cert'];
|
|
662
|
+
let commandInput = rawArgv.join(' ');
|
|
663
|
+
const commandArguments: Record<string, string | string[]> = {};
|
|
664
|
+
for (const [key, value] of Object.entries(parsedArgs)) {
|
|
665
|
+
if (KEYS_TO_CLEAN.includes(key)) {
|
|
666
|
+
commandArguments[key] = '***';
|
|
667
|
+
commandInput = replaceArgs(commandInput, value, '***');
|
|
656
668
|
} else if (typeof value === 'string') {
|
|
657
|
-
|
|
669
|
+
const cleanedValue = cleanString(value);
|
|
670
|
+
commandArguments[key] = cleanedValue;
|
|
671
|
+
commandInput = replaceArgs(commandInput, value, cleanedValue);
|
|
658
672
|
} else if (Array.isArray(value)) {
|
|
659
|
-
|
|
673
|
+
commandArguments[key] = value.map(cleanString);
|
|
674
|
+
for (const replacedValue of value) {
|
|
675
|
+
const newValue = cleanString(replacedValue);
|
|
676
|
+
if (commandInput.includes(replacedValue)) {
|
|
677
|
+
commandInput = commandInput.replaceAll(replacedValue, newValue);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
660
680
|
} else {
|
|
661
|
-
|
|
681
|
+
commandArguments[key] = value;
|
|
662
682
|
}
|
|
663
683
|
}
|
|
664
|
-
return result;
|
|
665
|
-
}
|
|
666
684
|
|
|
667
|
-
|
|
668
|
-
return argv.map((entry) => entry.split('=').map(cleanString).join('=')).join(' ');
|
|
685
|
+
return { arguments: JSON.stringify(commandArguments), raw_input: commandInput };
|
|
669
686
|
}
|
|
670
687
|
|
|
671
688
|
export function checkForDeprecatedOptions<T>(argv: T, deprecatedOptions: Array<keyof T>) {
|