mirror of
https://github.com/LukeHagar/redocly-cli.git
synced 2025-12-06 04:21:09 +00:00
chore: aggregate data regarding API types and versions usage (#1580)
This commit is contained in:
@@ -16,6 +16,8 @@ When a command is run, the following data is collected:
|
||||
- values from `REDOCLY_ENVIRONMENT`
|
||||
- CLI version
|
||||
- Node.js and NPM versions
|
||||
- whether the `redocly.yaml` configuration file exists
|
||||
- API specification version
|
||||
|
||||
Values such as file names, organization IDs, and URLs are removed, replaced by just "URL" or "file", etc.
|
||||
|
||||
|
||||
@@ -39,8 +39,8 @@ describe('build-docs', () => {
|
||||
|
||||
it('should work correctly when calling handlerBuildCommand', async () => {
|
||||
const processExitMock = jest.spyOn(process, 'exit').mockImplementation();
|
||||
await handlerBuildCommand(
|
||||
{
|
||||
await handlerBuildCommand({
|
||||
argv: {
|
||||
o: '',
|
||||
title: 'test',
|
||||
disableGoogleFont: false,
|
||||
@@ -49,8 +49,9 @@ describe('build-docs', () => {
|
||||
theme: { openapi: {} },
|
||||
api: '../some-path/openapi.yaml',
|
||||
} as BuildDocsArgv,
|
||||
{} as any
|
||||
);
|
||||
config: {} as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
expect(loadAndBundleSpec).toBeCalledTimes(1);
|
||||
expect(getFallbackApisOrExit).toBeCalledTimes(1);
|
||||
expect(processExitMock).toBeCalledTimes(0);
|
||||
|
||||
@@ -14,21 +14,21 @@ describe('handleJoin', () => {
|
||||
colloreteYellowMock.mockImplementation((string: string) => string);
|
||||
|
||||
it('should call exitWithError because only one entrypoint', async () => {
|
||||
await handleJoin({ apis: ['first.yaml'] }, {} as any, 'cli-version');
|
||||
await handleJoin({ argv: { apis: ['first.yaml'] }, config: {} as any, version: 'cli-version' });
|
||||
expect(exitWithError).toHaveBeenCalledWith(`At least 2 apis should be provided.`);
|
||||
});
|
||||
|
||||
it('should call exitWithError because passed all 3 options for tags', async () => {
|
||||
await handleJoin(
|
||||
{
|
||||
await handleJoin({
|
||||
argv: {
|
||||
apis: ['first.yaml', 'second.yaml'],
|
||||
'prefix-tags-with-info-prop': 'something',
|
||||
'without-x-tag-groups': true,
|
||||
'prefix-tags-with-filename': true,
|
||||
},
|
||||
{} as any,
|
||||
'cli-version'
|
||||
);
|
||||
config: {} as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(exitWithError).toHaveBeenCalledWith(
|
||||
`You use prefix-tags-with-filename, prefix-tags-with-info-prop, without-x-tag-groups together.\nPlease choose only one!`
|
||||
@@ -36,15 +36,15 @@ describe('handleJoin', () => {
|
||||
});
|
||||
|
||||
it('should call exitWithError because passed all 2 options for tags', async () => {
|
||||
await handleJoin(
|
||||
{
|
||||
await handleJoin({
|
||||
argv: {
|
||||
apis: ['first.yaml', 'second.yaml'],
|
||||
'without-x-tag-groups': true,
|
||||
'prefix-tags-with-filename': true,
|
||||
},
|
||||
{} as any,
|
||||
'cli-version'
|
||||
);
|
||||
config: {} as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(exitWithError).toHaveBeenCalledWith(
|
||||
`You use prefix-tags-with-filename, without-x-tag-groups together.\nPlease choose only one!`
|
||||
@@ -52,13 +52,13 @@ describe('handleJoin', () => {
|
||||
});
|
||||
|
||||
it('should call exitWithError because Only OpenAPI 3.0 and OpenAPI 3.1 are supported', async () => {
|
||||
await handleJoin(
|
||||
{
|
||||
await handleJoin({
|
||||
argv: {
|
||||
apis: ['first.yaml', 'second.yaml'],
|
||||
},
|
||||
ConfigFixture as any,
|
||||
'cli-version'
|
||||
);
|
||||
config: ConfigFixture as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
expect(exitWithError).toHaveBeenCalledWith(
|
||||
'Only OpenAPI 3.0 and OpenAPI 3.1 are supported: undefined.'
|
||||
);
|
||||
@@ -68,13 +68,13 @@ describe('handleJoin', () => {
|
||||
(detectSpec as jest.Mock)
|
||||
.mockImplementationOnce(() => 'oas3_0')
|
||||
.mockImplementationOnce(() => 'oas3_1');
|
||||
await handleJoin(
|
||||
{
|
||||
await handleJoin({
|
||||
argv: {
|
||||
apis: ['first.yaml', 'second.yaml'],
|
||||
},
|
||||
ConfigFixture as any,
|
||||
'cli-version'
|
||||
);
|
||||
config: ConfigFixture as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(exitWithError).toHaveBeenCalledWith(
|
||||
'All APIs must use the same OpenAPI version: undefined.'
|
||||
@@ -83,13 +83,13 @@ describe('handleJoin', () => {
|
||||
|
||||
it('should call writeToFileByExtension function', async () => {
|
||||
(detectSpec as jest.Mock).mockReturnValue('oas3_0');
|
||||
await handleJoin(
|
||||
{
|
||||
await handleJoin({
|
||||
argv: {
|
||||
apis: ['first.yaml', 'second.yaml'],
|
||||
},
|
||||
ConfigFixture as any,
|
||||
'cli-version'
|
||||
);
|
||||
config: ConfigFixture as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(writeToFileByExtension).toHaveBeenCalledWith(
|
||||
expect.any(Object),
|
||||
@@ -100,13 +100,13 @@ describe('handleJoin', () => {
|
||||
|
||||
it('should call writeToFileByExtension function for OpenAPI 3.1', async () => {
|
||||
(detectSpec as jest.Mock).mockReturnValue('oas3_1');
|
||||
await handleJoin(
|
||||
{
|
||||
await handleJoin({
|
||||
argv: {
|
||||
apis: ['first.yaml', 'second.yaml'],
|
||||
},
|
||||
ConfigFixture as any,
|
||||
'cli-version'
|
||||
);
|
||||
config: ConfigFixture as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(writeToFileByExtension).toHaveBeenCalledWith(
|
||||
expect.any(Object),
|
||||
@@ -117,14 +117,14 @@ describe('handleJoin', () => {
|
||||
|
||||
it('should call writeToFileByExtension function with custom output file', async () => {
|
||||
(detectSpec as jest.Mock).mockReturnValue('oas3_0');
|
||||
await handleJoin(
|
||||
{
|
||||
await handleJoin({
|
||||
argv: {
|
||||
apis: ['first.yaml', 'second.yaml'],
|
||||
output: 'output.yml',
|
||||
},
|
||||
ConfigFixture as any,
|
||||
'cli-version'
|
||||
);
|
||||
config: ConfigFixture as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(writeToFileByExtension).toHaveBeenCalledWith(
|
||||
expect.any(Object),
|
||||
@@ -135,13 +135,13 @@ describe('handleJoin', () => {
|
||||
|
||||
it('should call writeToFileByExtension function with json file extension', async () => {
|
||||
(detectSpec as jest.Mock).mockReturnValue('oas3_0');
|
||||
await handleJoin(
|
||||
{
|
||||
await handleJoin({
|
||||
argv: {
|
||||
apis: ['first.json', 'second.yaml'],
|
||||
},
|
||||
ConfigFixture as any,
|
||||
'cli-version'
|
||||
);
|
||||
config: ConfigFixture as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(writeToFileByExtension).toHaveBeenCalledWith(
|
||||
expect.any(Object),
|
||||
@@ -152,13 +152,13 @@ describe('handleJoin', () => {
|
||||
|
||||
it('should call skipDecorators and skipPreprocessors', async () => {
|
||||
(detectSpec as jest.Mock).mockReturnValue('oas3_0');
|
||||
await handleJoin(
|
||||
{
|
||||
await handleJoin({
|
||||
argv: {
|
||||
apis: ['first.yaml', 'second.yaml'],
|
||||
},
|
||||
ConfigFixture as any,
|
||||
'cli-version'
|
||||
);
|
||||
config: ConfigFixture as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
const config = loadConfig();
|
||||
expect(config.styleguide.skipDecorators).toHaveBeenCalled();
|
||||
@@ -168,15 +168,15 @@ describe('handleJoin', () => {
|
||||
it('should handle join with prefix-components-with-info-prop and null values', async () => {
|
||||
(detectSpec as jest.Mock).mockReturnValue('oas3_0');
|
||||
|
||||
await handleJoin(
|
||||
{
|
||||
await handleJoin({
|
||||
argv: {
|
||||
apis: ['first.yaml', 'second.yaml', 'third.yaml'],
|
||||
'prefix-components-with-info-prop': 'title',
|
||||
output: 'join-result.yaml',
|
||||
},
|
||||
ConfigFixture as any,
|
||||
'cli-version'
|
||||
);
|
||||
config: ConfigFixture as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(writeToFileByExtension).toHaveBeenCalledWith(
|
||||
{
|
||||
|
||||
@@ -28,30 +28,32 @@ describe('push-with-region', () => {
|
||||
|
||||
it('should call login with default domain when region is US', async () => {
|
||||
redoclyClient.domain = 'redoc.ly';
|
||||
await handlePush(
|
||||
{
|
||||
await handlePush({
|
||||
argv: {
|
||||
upsert: true,
|
||||
api: 'spec.json',
|
||||
destination: '@org/my-api@1.0.0',
|
||||
branchName: 'test',
|
||||
},
|
||||
ConfigFixture as any
|
||||
);
|
||||
config: ConfigFixture as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
expect(mockPromptClientToken).toBeCalledTimes(1);
|
||||
expect(mockPromptClientToken).toHaveBeenCalledWith(redoclyClient.domain);
|
||||
});
|
||||
|
||||
it('should call login with EU domain when region is EU', async () => {
|
||||
redoclyClient.domain = 'eu.redocly.com';
|
||||
await handlePush(
|
||||
{
|
||||
await handlePush({
|
||||
argv: {
|
||||
upsert: true,
|
||||
api: 'spec.json',
|
||||
destination: '@org/my-api@1.0.0',
|
||||
branchName: 'test',
|
||||
},
|
||||
ConfigFixture as any
|
||||
);
|
||||
config: ConfigFixture as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
expect(mockPromptClientToken).toBeCalledTimes(1);
|
||||
expect(mockPromptClientToken).toHaveBeenCalledWith(redoclyClient.domain);
|
||||
});
|
||||
|
||||
@@ -25,8 +25,8 @@ describe('push', () => {
|
||||
});
|
||||
|
||||
it('pushes definition', async () => {
|
||||
await handlePush(
|
||||
{
|
||||
await handlePush({
|
||||
argv: {
|
||||
upsert: true,
|
||||
api: 'spec.json',
|
||||
destination: '@org/my-api@1.0.0',
|
||||
@@ -35,8 +35,9 @@ describe('push', () => {
|
||||
'job-id': '123',
|
||||
'batch-size': 2,
|
||||
},
|
||||
ConfigFixture as any
|
||||
);
|
||||
config: ConfigFixture as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(redoclyClient.registryApi.prepareFileUpload).toBeCalledTimes(1);
|
||||
expect(redoclyClient.registryApi.pushApi).toBeCalledTimes(1);
|
||||
@@ -55,8 +56,8 @@ describe('push', () => {
|
||||
});
|
||||
|
||||
it('fails if jobId value is an empty string', async () => {
|
||||
await handlePush(
|
||||
{
|
||||
await handlePush({
|
||||
argv: {
|
||||
upsert: true,
|
||||
api: 'spec.json',
|
||||
destination: '@org/my-api@1.0.0',
|
||||
@@ -65,15 +66,16 @@ describe('push', () => {
|
||||
'job-id': ' ',
|
||||
'batch-size': 2,
|
||||
},
|
||||
ConfigFixture as any
|
||||
);
|
||||
config: ConfigFixture as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(exitWithError).toBeCalledTimes(1);
|
||||
});
|
||||
|
||||
it('fails if batchSize value is less than 2', async () => {
|
||||
await handlePush(
|
||||
{
|
||||
await handlePush({
|
||||
argv: {
|
||||
upsert: true,
|
||||
api: 'spec.json',
|
||||
destination: '@org/my-api@1.0.0',
|
||||
@@ -82,8 +84,9 @@ describe('push', () => {
|
||||
'job-id': '123',
|
||||
'batch-size': 1,
|
||||
},
|
||||
ConfigFixture as any
|
||||
);
|
||||
config: ConfigFixture as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(exitWithError).toBeCalledTimes(1);
|
||||
});
|
||||
@@ -95,16 +98,17 @@ describe('push', () => {
|
||||
return { isDirectory: () => false, size: 10 };
|
||||
});
|
||||
|
||||
await handlePush(
|
||||
{
|
||||
await handlePush({
|
||||
argv: {
|
||||
upsert: true,
|
||||
api: 'spec.json',
|
||||
destination: '@org/my-api@1.0.0',
|
||||
public: true,
|
||||
files: ['./resouces/1.md', './resouces/2.md'],
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(redoclyClient.registryApi.pushApi).toHaveBeenLastCalledWith({
|
||||
filePaths: ['filePath', 'filePath', 'filePath'],
|
||||
@@ -119,8 +123,8 @@ describe('push', () => {
|
||||
});
|
||||
|
||||
it('push should fail if organization not provided', async () => {
|
||||
await handlePush(
|
||||
{
|
||||
await handlePush({
|
||||
argv: {
|
||||
upsert: true,
|
||||
api: 'spec.json',
|
||||
destination: 'test@v1',
|
||||
@@ -129,8 +133,9 @@ describe('push', () => {
|
||||
'job-id': '123',
|
||||
'batch-size': 2,
|
||||
},
|
||||
ConfigFixture as any
|
||||
);
|
||||
config: ConfigFixture as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(exitWithError).toBeCalledTimes(1);
|
||||
expect(exitWithError).toBeCalledWith(
|
||||
@@ -140,8 +145,8 @@ describe('push', () => {
|
||||
|
||||
it('push should work with organization in config', async () => {
|
||||
const mockConfig = { ...ConfigFixture, organization: 'test_org' } as any;
|
||||
await handlePush(
|
||||
{
|
||||
await handlePush({
|
||||
argv: {
|
||||
upsert: true,
|
||||
api: 'spec.json',
|
||||
destination: 'my-api@1.0.0',
|
||||
@@ -150,8 +155,9 @@ describe('push', () => {
|
||||
'job-id': '123',
|
||||
'batch-size': 2,
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(redoclyClient.registryApi.pushApi).toBeCalledTimes(1);
|
||||
expect(redoclyClient.registryApi.pushApi).toHaveBeenLastCalledWith({
|
||||
@@ -175,16 +181,17 @@ describe('push', () => {
|
||||
apis: { 'my-api@1.0.0': { root: 'path' } },
|
||||
} as any;
|
||||
|
||||
await handlePush(
|
||||
{
|
||||
await handlePush({
|
||||
argv: {
|
||||
upsert: true,
|
||||
branchName: 'test',
|
||||
public: true,
|
||||
'job-id': '123',
|
||||
'batch-size': 2,
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(redoclyClient.registryApi.pushApi).toBeCalledTimes(1);
|
||||
});
|
||||
@@ -192,16 +199,17 @@ describe('push', () => {
|
||||
it('push should fail if apis not provided', async () => {
|
||||
const mockConfig = { organization: 'test_org', apis: {} } as any;
|
||||
|
||||
await handlePush(
|
||||
{
|
||||
await handlePush({
|
||||
argv: {
|
||||
upsert: true,
|
||||
branchName: 'test',
|
||||
public: true,
|
||||
'job-id': '123',
|
||||
'batch-size': 2,
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(exitWithError).toBeCalledTimes(1);
|
||||
expect(exitWithError).toHaveBeenLastCalledWith(
|
||||
@@ -212,8 +220,8 @@ describe('push', () => {
|
||||
it('push should fail if destination not provided', async () => {
|
||||
const mockConfig = { organization: 'test_org', apis: {} } as any;
|
||||
|
||||
await handlePush(
|
||||
{
|
||||
await handlePush({
|
||||
argv: {
|
||||
upsert: true,
|
||||
api: 'api.yaml',
|
||||
branchName: 'test',
|
||||
@@ -221,8 +229,9 @@ describe('push', () => {
|
||||
'job-id': '123',
|
||||
'batch-size': 2,
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(exitWithError).toBeCalledTimes(1);
|
||||
expect(exitWithError).toHaveBeenLastCalledWith(
|
||||
@@ -233,8 +242,8 @@ describe('push', () => {
|
||||
it('push should fail if destination format is not valid', async () => {
|
||||
const mockConfig = { organization: 'test_org', apis: {} } as any;
|
||||
|
||||
await handlePush(
|
||||
{
|
||||
await handlePush({
|
||||
argv: {
|
||||
upsert: true,
|
||||
destination: 'name/v1',
|
||||
branchName: 'test',
|
||||
@@ -242,8 +251,9 @@ describe('push', () => {
|
||||
'job-id': '123',
|
||||
'batch-size': 2,
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(exitWithError).toHaveBeenCalledWith(
|
||||
`Destination argument value is not valid, please use the right format: ${yellow(
|
||||
@@ -261,8 +271,8 @@ describe('push', () => {
|
||||
apis: { 'my test api@v1': { root: 'path' } },
|
||||
} as any;
|
||||
|
||||
await handlePush(
|
||||
{
|
||||
await handlePush({
|
||||
argv: {
|
||||
upsert: true,
|
||||
destination: 'my test api@v1',
|
||||
branchName: 'test',
|
||||
@@ -270,8 +280,9 @@ describe('push', () => {
|
||||
'job-id': '123',
|
||||
'batch-size': 2,
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(encodeURIComponentSpy).toHaveReturnedWith('my%20test%20api');
|
||||
expect(redoclyClient.registryApi.pushApi).toBeCalledTimes(1);
|
||||
@@ -281,130 +292,144 @@ describe('push', () => {
|
||||
describe('transformPush', () => {
|
||||
it('should adapt the existing syntax', () => {
|
||||
const cb = jest.fn();
|
||||
transformPush(cb)(
|
||||
{
|
||||
transformPush(cb)({
|
||||
argv: {
|
||||
apis: ['openapi.yaml', '@testing_org/main@v1'],
|
||||
},
|
||||
{} as any
|
||||
);
|
||||
expect(cb).toBeCalledWith(
|
||||
{
|
||||
config: {} as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
expect(cb).toBeCalledWith({
|
||||
argv: {
|
||||
api: 'openapi.yaml',
|
||||
destination: '@testing_org/main@v1',
|
||||
},
|
||||
{}
|
||||
);
|
||||
config: {},
|
||||
version: 'cli-version',
|
||||
});
|
||||
});
|
||||
it('should adapt the existing syntax (including branchName)', () => {
|
||||
const cb = jest.fn();
|
||||
transformPush(cb)(
|
||||
{
|
||||
transformPush(cb)({
|
||||
argv: {
|
||||
apis: ['openapi.yaml', '@testing_org/main@v1', 'other'],
|
||||
},
|
||||
{} as any
|
||||
);
|
||||
expect(cb).toBeCalledWith(
|
||||
{
|
||||
config: {} as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
expect(cb).toBeCalledWith({
|
||||
argv: {
|
||||
api: 'openapi.yaml',
|
||||
destination: '@testing_org/main@v1',
|
||||
branchName: 'other',
|
||||
},
|
||||
{}
|
||||
);
|
||||
config: {},
|
||||
version: 'cli-version',
|
||||
});
|
||||
});
|
||||
it('should use --branch option firstly', () => {
|
||||
const cb = jest.fn();
|
||||
transformPush(cb)(
|
||||
{
|
||||
transformPush(cb)({
|
||||
argv: {
|
||||
apis: ['openapi.yaml', '@testing_org/main@v1', 'other'],
|
||||
branch: 'priority-branch',
|
||||
},
|
||||
{} as any
|
||||
);
|
||||
expect(cb).toBeCalledWith(
|
||||
{
|
||||
config: {} as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
expect(cb).toBeCalledWith({
|
||||
argv: {
|
||||
api: 'openapi.yaml',
|
||||
destination: '@testing_org/main@v1',
|
||||
branchName: 'priority-branch',
|
||||
},
|
||||
{}
|
||||
);
|
||||
config: {},
|
||||
version: 'cli-version',
|
||||
});
|
||||
});
|
||||
it('should work for a destination only', () => {
|
||||
const cb = jest.fn();
|
||||
transformPush(cb)(
|
||||
{
|
||||
transformPush(cb)({
|
||||
argv: {
|
||||
apis: ['main@v1'],
|
||||
},
|
||||
{} as any
|
||||
);
|
||||
expect(cb).toBeCalledWith(
|
||||
{
|
||||
config: {} as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
expect(cb).toBeCalledWith({
|
||||
argv: {
|
||||
destination: 'main@v1',
|
||||
},
|
||||
{}
|
||||
);
|
||||
config: {},
|
||||
version: 'cli-version',
|
||||
});
|
||||
});
|
||||
it('should work for a api only', () => {
|
||||
const cb = jest.fn();
|
||||
transformPush(cb)(
|
||||
{
|
||||
transformPush(cb)({
|
||||
argv: {
|
||||
apis: ['test.yaml'],
|
||||
},
|
||||
{} as any
|
||||
);
|
||||
expect(cb).toBeCalledWith(
|
||||
{
|
||||
config: {} as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
expect(cb).toBeCalledWith({
|
||||
argv: {
|
||||
api: 'test.yaml',
|
||||
},
|
||||
{}
|
||||
);
|
||||
config: {},
|
||||
version: 'cli-version',
|
||||
});
|
||||
});
|
||||
|
||||
it('should use destination from option', () => {
|
||||
const cb = jest.fn();
|
||||
transformPush(cb)(
|
||||
{
|
||||
transformPush(cb)({
|
||||
argv: {
|
||||
apis: ['test.yaml', 'test@v1'],
|
||||
destination: 'main@v1',
|
||||
},
|
||||
{} as any
|
||||
);
|
||||
expect(cb).toBeCalledWith(
|
||||
{
|
||||
config: {} as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
expect(cb).toBeCalledWith({
|
||||
argv: {
|
||||
destination: 'main@v1',
|
||||
api: 'test.yaml',
|
||||
},
|
||||
{}
|
||||
);
|
||||
config: {},
|
||||
version: 'cli-version',
|
||||
});
|
||||
});
|
||||
|
||||
it('should use --job-id option firstly', () => {
|
||||
const cb = jest.fn();
|
||||
transformPush(cb)(
|
||||
{
|
||||
transformPush(cb)({
|
||||
argv: {
|
||||
'batch-id': 'b-123',
|
||||
'job-id': 'j-123',
|
||||
apis: ['test'],
|
||||
branch: 'test',
|
||||
destination: 'main@v1',
|
||||
},
|
||||
{} as any
|
||||
);
|
||||
expect(cb).toBeCalledWith(
|
||||
{
|
||||
config: {} as any,
|
||||
version: 'cli-version',
|
||||
});
|
||||
expect(cb).toBeCalledWith({
|
||||
argv: {
|
||||
'job-id': 'j-123',
|
||||
api: 'test',
|
||||
branchName: 'test',
|
||||
destination: 'main@v1',
|
||||
},
|
||||
{}
|
||||
);
|
||||
config: {},
|
||||
version: 'cli-version',
|
||||
});
|
||||
});
|
||||
it('should accept no arguments at all', () => {
|
||||
const cb = jest.fn();
|
||||
transformPush(cb)({}, {} as any);
|
||||
expect(cb).toBeCalledWith({}, {});
|
||||
transformPush(cb)({ argv: {}, config: {} as any, version: 'cli-version' });
|
||||
expect(cb).toBeCalledWith({ argv: {}, config: {}, version: 'cli-version' });
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { commandWrapper } from '../wrapper';
|
||||
import { handleLint } from '../commands/lint';
|
||||
import { Arguments } from 'yargs';
|
||||
import { handlePush, PushOptions } from '../commands/push';
|
||||
import { detectSpec } from '@redocly/openapi-core';
|
||||
|
||||
jest.mock('node-fetch');
|
||||
jest.mock('../utils/miscellaneous', () => ({
|
||||
@@ -11,7 +12,9 @@ jest.mock('../utils/miscellaneous', () => ({
|
||||
loadConfigAndHandleErrors: jest.fn(),
|
||||
}));
|
||||
jest.mock('../commands/lint', () => ({
|
||||
handleLint: jest.fn(),
|
||||
handleLint: jest.fn().mockImplementation(({ collectSpecData }) => {
|
||||
collectSpecData({ openapi: '3.1.0' });
|
||||
}),
|
||||
lintConfigCallback: jest.fn(),
|
||||
}));
|
||||
|
||||
@@ -20,13 +23,32 @@ describe('commandWrapper', () => {
|
||||
(loadConfigAndHandleErrors as jest.Mock).mockImplementation(() => {
|
||||
return { telemetry: 'on', styleguide: { recommendedFallback: true } };
|
||||
});
|
||||
(detectSpec as jest.Mock).mockImplementationOnce(() => {
|
||||
return 'oas3_1';
|
||||
});
|
||||
process.env.REDOCLY_TELEMETRY = 'on';
|
||||
|
||||
const wrappedHandler = commandWrapper(handleLint);
|
||||
await wrappedHandler({} as any);
|
||||
expect(handleLint).toHaveBeenCalledTimes(1);
|
||||
expect(sendTelemetry).toHaveBeenCalledTimes(1);
|
||||
expect(sendTelemetry).toHaveBeenCalledWith({}, 0, false);
|
||||
expect(sendTelemetry).toHaveBeenCalledWith({}, 0, false, 'oas3_1', 'openapi', '3.1.0');
|
||||
});
|
||||
|
||||
it('should not collect spec version if the file is not parsed to json', async () => {
|
||||
(loadConfigAndHandleErrors as jest.Mock).mockImplementation(() => {
|
||||
return { telemetry: 'on', styleguide: { recommendedFallback: true } };
|
||||
});
|
||||
(handleLint as jest.Mock).mockImplementation(({ collectSpecData }) => {
|
||||
collectSpecData();
|
||||
});
|
||||
process.env.REDOCLY_TELEMETRY = 'on';
|
||||
|
||||
const wrappedHandler = commandWrapper(handleLint);
|
||||
await wrappedHandler({} as any);
|
||||
expect(handleLint).toHaveBeenCalledTimes(1);
|
||||
expect(sendTelemetry).toHaveBeenCalledTimes(1);
|
||||
expect(sendTelemetry).toHaveBeenCalledWith({}, 0, false, undefined, undefined, undefined);
|
||||
});
|
||||
|
||||
it('should NOT send telemetry if there is "telemetry: off" in the config', async () => {
|
||||
|
||||
@@ -85,15 +85,16 @@ describe('handlePushStatus()', () => {
|
||||
|
||||
it('should throw error if organization not provided', async () => {
|
||||
await expect(
|
||||
handlePushStatus(
|
||||
{
|
||||
handlePushStatus({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
organization: '',
|
||||
project: 'test-project',
|
||||
pushId: 'test-push-id',
|
||||
},
|
||||
mockConfig
|
||||
)
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
})
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
`"No organization provided, please use --organization option or specify the 'organization' field in the config file."`
|
||||
);
|
||||
@@ -108,15 +109,16 @@ describe('handlePushStatus()', () => {
|
||||
process.env.REDOCLY_AUTHORIZATION = 'test-api-key';
|
||||
remotes.getPush.mockResolvedValueOnce(pushResponseStub);
|
||||
|
||||
await handlePushStatus(
|
||||
{
|
||||
await handlePushStatus({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
organization: 'test-org',
|
||||
project: 'test-project',
|
||||
pushId: 'test-push-id',
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
expect(process.stdout.write).toHaveBeenCalledTimes(1);
|
||||
expect(process.stdout.write).toHaveBeenCalledWith(
|
||||
'🚀 Preview deploy success.\nPreview URL: https://preview-test-url\n'
|
||||
@@ -127,15 +129,16 @@ describe('handlePushStatus()', () => {
|
||||
process.env.REDOCLY_AUTHORIZATION = 'test-api-key';
|
||||
remotes.getPush.mockResolvedValue({ ...pushResponseStub, isMainBranch: true });
|
||||
|
||||
await handlePushStatus(
|
||||
{
|
||||
await handlePushStatus({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
organization: 'test-org',
|
||||
project: 'test-project',
|
||||
pushId: 'test-push-id',
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
expect(process.stdout.write).toHaveBeenCalledTimes(2);
|
||||
expect(process.stdout.write).toHaveBeenCalledWith(
|
||||
'🚀 Preview deploy success.\nPreview URL: https://preview-test-url\n'
|
||||
@@ -157,15 +160,16 @@ describe('handlePushStatus()', () => {
|
||||
});
|
||||
|
||||
await expect(
|
||||
handlePushStatus(
|
||||
{
|
||||
handlePushStatus({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
organization: 'test-org',
|
||||
project: 'test-project',
|
||||
pushId: 'test-push-id',
|
||||
},
|
||||
mockConfig
|
||||
)
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
})
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
||||
"❌ Preview deploy fail.
|
||||
Preview URL: https://preview-test-url"
|
||||
@@ -197,15 +201,16 @@ describe('handlePushStatus()', () => {
|
||||
},
|
||||
});
|
||||
|
||||
await handlePushStatus(
|
||||
{
|
||||
await handlePushStatus({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
organization: 'test-org',
|
||||
project: 'test-project',
|
||||
pushId: 'test-push-id',
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
expect(process.stdout.write).toHaveBeenCalledTimes(4);
|
||||
expect(process.stdout.write).toHaveBeenCalledWith(
|
||||
'🚀 Preview deploy success.\nPreview URL: https://preview-test-url\n'
|
||||
@@ -232,16 +237,17 @@ describe('handlePushStatus()', () => {
|
||||
},
|
||||
});
|
||||
|
||||
await handlePushStatus(
|
||||
{
|
||||
await handlePushStatus({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
organization: 'test-org',
|
||||
project: 'test-project',
|
||||
pushId: 'test-push-id',
|
||||
wait: true,
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(process.stderr.write).toHaveBeenCalledWith(
|
||||
'Files not added to your project. Reason: no changes.\n'
|
||||
@@ -253,15 +259,16 @@ describe('handlePushStatus()', () => {
|
||||
process.env.REDOCLY_AUTHORIZATION = 'test-api-key';
|
||||
remotes.getPush.mockResolvedValue({ ...pushResponseStub, isMainBranch: false });
|
||||
|
||||
const result = await handlePushStatus(
|
||||
{
|
||||
const result = await handlePushStatus({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
organization: 'test-org',
|
||||
project: 'test-project',
|
||||
pushId: 'test-push-id',
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
preview: {
|
||||
@@ -280,15 +287,16 @@ describe('handlePushStatus()', () => {
|
||||
process.env.REDOCLY_AUTHORIZATION = 'test-api-key';
|
||||
remotes.getPush.mockResolvedValue({ ...pushResponseStub, isMainBranch: true });
|
||||
|
||||
const result = await handlePushStatus(
|
||||
{
|
||||
const result = await handlePushStatus({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
organization: 'test-org',
|
||||
project: 'test-project',
|
||||
pushId: 'test-push-id',
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
preview: {
|
||||
@@ -344,8 +352,8 @@ describe('handlePushStatus()', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const result = await handlePushStatus(
|
||||
{
|
||||
const result = await handlePushStatus({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
organization: 'test-org',
|
||||
project: 'test-project',
|
||||
@@ -353,8 +361,9 @@ describe('handlePushStatus()', () => {
|
||||
'retry-interval': 0.5, // 500 ms
|
||||
wait: true,
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
preview: {
|
||||
@@ -417,8 +426,8 @@ describe('handlePushStatus()', () => {
|
||||
},
|
||||
});
|
||||
|
||||
const result = await handlePushStatus(
|
||||
{
|
||||
const result = await handlePushStatus({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
organization: 'test-org',
|
||||
project: 'test-project',
|
||||
@@ -426,8 +435,9 @@ describe('handlePushStatus()', () => {
|
||||
'retry-interval': 0.5, // 500 ms
|
||||
wait: true,
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(result).toEqual({
|
||||
preview: {
|
||||
@@ -458,16 +468,17 @@ describe('handlePushStatus()', () => {
|
||||
});
|
||||
|
||||
await expect(
|
||||
handlePushStatus(
|
||||
{
|
||||
handlePushStatus({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
organization: 'test-org',
|
||||
project: 'test-project',
|
||||
pushId: 'test-push-id',
|
||||
'continue-on-deploy-failures': false,
|
||||
},
|
||||
mockConfig
|
||||
)
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
})
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
||||
"❌ Preview deploy fail.
|
||||
Preview URL: https://preview-test-url"
|
||||
@@ -488,16 +499,17 @@ describe('handlePushStatus()', () => {
|
||||
});
|
||||
|
||||
await expect(
|
||||
handlePushStatus(
|
||||
{
|
||||
handlePushStatus({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
organization: 'test-org',
|
||||
project: 'test-project',
|
||||
pushId: 'test-push-id',
|
||||
'continue-on-deploy-failures': true,
|
||||
},
|
||||
mockConfig
|
||||
)
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
})
|
||||
).resolves.toStrictEqual({
|
||||
preview: {
|
||||
deploy: { status: 'failed', url: 'https://preview-test-url' },
|
||||
@@ -545,8 +557,8 @@ describe('handlePushStatus()', () => {
|
||||
|
||||
const onRetrySpy = jest.fn();
|
||||
|
||||
const result = await handlePushStatus(
|
||||
{
|
||||
const result = await handlePushStatus({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
organization: 'test-org',
|
||||
project: 'test-project',
|
||||
@@ -555,8 +567,9 @@ describe('handlePushStatus()', () => {
|
||||
'retry-interval': 0.5, // 500 ms
|
||||
onRetry: onRetrySpy,
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(onRetrySpy).toBeCalledTimes(2);
|
||||
|
||||
@@ -617,8 +630,8 @@ describe('handlePushStatus()', () => {
|
||||
});
|
||||
|
||||
await expect(
|
||||
handlePushStatus(
|
||||
{
|
||||
handlePushStatus({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
organization: 'test-org',
|
||||
project: 'test-project',
|
||||
@@ -627,8 +640,9 @@ describe('handlePushStatus()', () => {
|
||||
'max-execution-time': 1, // seconds
|
||||
wait: true,
|
||||
},
|
||||
mockConfig
|
||||
)
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
})
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(`
|
||||
"✗ Failed to get push status. Reason: Timeout exceeded
|
||||
"
|
||||
|
||||
@@ -63,8 +63,8 @@ describe('handlePush()', () => {
|
||||
pathRelativeSpy.mockImplementationOnce((_, p) => p);
|
||||
pathDirnameSpy.mockImplementation((_: string) => '.');
|
||||
|
||||
await handlePush(
|
||||
{
|
||||
await handlePush({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
'mount-path': 'test-mount-path',
|
||||
organization: 'test-org',
|
||||
@@ -81,8 +81,9 @@ describe('handlePush()', () => {
|
||||
files: ['test-file'],
|
||||
'max-execution-time': 10,
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(remotes.getDefaultBranch).toHaveBeenCalledWith('test-org', 'test-project');
|
||||
expect(remotes.upsert).toHaveBeenCalledWith('test-org', 'test-project', {
|
||||
@@ -132,8 +133,8 @@ describe('handlePush()', () => {
|
||||
pathRelativeSpy.mockImplementationOnce((_, p) => p);
|
||||
pathDirnameSpy.mockImplementation((_: string) => '.');
|
||||
|
||||
const result = await handlePush(
|
||||
{
|
||||
const result = await handlePush({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
'mount-path': 'test-mount-path',
|
||||
organization: 'test-org',
|
||||
@@ -150,8 +151,9 @@ describe('handlePush()', () => {
|
||||
files: ['test-file'],
|
||||
'max-execution-time': 10,
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(result).toEqual({ pushId: 'test-id' });
|
||||
});
|
||||
@@ -188,8 +190,8 @@ describe('handlePush()', () => {
|
||||
throw new Error('Not a directory');
|
||||
});
|
||||
|
||||
await handlePush(
|
||||
{
|
||||
await handlePush({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
'mount-path': 'test-mount-path',
|
||||
organization: 'test-org',
|
||||
@@ -201,8 +203,9 @@ describe('handlePush()', () => {
|
||||
files: ['test-folder'],
|
||||
'max-execution-time': 10,
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(remotes.push).toHaveBeenCalledWith(
|
||||
expect.anything(),
|
||||
@@ -230,8 +233,8 @@ describe('handlePush()', () => {
|
||||
const mockConfig = { apis: {} } as any;
|
||||
process.env.REDOCLY_AUTHORIZATION = 'test-api-key';
|
||||
|
||||
await handlePush(
|
||||
{
|
||||
await handlePush({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
'mount-path': 'test-mount-path',
|
||||
organization: 'test-org',
|
||||
@@ -243,8 +246,9 @@ describe('handlePush()', () => {
|
||||
files: [],
|
||||
'max-execution-time': 10,
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(remotes.getDefaultBranch).not.toHaveBeenCalled();
|
||||
expect(remotes.upsert).not.toHaveBeenCalled();
|
||||
@@ -265,8 +269,8 @@ describe('handlePush()', () => {
|
||||
pathRelativeSpy.mockImplementationOnce((_, p) => p);
|
||||
pathDirnameSpy.mockImplementation((_: string) => '.');
|
||||
|
||||
await handlePush(
|
||||
{
|
||||
await handlePush({
|
||||
argv: {
|
||||
domain: 'test-domain',
|
||||
'mount-path': 'test-mount-path',
|
||||
project: 'test-project',
|
||||
@@ -277,8 +281,9 @@ describe('handlePush()', () => {
|
||||
'default-branch': 'main',
|
||||
'max-execution-time': 10,
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(remotes.getDefaultBranch).toHaveBeenCalledWith(
|
||||
'test-org-from-config',
|
||||
@@ -312,8 +317,8 @@ describe('handlePush()', () => {
|
||||
pathRelativeSpy.mockImplementationOnce((_, p) => p);
|
||||
pathDirnameSpy.mockImplementation((_: string) => '.');
|
||||
|
||||
await handlePush(
|
||||
{
|
||||
await handlePush({
|
||||
argv: {
|
||||
'mount-path': 'test-mount-path',
|
||||
project: 'test-project',
|
||||
branch: 'test-branch',
|
||||
@@ -323,8 +328,9 @@ describe('handlePush()', () => {
|
||||
files: ['test-file'],
|
||||
'max-execution-time': 10,
|
||||
},
|
||||
mockConfig
|
||||
);
|
||||
config: mockConfig,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(ReuniteApiClient).toBeCalledWith('test-domain-from-env', 'test-api-key');
|
||||
});
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
import * as colors from 'colorette';
|
||||
import type { Config, OutputFormat } from '@redocly/openapi-core';
|
||||
|
||||
import { exitWithError, printExecutionTime } from '../../utils/miscellaneous';
|
||||
import { Spinner } from '../../utils/spinner';
|
||||
import { DeploymentError } from '../utils';
|
||||
import { ReuniteApiClient, getApiKeys, getDomain } from '../api';
|
||||
import { capitalize } from '../../utils/js-utils';
|
||||
import { retryUntilConditionMet } from './utils';
|
||||
|
||||
import type { OutputFormat } from '@redocly/openapi-core';
|
||||
import type { CommandArgs } from '../../wrapper';
|
||||
import type {
|
||||
DeploymentStatus,
|
||||
DeploymentStatusResponse,
|
||||
PushResponse,
|
||||
ScorecardItem,
|
||||
} from '../api/types';
|
||||
import { retryUntilConditionMet } from './utils';
|
||||
|
||||
const RETRY_INTERVAL_MS = 5000; // 5 sec
|
||||
|
||||
@@ -37,10 +38,10 @@ export interface PushStatusSummary {
|
||||
commit: PushResponse['commit'];
|
||||
}
|
||||
|
||||
export async function handlePushStatus(
|
||||
argv: PushStatusOptions,
|
||||
config: Config
|
||||
): Promise<PushStatusSummary | undefined> {
|
||||
export async function handlePushStatus({
|
||||
argv,
|
||||
config,
|
||||
}: CommandArgs<PushStatusOptions>): Promise<PushStatusSummary | undefined> {
|
||||
const startedAt = performance.now();
|
||||
const spinner = new Spinner();
|
||||
|
||||
|
||||
@@ -11,7 +11,8 @@ import {
|
||||
import { handlePushStatus } from './push-status';
|
||||
import { ReuniteApiClient, getDomain, getApiKeys } from '../api';
|
||||
|
||||
import type { OutputFormat, Config } from '@redocly/openapi-core';
|
||||
import type { OutputFormat } from '@redocly/openapi-core';
|
||||
import type { CommandArgs } from '../../wrapper';
|
||||
|
||||
export type PushOptions = {
|
||||
apis?: string[];
|
||||
@@ -42,10 +43,11 @@ export type PushOptions = {
|
||||
|
||||
type FileToUpload = { name: string; path: string };
|
||||
|
||||
export async function handlePush(
|
||||
argv: PushOptions,
|
||||
config: Config
|
||||
): Promise<{ pushId: string } | void> {
|
||||
export async function handlePush({
|
||||
argv,
|
||||
config,
|
||||
version,
|
||||
}: CommandArgs<PushOptions>): Promise<{ pushId: string } | void> {
|
||||
const startedAt = performance.now(); // for printing execution time
|
||||
const startTime = Date.now(); // for push-status command
|
||||
|
||||
@@ -131,8 +133,8 @@ export async function handlePush(
|
||||
if (waitForDeployment) {
|
||||
process.stdout.write('\n');
|
||||
|
||||
await handlePushStatus(
|
||||
{
|
||||
await handlePushStatus({
|
||||
argv: {
|
||||
organization: orgId,
|
||||
project: projectId,
|
||||
pushId: id,
|
||||
@@ -142,8 +144,9 @@ export async function handlePush(
|
||||
'start-time': startTime,
|
||||
'continue-on-deploy-failures': argv['continue-on-deploy-failures'],
|
||||
},
|
||||
config
|
||||
);
|
||||
config,
|
||||
version,
|
||||
});
|
||||
}
|
||||
verbose &&
|
||||
printExecutionTime(
|
||||
|
||||
@@ -2,17 +2,21 @@ import { loadAndBundleSpec } from 'redoc';
|
||||
import { dirname, resolve } from 'path';
|
||||
import { writeFileSync, mkdirSync } from 'fs';
|
||||
import { performance } from 'perf_hooks';
|
||||
|
||||
import { getMergedConfig, isAbsoluteUrl } from '@redocly/openapi-core';
|
||||
import { getObjectOrJSON, getPageHTML } from './utils';
|
||||
import type { BuildDocsArgv } from './types';
|
||||
import { Config, getMergedConfig, isAbsoluteUrl } from '@redocly/openapi-core';
|
||||
import { exitWithError, getExecutionTime, getFallbackApisOrExit } from '../../utils/miscellaneous';
|
||||
|
||||
export const handlerBuildCommand = async (argv: BuildDocsArgv, configFromFile: Config) => {
|
||||
import type { BuildDocsArgv } from './types';
|
||||
import type { CommandArgs } from '../../wrapper';
|
||||
|
||||
export const handlerBuildCommand = async ({
|
||||
argv,
|
||||
config: configFromFile,
|
||||
collectSpecData,
|
||||
}: CommandArgs<BuildDocsArgv>) => {
|
||||
const startedAt = performance.now();
|
||||
|
||||
const config = getMergedConfig(configFromFile, argv.api);
|
||||
|
||||
const apis = await getFallbackApisOrExit(argv.api ? [argv.api] : [], config);
|
||||
const { path: pathToApi } = apis[0];
|
||||
|
||||
@@ -31,6 +35,7 @@ export const handlerBuildCommand = async (argv: BuildDocsArgv, configFromFile: C
|
||||
const elapsed = getExecutionTime(startedAt);
|
||||
|
||||
const api = await loadAndBundleSpec(isAbsoluteUrl(pathToApi) ? pathToApi : resolve(pathToApi));
|
||||
collectSpecData?.(api);
|
||||
const pageHTML = await getPageHTML(
|
||||
api,
|
||||
pathToApi,
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
import { formatProblems, getTotals, getMergedConfig, bundle, Config } from '@redocly/openapi-core';
|
||||
import { performance } from 'perf_hooks';
|
||||
import { blue, gray, green, yellow } from 'colorette';
|
||||
import { writeFileSync } from 'fs';
|
||||
import { formatProblems, getTotals, getMergedConfig, bundle } from '@redocly/openapi-core';
|
||||
import {
|
||||
dumpBundle,
|
||||
getExecutionTime,
|
||||
@@ -8,12 +11,11 @@ import {
|
||||
printUnusedWarnings,
|
||||
saveBundle,
|
||||
sortTopLevelKeysForOas,
|
||||
checkForDeprecatedOptions,
|
||||
} from '../utils/miscellaneous';
|
||||
|
||||
import type { OutputExtensions, Skips, Totals } from '../types';
|
||||
import { performance } from 'perf_hooks';
|
||||
import { blue, gray, green, yellow } from 'colorette';
|
||||
import { writeFileSync } from 'fs';
|
||||
import { checkForDeprecatedOptions } from '../utils/miscellaneous';
|
||||
import type { CommandArgs } from '../wrapper';
|
||||
|
||||
export type BundleOptions = {
|
||||
apis?: string[];
|
||||
@@ -28,7 +30,12 @@ export type BundleOptions = {
|
||||
'keep-url-references'?: boolean;
|
||||
} & Skips;
|
||||
|
||||
export async function handleBundle(argv: BundleOptions, config: Config, version: string) {
|
||||
export async function handleBundle({
|
||||
argv,
|
||||
config,
|
||||
version,
|
||||
collectSpecData,
|
||||
}: CommandArgs<BundleOptions>) {
|
||||
const removeUnusedComponents =
|
||||
argv['remove-unused-components'] ||
|
||||
config.rawConfig?.styleguide?.decorators?.hasOwnProperty('remove-unused-components');
|
||||
@@ -59,6 +66,7 @@ export async function handleBundle(argv: BundleOptions, config: Config, version:
|
||||
dereference: argv.dereferenced,
|
||||
removeUnusedComponents,
|
||||
keepUrlRefs: argv['keep-url-references'],
|
||||
collectSpecData,
|
||||
});
|
||||
|
||||
const fileTotals = getTotals(problems);
|
||||
|
||||
@@ -2,7 +2,6 @@ import * as path from 'path';
|
||||
import { red, blue, yellow, green } from 'colorette';
|
||||
import { performance } from 'perf_hooks';
|
||||
import {
|
||||
Config,
|
||||
SpecVersion,
|
||||
BaseResolver,
|
||||
formatProblems,
|
||||
@@ -38,6 +37,7 @@ import type {
|
||||
Oas3Server,
|
||||
Oas3_1Definition,
|
||||
} from '@redocly/openapi-core/lib/typings/openapi';
|
||||
import type { CommandArgs } from '../wrapper';
|
||||
|
||||
const Tags = 'tags';
|
||||
const xTagGroups = 'x-tagGroups';
|
||||
@@ -64,7 +64,11 @@ export type JoinOptions = {
|
||||
'lint-config'?: RuleSeverity;
|
||||
};
|
||||
|
||||
export async function handleJoin(argv: JoinOptions, config: Config, packageVersion: string) {
|
||||
export async function handleJoin({
|
||||
argv,
|
||||
config,
|
||||
version: packageVersion,
|
||||
}: CommandArgs<JoinOptions>) {
|
||||
const startedAt = performance.now();
|
||||
|
||||
if (argv.apis.length < 2) {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { blue, gray } from 'colorette';
|
||||
import { performance } from 'perf_hooks';
|
||||
import {
|
||||
Config,
|
||||
formatProblems,
|
||||
getMergedConfig,
|
||||
getTotals,
|
||||
@@ -23,10 +22,11 @@ import {
|
||||
} from '../utils/miscellaneous';
|
||||
import { getCommandNameFromArgs } from '../utils/getCommandNameFromArgs';
|
||||
|
||||
import type { Arguments } from 'yargs';
|
||||
import type { OutputFormat, ProblemSeverity, RuleSeverity } from '@redocly/openapi-core';
|
||||
import type { RawConfigProcessor } from '@redocly/openapi-core/lib/config';
|
||||
import type { CommandOptions, Skips, Totals } from '../types';
|
||||
import type { Arguments } from 'yargs';
|
||||
import type { CommandArgs } from '../wrapper';
|
||||
|
||||
export type LintOptions = {
|
||||
apis?: string[];
|
||||
@@ -38,7 +38,12 @@ export type LintOptions = {
|
||||
'lint-config'?: RuleSeverity;
|
||||
} & Omit<Skips, 'skip-decorator'>;
|
||||
|
||||
export async function handleLint(argv: LintOptions, config: Config, version: string) {
|
||||
export async function handleLint({
|
||||
argv,
|
||||
config,
|
||||
version,
|
||||
collectSpecData,
|
||||
}: CommandArgs<LintOptions>) {
|
||||
const apis = await getFallbackApisOrExit(argv.apis, config);
|
||||
|
||||
if (!apis.length) {
|
||||
@@ -74,6 +79,7 @@ export async function handleLint(argv: LintOptions, config: Config, version: str
|
||||
const results = await lint({
|
||||
ref: path,
|
||||
config: resolvedConfig,
|
||||
collectSpecData,
|
||||
});
|
||||
|
||||
const fileTotals = getTotals(results);
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Region, RedoclyClient, Config } from '@redocly/openapi-core';
|
||||
import { Region, RedoclyClient } from '@redocly/openapi-core';
|
||||
import { blue, green, gray } from 'colorette';
|
||||
import { promptUser } from '../utils/miscellaneous';
|
||||
|
||||
import type { CommandArgs } from '../wrapper';
|
||||
|
||||
export function promptClientToken(domain: string) {
|
||||
return promptUser(
|
||||
green(
|
||||
@@ -17,7 +19,7 @@ export type LoginOptions = {
|
||||
config?: string;
|
||||
};
|
||||
|
||||
export async function handleLogin(argv: LoginOptions, config: Config) {
|
||||
export async function handleLogin({ argv, config }: CommandArgs<LoginOptions>) {
|
||||
const region = argv.region || config.region;
|
||||
const client = new RedoclyClient(region);
|
||||
const clientToken = await promptClientToken(client.domain);
|
||||
|
||||
@@ -7,7 +7,9 @@ import {
|
||||
loadConfigAndHandleErrors,
|
||||
} from '../../utils/miscellaneous';
|
||||
import startPreviewServer from './preview-server/preview-server';
|
||||
|
||||
import type { Skips } from '../../types';
|
||||
import type { CommandArgs } from '../../wrapper';
|
||||
|
||||
export type PreviewDocsOptions = {
|
||||
port: number;
|
||||
@@ -18,7 +20,10 @@ export type PreviewDocsOptions = {
|
||||
force?: boolean;
|
||||
} & Omit<Skips, 'skip-rule'>;
|
||||
|
||||
export async function previewDocs(argv: PreviewDocsOptions, configFromFile: Config) {
|
||||
export async function previewDocs({
|
||||
argv,
|
||||
config: configFromFile,
|
||||
}: CommandArgs<PreviewDocsOptions>) {
|
||||
let isAuthorizedWithRedocly = false;
|
||||
let redocOptions: any = {};
|
||||
let config = await reloadConfig(configFromFile);
|
||||
|
||||
@@ -4,12 +4,13 @@ import { spawn } from 'child_process';
|
||||
import { PRODUCT_NAMES, PRODUCT_PACKAGES } from './constants';
|
||||
|
||||
import type { PreviewProjectOptions, Product } from './types';
|
||||
import type { CommandArgs } from '../../wrapper';
|
||||
|
||||
export const previewProject = async (args: PreviewProjectOptions) => {
|
||||
const { plan, port } = args;
|
||||
const projectDir = args['source-dir'];
|
||||
export const previewProject = async ({ argv }: CommandArgs<PreviewProjectOptions>) => {
|
||||
const { plan, port } = argv;
|
||||
const projectDir = argv['source-dir'];
|
||||
|
||||
const product = args.product || tryGetProductFromPackageJson(projectDir);
|
||||
const product = argv.product || tryGetProductFromPackageJson(projectDir);
|
||||
|
||||
if (!isValidProduct(product)) {
|
||||
process.stderr.write(`Invalid product ${product}`);
|
||||
|
||||
@@ -26,6 +26,8 @@ import {
|
||||
import { promptClientToken } from './login';
|
||||
import { handlePush as handleCMSPush } from '../cms/commands/push';
|
||||
|
||||
import type { CommandArgs } from '../wrapper';
|
||||
|
||||
const DEFAULT_VERSION = 'latest';
|
||||
|
||||
export const DESTINATION_REGEX =
|
||||
@@ -59,7 +61,7 @@ export function commonPushHandler({
|
||||
return transformPush(handlePush);
|
||||
}
|
||||
|
||||
export async function handlePush(argv: PushOptions, config: Config): Promise<void> {
|
||||
export async function handlePush({ argv, config }: CommandArgs<PushOptions>): Promise<void> {
|
||||
const client = new RedoclyClient(config.region);
|
||||
const isAuthorized = await client.isAuthorizedWithRedoclyByRegion();
|
||||
if (!isAuthorized) {
|
||||
@@ -366,16 +368,11 @@ type BarePushArgs = Omit<PushOptions, 'destination' | 'branchName'> & {
|
||||
|
||||
export const transformPush =
|
||||
(callback: typeof handlePush) =>
|
||||
(
|
||||
{
|
||||
apis,
|
||||
branch,
|
||||
'batch-id': batchId,
|
||||
'job-id': jobId,
|
||||
...rest
|
||||
}: BarePushArgs & { 'batch-id'?: string },
|
||||
config: Config
|
||||
) => {
|
||||
({
|
||||
argv: { apis, branch, 'batch-id': batchId, 'job-id': jobId, ...rest },
|
||||
config,
|
||||
version,
|
||||
}: CommandArgs<BarePushArgs & { 'batch-id'?: string }>) => {
|
||||
const [maybeApiOrDestination, maybeDestination, maybeBranchName] = apis || [];
|
||||
|
||||
if (batchId) {
|
||||
@@ -414,16 +411,17 @@ export const transformPush =
|
||||
apiFile = maybeApiOrDestination;
|
||||
}
|
||||
|
||||
return callback(
|
||||
{
|
||||
return callback({
|
||||
argv: {
|
||||
...rest,
|
||||
destination: rest.destination ?? destination,
|
||||
api: apiFile,
|
||||
branchName: branch ?? maybeBranchName,
|
||||
'job-id': jobId || batchId,
|
||||
},
|
||||
config
|
||||
);
|
||||
config,
|
||||
version,
|
||||
});
|
||||
};
|
||||
|
||||
export function getApiRoot({
|
||||
|
||||
@@ -3,6 +3,9 @@ import * as path from 'path';
|
||||
import * as openapiCore from '@redocly/openapi-core';
|
||||
import { ComponentsFiles } from '../types';
|
||||
import { blue, green } from 'colorette';
|
||||
import { loadConfigAndHandleErrors } from '../../../utils/__mocks__/miscellaneous';
|
||||
|
||||
import type { Config } from '@redocly/openapi-core';
|
||||
|
||||
const utils = require('../../../utils/miscellaneous');
|
||||
|
||||
@@ -25,9 +28,13 @@ describe('#split', () => {
|
||||
jest.spyOn(process.stderr, 'write').mockImplementation(() => true);
|
||||
|
||||
await handleSplit({
|
||||
argv: {
|
||||
api: filePath,
|
||||
outDir: openapiDir,
|
||||
separator: '_',
|
||||
},
|
||||
config: loadConfigAndHandleErrors() as any as Config,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(process.stderr.write).toBeCalledTimes(2);
|
||||
@@ -46,9 +53,13 @@ describe('#split', () => {
|
||||
jest.spyOn(utils, 'pathToFilename').mockImplementation(() => 'newFilePath');
|
||||
|
||||
await handleSplit({
|
||||
argv: {
|
||||
api: filePath,
|
||||
outDir: openapiDir,
|
||||
separator: '_',
|
||||
},
|
||||
config: loadConfigAndHandleErrors() as any as Config,
|
||||
version: 'cli-version',
|
||||
});
|
||||
|
||||
expect(utils.pathToFilename).toBeCalledWith(expect.anything(), '_');
|
||||
|
||||
@@ -36,6 +36,7 @@ import type {
|
||||
Oas3PathItem,
|
||||
Referenced,
|
||||
} from './types';
|
||||
import type { CommandArgs } from '../../wrapper';
|
||||
|
||||
export type SplitOptions = {
|
||||
api: string;
|
||||
@@ -44,12 +45,13 @@ export type SplitOptions = {
|
||||
config?: string;
|
||||
};
|
||||
|
||||
export async function handleSplit(argv: SplitOptions) {
|
||||
export async function handleSplit({ argv, collectSpecData }: CommandArgs<SplitOptions>) {
|
||||
const startedAt = performance.now();
|
||||
const { api, outDir, separator } = argv;
|
||||
validateDefinitionFileName(api!);
|
||||
const ext = getAndValidateFileExtension(api);
|
||||
const openapi = readYaml(api!) as Oas3Definition | Oas3_1Definition;
|
||||
collectSpecData?.(openapi);
|
||||
splitDefinition(openapi, outDir, separator, ext);
|
||||
process.stderr.write(
|
||||
`🪓 Document: ${blue(api!)} ${green('is successfully split')}
|
||||
@@ -292,7 +294,7 @@ function iteratePathItems(
|
||||
|
||||
for (const pathName of Object.keys(pathItems)) {
|
||||
const pathFile = `${path.join(outDir, pathToFilename(pathName, pathSeparator))}.${ext}`;
|
||||
const pathData = pathItems[pathName] as Oas3PathItem;
|
||||
const pathData = pathItems[pathName];
|
||||
|
||||
if (isRef(pathData)) continue;
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { performance } from 'perf_hooks';
|
||||
import * as colors from 'colorette';
|
||||
import {
|
||||
Config,
|
||||
StyleguideConfig,
|
||||
normalizeTypes,
|
||||
BaseResolver,
|
||||
@@ -15,7 +14,9 @@ import {
|
||||
} from '@redocly/openapi-core';
|
||||
import { getFallbackApisOrExit } from '../utils/miscellaneous';
|
||||
import { printExecutionTime } from '../utils/miscellaneous';
|
||||
|
||||
import type { StatsAccumulator, StatsName, WalkContext, OutputFormat } from '@redocly/openapi-core';
|
||||
import type { CommandArgs } from '../wrapper';
|
||||
|
||||
const statsAccumulator: StatsAccumulator = {
|
||||
refs: { metric: '🚗 References', total: 0, color: 'red', items: new Set() },
|
||||
@@ -86,10 +87,11 @@ export type StatsOptions = {
|
||||
config?: string;
|
||||
};
|
||||
|
||||
export async function handleStats(argv: StatsOptions, config: Config) {
|
||||
export async function handleStats({ argv, config, collectSpecData }: CommandArgs<StatsOptions>) {
|
||||
const [{ path }] = await getFallbackApisOrExit(argv.api ? [argv.api] : [], config);
|
||||
const externalRefResolver = new BaseResolver(config.resolve);
|
||||
const { bundle: document } = await bundle({ config, ref: path });
|
||||
collectSpecData?.(document.parsed);
|
||||
const lintConfig: StyleguideConfig = config.styleguide;
|
||||
const specVersion = detectSpec(document.parsed);
|
||||
const types = normalizeTypes(
|
||||
|
||||
@@ -541,7 +541,10 @@ export function cleanColors(input: string): string {
|
||||
export async function sendTelemetry(
|
||||
argv: Arguments | undefined,
|
||||
exit_code: ExitCode,
|
||||
has_config: boolean | undefined
|
||||
has_config: boolean | undefined,
|
||||
spec_version: string | undefined,
|
||||
spec_keyword: string | undefined,
|
||||
spec_full_version: string | undefined
|
||||
): Promise<void> {
|
||||
try {
|
||||
if (!argv) {
|
||||
@@ -569,6 +572,9 @@ export async function sendTelemetry(
|
||||
environment_ci: process.env.CI,
|
||||
raw_input: cleanRawInput(process.argv.slice(2)),
|
||||
has_config,
|
||||
spec_version,
|
||||
spec_keyword,
|
||||
spec_full_version,
|
||||
};
|
||||
await fetch(`https://api.redocly.com/registry/telemetry/cli`, {
|
||||
method: 'POST',
|
||||
@@ -598,6 +604,9 @@ export type Analytics = {
|
||||
environment_ci?: string;
|
||||
raw_input: string;
|
||||
has_config?: boolean;
|
||||
spec_version?: string;
|
||||
spec_keyword?: string;
|
||||
spec_full_version?: string;
|
||||
};
|
||||
|
||||
function isFile(value: string) {
|
||||
|
||||
@@ -1,22 +1,48 @@
|
||||
import { Config, Region, doesYamlFileExist } from '@redocly/openapi-core';
|
||||
import type { Arguments } from 'yargs';
|
||||
import { detectSpec, doesYamlFileExist } from '@redocly/openapi-core';
|
||||
import { isPlainObject } from '@redocly/openapi-core/lib/utils';
|
||||
import { version } from './utils/update-version-notifier';
|
||||
import {
|
||||
ExitCode,
|
||||
exitWithError,
|
||||
loadConfigAndHandleErrors,
|
||||
sendTelemetry,
|
||||
} from './utils/miscellaneous';
|
||||
import { exitWithError, loadConfigAndHandleErrors, sendTelemetry } from './utils/miscellaneous';
|
||||
import { lintConfigCallback } from './commands/lint';
|
||||
|
||||
import type { Arguments } from 'yargs';
|
||||
import type { Config, Region } from '@redocly/openapi-core';
|
||||
import type { CollectFn } from '@redocly/openapi-core/lib/utils';
|
||||
import type { ExitCode } from './utils/miscellaneous';
|
||||
import type { CommandOptions } from './types';
|
||||
|
||||
export type CommandArgs<T extends CommandOptions> = {
|
||||
argv: T;
|
||||
config: Config;
|
||||
version: string;
|
||||
collectSpecData?: CollectFn;
|
||||
};
|
||||
|
||||
export function commandWrapper<T extends CommandOptions>(
|
||||
commandHandler?: (argv: T, config: Config, version: string) => Promise<unknown>
|
||||
commandHandler?: (wrapperArgs: CommandArgs<T>) => Promise<unknown>
|
||||
) {
|
||||
return async (argv: Arguments<T>) => {
|
||||
let code: ExitCode = 2;
|
||||
let hasConfig;
|
||||
let telemetry;
|
||||
let specVersion: string | undefined;
|
||||
let specKeyword: string | undefined;
|
||||
let specFullVersion: string | undefined;
|
||||
const collectSpecData: CollectFn = (document) => {
|
||||
specVersion = detectSpec(document);
|
||||
if (!isPlainObject(document)) return;
|
||||
specKeyword = document?.openapi
|
||||
? 'openapi'
|
||||
: document?.swagger
|
||||
? 'swagger'
|
||||
: document?.asyncapi
|
||||
? 'asyncapi'
|
||||
: document?.arazzo
|
||||
? 'arazzo'
|
||||
: undefined;
|
||||
if (specKeyword) {
|
||||
specFullVersion = document[specKeyword] as string;
|
||||
}
|
||||
};
|
||||
try {
|
||||
if (argv.config && !doesYamlFileExist(argv.config)) {
|
||||
exitWithError('Please provide a valid path to the configuration file.');
|
||||
@@ -32,14 +58,14 @@ export function commandWrapper<T extends CommandOptions>(
|
||||
hasConfig = !config.styleguide.recommendedFallback;
|
||||
code = 1;
|
||||
if (typeof commandHandler === 'function') {
|
||||
await commandHandler(argv, config, version);
|
||||
await commandHandler({ argv, config, version, collectSpecData });
|
||||
}
|
||||
code = 0;
|
||||
} catch (err) {
|
||||
// Do nothing
|
||||
} finally {
|
||||
if (process.env.REDOCLY_TELEMETRY !== 'off' && telemetry !== 'off') {
|
||||
await sendTelemetry(argv, code, hasConfig);
|
||||
await sendTelemetry(argv, code, hasConfig, specVersion, specKeyword, specFullVersion);
|
||||
}
|
||||
process.once('beforeExit', () => {
|
||||
process.exit(code);
|
||||
|
||||
@@ -24,6 +24,7 @@ import type { WalkContext, UserContext, ResolveResult, NormalizedProblem } from
|
||||
import type { Config, StyleguideConfig } from './config';
|
||||
import type { OasRef } from './typings/openapi';
|
||||
import type { Document, ResolvedRefMap } from './resolve';
|
||||
import type { CollectFn } from './utils';
|
||||
|
||||
export enum OasVersion {
|
||||
Version2 = 'oas2',
|
||||
@@ -82,6 +83,7 @@ export async function bundle(
|
||||
opts: {
|
||||
ref?: string;
|
||||
doc?: Document;
|
||||
collectSpecData?: CollectFn;
|
||||
} & BundleOptions
|
||||
) {
|
||||
const {
|
||||
@@ -100,6 +102,7 @@ export async function bundle(
|
||||
if (document instanceof Error) {
|
||||
throw document;
|
||||
}
|
||||
opts.collectSpecData?.(document.parsed);
|
||||
|
||||
return bundleDocument({
|
||||
document,
|
||||
|
||||
@@ -22,14 +22,17 @@ import type {
|
||||
Oas3Visitor,
|
||||
RuleInstanceConfig,
|
||||
} from './visitors';
|
||||
import type { CollectFn } from './utils';
|
||||
|
||||
export async function lint(opts: {
|
||||
ref: string;
|
||||
config: Config;
|
||||
externalRefResolver?: BaseResolver;
|
||||
collectSpecData?: CollectFn;
|
||||
}) {
|
||||
const { ref, externalRefResolver = new BaseResolver(opts.config.resolve) } = opts;
|
||||
const document = (await externalRefResolver.resolveDocument(null, ref, true)) as Document;
|
||||
opts.collectSpecData?.(document.parsed);
|
||||
|
||||
return lintDocument({
|
||||
document,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {
|
||||
import type {
|
||||
Oas3Rule,
|
||||
Oas3Preprocessor,
|
||||
Oas2Rule,
|
||||
@@ -16,7 +16,7 @@ import { Oas3_1Types } from './types/oas3_1';
|
||||
import { AsyncApi2Types } from './types/asyncapi2';
|
||||
import { AsyncApi3Types } from './types/asyncapi3';
|
||||
import { ArazzoTypes } from './types/arazzo';
|
||||
import {
|
||||
import type {
|
||||
BuiltInAsync2RuleId,
|
||||
BuiltInAsync3RuleId,
|
||||
BuiltInCommonOASRuleId,
|
||||
@@ -24,6 +24,7 @@ import {
|
||||
BuiltInOAS2RuleId,
|
||||
BuiltInOAS3RuleId,
|
||||
} from './types/redocly-yaml';
|
||||
import { isPlainObject } from './utils';
|
||||
|
||||
export enum SpecVersion {
|
||||
OAS2 = 'oas2',
|
||||
@@ -93,8 +94,8 @@ export type Async2DecoratorsSet = Record<string, Async2Preprocessor>;
|
||||
export type Async3DecoratorsSet = Record<string, Async3Preprocessor>;
|
||||
export type ArazzoDecoratorsSet = Record<string, ArazzoPreprocessor>;
|
||||
|
||||
export function detectSpec(root: any): SpecVersion {
|
||||
if (typeof root !== 'object') {
|
||||
export function detectSpec(root: unknown): SpecVersion {
|
||||
if (!isPlainObject(root)) {
|
||||
throw new Error(`Document must be JSON object, got ${typeof root}`);
|
||||
}
|
||||
|
||||
@@ -102,11 +103,11 @@ export function detectSpec(root: any): SpecVersion {
|
||||
throw new Error(`Invalid OpenAPI version: should be a string but got "${typeof root.openapi}"`);
|
||||
}
|
||||
|
||||
if (root.openapi && root.openapi.startsWith('3.0')) {
|
||||
if (typeof root.openapi === 'string' && root.openapi.startsWith('3.0')) {
|
||||
return SpecVersion.OAS3_0;
|
||||
}
|
||||
|
||||
if (root.openapi && root.openapi.startsWith('3.1')) {
|
||||
if (typeof root.openapi === 'string' && root.openapi.startsWith('3.1')) {
|
||||
return SpecVersion.OAS3_1;
|
||||
}
|
||||
|
||||
@@ -114,16 +115,15 @@ export function detectSpec(root: any): SpecVersion {
|
||||
return SpecVersion.OAS2;
|
||||
}
|
||||
|
||||
// if not detected yet
|
||||
if (root.openapi || root.swagger) {
|
||||
throw new Error(`Unsupported OpenAPI version: ${root.openapi || root.swagger}`);
|
||||
}
|
||||
|
||||
if (root.asyncapi && root.asyncapi.startsWith('2.')) {
|
||||
if (typeof root.asyncapi === 'string' && root.asyncapi.startsWith('2.')) {
|
||||
return SpecVersion.Async2;
|
||||
}
|
||||
|
||||
if (root.asyncapi && root.asyncapi.startsWith('3.')) {
|
||||
if (typeof root.asyncapi === 'string' && root.asyncapi.startsWith('3.')) {
|
||||
return SpecVersion.Async3;
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ export function detectSpec(root: any): SpecVersion {
|
||||
throw new Error(`Unsupported AsyncAPI version: ${root.asyncapi}`);
|
||||
}
|
||||
|
||||
if (root.arazzo && root.arazzo.startsWith('1.')) {
|
||||
if (typeof root.arazzo === 'string' && root.arazzo.startsWith('1.')) {
|
||||
return SpecVersion.Arazzo;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,15 +38,15 @@ export function isDefined<T>(x: T | undefined): x is T {
|
||||
return x !== undefined;
|
||||
}
|
||||
|
||||
export function isPlainObject(value: any): value is Record<string, unknown> {
|
||||
export function isPlainObject(value: unknown): value is Record<string, unknown> {
|
||||
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
||||
}
|
||||
|
||||
export function isEmptyObject(value: any): value is Record<string, unknown> {
|
||||
export function isEmptyObject(value: unknown): value is Record<string, unknown> {
|
||||
return isPlainObject(value) && Object.keys(value).length === 0;
|
||||
}
|
||||
|
||||
export function isEmptyArray(value: any) {
|
||||
export function isEmptyArray(value: unknown) {
|
||||
return Array.isArray(value) && value.length === 0;
|
||||
}
|
||||
|
||||
@@ -317,3 +317,5 @@ export function dequal(foo: any, bar: any): boolean {
|
||||
|
||||
return foo !== foo && bar !== bar;
|
||||
}
|
||||
|
||||
export type CollectFn = (value: unknown) => void;
|
||||
|
||||
Reference in New Issue
Block a user