Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(openapi): version handling improvements #559

Merged
merged 22 commits into from
Aug 10, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions __tests__/__fixtures__/petstore-simple-weird-version.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"openapi": "3.0.0",
"info": {
"version": "1.2.3",
"title": "Single Path",
"description": "This is a slimmed down single path version of the Petstore definition."
},
"servers": [
{
"url": "https://httpbin.org"
}
],
"paths": {
"/pet/{id}": {
"parameters": [
{
"name": "id",
"in": "path",
"required": true,
"schema": {
"type": "integer"
}
}
],
"put": {
"tags": ["pet"],
"summary": "Update a pet",
"description": "This operation will update a pet in the database.",
"responses": {
"400": {
"description": "Invalid id value"
}
},
"security": [
{
"apiKey": []
}
]
},
"get": {
"tags": ["pet"],
"summary": "Find a pet",
"description": "This operation will find a pet in the database.",
"responses": {
"400": {
"description": "Invalid status value"
}
},
"security": []
}
}
},
"components": {
"securitySchemes": {
"apiKey": {
"type": "http",
"scheme": "basic"
}
}
}
}
23 changes: 16 additions & 7 deletions __tests__/__snapshots__/index.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ Options

--key string Project API key
--id string Unique identifier for your API definition. Use this if you're re-
uploading an existing API definition
--version string Project version
uploading an existing API definition.
--version string Project version. If running command in a CI environment and this
option is not passed, the main project version will be used.
--useSpecVersion Uses the version listed in the \`info.version\` field in the API
definition for the project version parameter.
--workingDirectory string Working directory (for usage with relative external references)
-h, --help Display this usage guide

Expand All @@ -36,8 +39,11 @@ Options

--key string Project API key
--id string Unique identifier for your API definition. Use this if you're re-
uploading an existing API definition
--version string Project version
uploading an existing API definition.
--version string Project version. If running command in a CI environment and this
option is not passed, the main project version will be used.
--useSpecVersion Uses the version listed in the \`info.version\` field in the API
definition for the project version parameter.
--workingDirectory string Working directory (for usage with relative external references)
-h, --help Display this usage guide

Expand All @@ -60,8 +66,11 @@ Options

--key string Project API key
--id string Unique identifier for your API definition. Use this if you're re-
uploading an existing API definition
--version string Project version
uploading an existing API definition.
--version string Project version. If running command in a CI environment and this
option is not passed, the main project version will be used.
--useSpecVersion Uses the version listed in the \`info.version\` field in the API
definition for the project version parameter.
--workingDirectory string Working directory (for usage with relative external references)
-h, --help Display this usage guide

Expand All @@ -83,7 +92,7 @@ Usage
Options

--key string Project API key
--version string A specific project version to view
--version string A specific project version to view.
--raw Return raw output from the API instead of in a \\"pretty\\" format.
-h, --help Display this usage guide

Expand Down
2 changes: 1 addition & 1 deletion __tests__/cmds/docs/edit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ describe('rdme docs:edit', () => {

expect(fs.existsSync(`${slug}.md`)).toBe(false);
// eslint-disable-next-line no-console
expect(console.info).toHaveBeenCalledWith('Doc successfully updated. Cleaning up local file.');
expect(console.info).toHaveBeenCalledWith('ℹ️ Doc successfully updated. Cleaning up local file.');
consoleSpy.mockRestore();
});

Expand Down
119 changes: 109 additions & 10 deletions __tests__/cmds/openapi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ describe('rdme openapi', () => {
expect(console.info).toHaveBeenCalledTimes(1);

const output = getCommandOutput();
expect(output).toBe(chalk.yellow('We found swagger.json and are attempting to upload it.'));
expect(output).toBe(chalk.yellow('ℹ️ We found swagger.json and are attempting to upload it.'));

fs.unlinkSync('./swagger.json');
return mock.done();
Expand Down Expand Up @@ -277,6 +277,110 @@ describe('rdme openapi', () => {
});

describe('versioning', () => {
it('should use version from version param properly', async () => {
expect.assertions(2);
let requestBody = '';
const registryUUID = getRandomRegistryId();
const mock = getAPIMock()
.get(`/api/v1/version/${version}`)
.basicAuth({ user: key })
.reply(200, { version: '1.0.0' })
.post('/api/v1/api-registry', body => {
requestBody = body.substring(body.indexOf('{'), body.lastIndexOf('}') + 1);
requestBody = JSON.parse(requestBody);

return body.match('form-data; name="spec"');
})
.reply(201, { registryUUID, spec: { openapi: '3.0.0' } })
.get('/api/v1/api-specification')
.basicAuth({ user: key })
.reply(200, [])
.post('/api/v1/api-specification', { registryUUID })
.basicAuth({ user: key })
.reply(function (uri, rBody, cb) {
expect(this.req.headers['x-readme-version'][0]).toBe(version);
return cb(null, [201, { _id: 1 }, { location: exampleRefLocation }]);
});

const spec = './__tests__/__fixtures__/petstore-simple-weird-version.json';

await expect(openapi.run({ spec, key, version })).resolves.toBe(successfulUpload(spec));

return mock.done();
});

it('should use version from spec file properly', async () => {
expect.assertions(2);
const specVersion = '1.2.3';
let requestBody = '';
const registryUUID = getRandomRegistryId();
const mock = getAPIMock()
.get(`/api/v1/version/${specVersion}`)
.basicAuth({ user: key })
.reply(200, { version: specVersion })
.post('/api/v1/api-registry', body => {
requestBody = body.substring(body.indexOf('{'), body.lastIndexOf('}') + 1);
requestBody = JSON.parse(requestBody);

return body.match('form-data; name="spec"');
})
.reply(201, { registryUUID, spec: { openapi: '3.0.0' } })
.get('/api/v1/api-specification')
.basicAuth({ user: key })
.reply(200, [])
.post('/api/v1/api-specification', { registryUUID })
.basicAuth({ user: key })
.reply(function (uri, rBody, cb) {
expect(this.req.headers['x-readme-version'][0]).toBe(specVersion);
return cb(null, [201, { _id: 1 }, { location: exampleRefLocation }]);
});

const spec = './__tests__/__fixtures__/petstore-simple-weird-version.json';

await expect(openapi.run({ spec, key, version, useSpecVersion: true })).resolves.toBe(successfulUpload(spec));

return mock.done();
});

describe('CI version handling', () => {
beforeEach(() => {
process.env.TEST_CI = 'true';
});

afterEach(() => {
delete process.env.TEST_CI;
});

it('should omit version header in CI environment', async () => {
expect.assertions(2);
let requestBody = '';
const registryUUID = getRandomRegistryId();
const mock = getAPIMock()
.post('/api/v1/api-registry', body => {
requestBody = body.substring(body.indexOf('{'), body.lastIndexOf('}') + 1);
requestBody = JSON.parse(requestBody);

return body.match('form-data; name="spec"');
})
.reply(201, { registryUUID, spec: { openapi: '3.0.0' } })
.get('/api/v1/api-specification')
.basicAuth({ user: key })
.reply(200, [])
.post('/api/v1/api-specification', { registryUUID })
.basicAuth({ user: key })
.reply(function (uri, rBody, cb) {
expect(this.req.headers['x-readme-version']).toBeUndefined();
return cb(null, [201, { _id: 1 }, { location: exampleRefLocation }]);
});

const spec = './__tests__/__fixtures__/ref-oas/petstore.json';

await expect(openapi.run({ spec, key })).resolves.toBe(successfulUpload(spec));

return mock.done();
});
});

it('should error if version flag sent to API returns a 404', async () => {
const invalidVersion = 'v1000';

Expand Down Expand Up @@ -372,15 +476,10 @@ describe('rdme openapi', () => {
return mock.done();
});

it('should error if no file was provided or able to be discovered', async () => {
const mock = getAPIMock()
.get(`/api/v1/version/${version}`)
.basicAuth({ user: key })
.reply(200, { version: '1.0.0' });

await expect(openapi.run({ key, version })).rejects.toThrow(/We couldn't find an OpenAPI or Swagger definition./);

return mock.done();
it('should error if no file was provided or able to be discovered', () => {
return expect(openapi.run({ key, version })).rejects.toThrow(
/We couldn't find an OpenAPI or Swagger definition./
);
});

it('should throw an error if an invalid OpenAPI 3.0 definition is supplied', () => {
Expand Down
2 changes: 1 addition & 1 deletion __tests__/cmds/validate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ describe('rdme validate', () => {
expect(console.info).toHaveBeenCalledTimes(1);

const output = getCommandOutput();
expect(output).toBe(chalk.yellow('We found swagger.json and are attempting to validate it.'));
expect(output).toBe(chalk.yellow('ℹ️ We found swagger.json and are attempting to validate it.'));

fs.unlinkSync('./swagger.json');
});
Expand Down
4 changes: 3 additions & 1 deletion __tests__/lib/commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,9 @@ describe('utils', () => {
}

expect(arg.description).toBe(
command.command !== 'versions' ? 'Project version' : 'A specific project version to view'
command.command !== 'versions'
? 'Project version. If running command in a CI environment and this option is not passed, the main project version will be used.'
: 'A specific project version to view.'
);
}
});
Expand Down
6 changes: 1 addition & 5 deletions src/cmds/categories/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,7 @@ export default class CategoriesCreateCommand extends Command {
type: String,
description: 'Project API key',
},
{
name: 'version',
type: String,
description: 'Project version',
},
this.getVersionArg(),
{
name: 'title',
type: String,
Expand Down
6 changes: 1 addition & 5 deletions src/cmds/categories/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,7 @@ export default class CategoriesCommand extends Command {
type: String,
description: 'Project API key',
},
{
name: 'version',
type: String,
description: 'Project version',
},
this.getVersionArg(),
];
}

Expand Down
6 changes: 1 addition & 5 deletions src/cmds/docs/edit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,7 @@ export default class EditDocsCommand extends Command {
type: String,
description: 'Project API key',
},
{
name: 'version',
type: String,
description: 'Project version',
},
this.getVersionArg(),
{
name: 'slug',
type: String,
Expand Down
6 changes: 1 addition & 5 deletions src/cmds/docs/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,7 @@ export default class DocsCommand extends Command {
type: String,
description: 'Project API key',
},
{
name: 'version',
type: String,
description: 'Project version',
},
this.getVersionArg(),
{
name: 'folder',
type: String,
Expand Down
6 changes: 1 addition & 5 deletions src/cmds/docs/single.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,7 @@ export default class SingleDocCommand extends Command {
type: String,
description: 'Project API key',
},
{
name: 'version',
type: String,
description: 'Project version',
},
this.getVersionArg(),
{
name: 'filePath',
type: String,
Expand Down
Loading