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

perf(openapi): upload improvements #518

Merged
merged 13 commits into from
Jun 23, 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
14 changes: 8 additions & 6 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: "/"
directory: '/'
schedule:
interval: monthly
reviewers:
Expand All @@ -13,7 +13,7 @@ updates:
prefix-development: chore(deps-dev)

- package-ecosystem: npm
directory: "/"
directory: '/'
schedule:
interval: monthly
open-pull-requests-limit: 10
Expand All @@ -28,11 +28,13 @@ updates:
# All of these packages are now ESM-only and can't be used here without a rewrite.
- dependency-name: chalk
versions:
- ">= 5"
- '>= 5'
- dependency-name: configstore
versions:
- ">= 6"
- '>= 6'
- dependency-name: node-fetch
versions:
- ">= 3"

- '>= 3'
- dependency-name: ora
versions:
- '>= 6'
104 changes: 87 additions & 17 deletions __tests__/cmds/openapi.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ const getCommandOutput = () => {
return [console.warn.mock.calls.join('\n\n'), console.info.mock.calls.join('\n\n')].filter(Boolean).join('\n\n');
};

const getRandomRegistryId = () => Math.random().toString(36).substring(2);

describe('rdme openapi', () => {
beforeAll(() => nock.disableNetConnect());

Expand All @@ -69,14 +71,18 @@ describe('rdme openapi', () => {
['OpenAPI 3.1', 'json', '3.1', 'OpenAPI'],
['OpenAPI 3.1', 'yaml', '3.1', 'OpenAPI'],
])('should support uploading a %s definition (format: %s)', async (_, format, specVersion, type) => {
const registryUUID = getRandomRegistryId();

const mock = getApiNock()
.post('/api/v1/api-registry', body => body.match('form-data; name="spec"'))
.reply(201, { registryUUID, spec: { openapi: specVersion } })
.get('/api/v1/api-specification')
.basicAuth({ user: key })
.reply(200, [])
.get(`/api/v1/version/${version}`)
.basicAuth({ user: key })
.reply(200, { version: '1.0.0' })
.post('/api/v1/api-specification', body => body.match('form-data; name="spec"'))
.post('/api/v1/api-specification', { registryUUID })
.basicAuth({ user: key })
.reply(201, { _id: 1 }, { location: exampleRefLocation });

Expand All @@ -98,17 +104,21 @@ describe('rdme openapi', () => {
it('should discover and upload an API definition if none is provided', async () => {
promptHandler.createOasPrompt.mockResolvedValue({ option: 'create' });

const registryUUID = getRandomRegistryId();

Check notice

Code scanning

Insecure randomness

Cryptographically insecure [random value](1) in a security context.

const mock = getApiNock()
.get('/api/v1/version')
.basicAuth({ user: key })
.reply(200, [{ version }])
.post('/api/v1/api-registry', body => body.match('form-data; name="spec"'))
.reply(201, { registryUUID, spec: { openapi: '3.0.0' } })
.post('/api/v1/version')
.basicAuth({ user: key })
.reply(200, { from: '1.0.1', version: '1.0.1' })
.get('/api/v1/api-specification')
.basicAuth({ user: key })
.reply(200, [])
.post('/api/v1/api-specification', body => body.match('form-data; name="spec"'))
.post('/api/v1/api-specification', { registryUUID })
.delayConnection(1000)
.basicAuth({ user: key })
.reply(201, { _id: 1 }, { location: exampleRefLocation });
Expand Down Expand Up @@ -139,8 +149,12 @@ describe('rdme openapi', () => {
['OpenAPI 3.1', 'json', '3.1', 'OpenAPI'],
['OpenAPI 3.1', 'yaml', '3.1', 'OpenAPI'],
])('should support updating a %s definition (format: %s)', async (_, format, specVersion, type) => {
const registryUUID = getRandomRegistryId();

Check notice

Code scanning

Insecure randomness

Cryptographically insecure [random value](1) in a security context.

const mock = getApiNock()
.put(`/api/v1/api-specification/${id}`, body => body.match('form-data; name="spec"'))
.post('/api/v1/api-registry', body => body.match('form-data; name="spec"'))
.reply(201, { registryUUID, spec: { openapi: specVersion } })
.put(`/api/v1/api-specification/${id}`, { registryUUID })
.basicAuth({ user: key })
.reply(201, { _id: 1 }, { location: exampleRefLocation });

Expand All @@ -159,8 +173,12 @@ describe('rdme openapi', () => {
});

it('should return warning if providing `id` and `version`', async () => {
const registryUUID = getRandomRegistryId();

Check notice

Code scanning

Insecure randomness

Cryptographically insecure [random value](1) in a security context.

const mock = getApiNock()
.put(`/api/v1/api-specification/${id}`, body => body.match('form-data; name="spec"'))
.post('/api/v1/api-registry', body => body.match('form-data; name="spec"'))
.reply(201, { registryUUID, spec: { openapi: '3.0.0' } })
.put(`/api/v1/api-specification/${id}`, { registryUUID })
.basicAuth({ user: key })
.reply(201, { _id: 1 }, { location: exampleRefLocation });

Expand Down Expand Up @@ -217,17 +235,21 @@ describe('rdme openapi', () => {
newVersion: '1.0.1',
});

const registryUUID = getRandomRegistryId();

Check notice

Code scanning

Insecure randomness

Cryptographically insecure [random value](1) in a security context.

const mock = getApiNock()
.get('/api/v1/version')
.basicAuth({ user: key })
.reply(200, [{ version: '1.0.0' }])
.post('/api/v1/version')
.basicAuth({ user: key })
.reply(200, { from: '1.0.0', version: '1.0.1' })
.post('/api/v1/api-registry', body => 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', body => body.match('form-data; name="spec"'))
.post('/api/v1/api-specification', { registryUUID })
.basicAuth({ user: key })
.reply(201, { _id: 1 }, { location: exampleRefLocation });

Expand All @@ -241,19 +263,22 @@ describe('rdme openapi', () => {

it('should bundle and upload the expected content', async () => {
let requestBody = null;
const registryUUID = getRandomRegistryId();

Check notice

Code scanning

Insecure randomness

Cryptographically insecure [random value](1) in a security context.
const mock = getApiNock()
.get('/api/v1/api-specification')
.basicAuth({ user: key })
.reply(200, [])
.get(`/api/v1/version/${version}`)
.basicAuth({ user: key })
.reply(200, { version: '1.0.0' })
.post('/api/v1/api-specification', body => {
.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(201, { _id: 1 }, { location: exampleRefLocation });

Expand All @@ -270,19 +295,22 @@ describe('rdme openapi', () => {

it('should use specified working directory and upload the expected content', async () => {
let requestBody = null;
const registryUUID = getRandomRegistryId();

Check notice

Code scanning

Insecure randomness

Cryptographically insecure [random value](1) in a security context.
const mock = getApiNock()
.get('/api/v1/api-specification')
.basicAuth({ user: key })
.reply(200, [])
.get(`/api/v1/version/${version}`)
.basicAuth({ user: key })
.reply(200, { version: '1.0.0' })
.post('/api/v1/api-specification', body => {
.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(201, { _id: 1 }, { location: exampleRefLocation });

Expand Down Expand Up @@ -367,14 +395,18 @@ describe('rdme openapi', () => {
help: 'If you need help, email [email protected] and mention log "fake-metrics-uuid".',
};

const registryUUID = getRandomRegistryId();

Check notice

Code scanning

Insecure randomness

Cryptographically insecure [random value](1) in a security context.

const mock = getApiNock()
.get(`/api/v1/version/${version}`)
.basicAuth({ user: key })
.reply(200, { version: '1.0.0' })
.post('/api/v1/api-registry', body => 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', body => body.match('form-data; name="spec"'))
.post('/api/v1/api-specification', { registryUUID })
.delayConnection(1000)
.basicAuth({ user: key })
.reply(500, errorObject);
Expand All @@ -390,6 +422,32 @@ describe('rdme openapi', () => {
return mock.done();
});

it('should throw an error if registry upload fails', async () => {
const errorObject = {
error: 'INTERNAL_ERROR',
message: 'Unknown error (Registry is offline? lol idk)',
suggestion: '...a suggestion to resolve the issue...',
help: 'If you need help, email [email protected] and mention log "fake-metrics-uuid".',
};

const mock = getApiNock()
.get(`/api/v1/version/${version}`)
.basicAuth({ user: key })
.reply(200, { version: '1.0.0' })
.post('/api/v1/api-registry', body => body.match('form-data; name="spec"'))
.reply(400, errorObject);

await expect(
openapi.run({
spec: './__tests__/__fixtures__/swagger-with-invalid-extensions.json',
key,
version,
})
).rejects.toStrictEqual(new APIError(errorObject));

return mock.done();
});

it('should error if API errors', async () => {
const errorObject = {
error: 'SPEC_VERSION_NOTFOUND',
Expand All @@ -399,14 +457,18 @@ describe('rdme openapi', () => {
help: 'If you need help, email [email protected] and mention log "fake-metrics-uuid".',
};

const registryUUID = getRandomRegistryId();

Check notice

Code scanning

Insecure randomness

Cryptographically insecure [random value](1) in a security context.

const mock = getApiNock()
.get(`/api/v1/version/${version}`)
.basicAuth({ user: key })
.reply(200, { version: '1.0.0' })
.post('/api/v1/api-registry', body => 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', body => body.match('form-data; name="spec"'))
.post('/api/v1/api-specification', { registryUUID })
.delayConnection(1000)
.basicAuth({ user: key })
.reply(400, errorObject);
Expand All @@ -419,14 +481,18 @@ describe('rdme openapi', () => {
});

it('should error if API errors (generic upload error)', async () => {
const registryUUID = getRandomRegistryId();

Check notice

Code scanning

Insecure randomness

Cryptographically insecure [random value](1) in a security context.

const mock = getApiNock()
.get(`/api/v1/version/${version}`)
.basicAuth({ user: key })
.reply(200, { version: '1.0.0' })
.post('/api/v1/api-registry', body => 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', body => body.match('form-data; name="spec"'))
.post('/api/v1/api-specification', { registryUUID })
.delayConnection(1000)
.basicAuth({ user: key })
.reply(400, 'some non-JSON upload error');
Expand All @@ -439,14 +505,18 @@ describe('rdme openapi', () => {
});

it('should error if API errors (request timeout)', async () => {
const registryUUID = getRandomRegistryId();

Check notice

Code scanning

Insecure randomness

Cryptographically insecure [random value](1) in a security context.

const mock = getApiNock()
.get(`/api/v1/version/${version}`)
.basicAuth({ user: key })
.reply(200, { version: '1.0.0' })
.post('/api/v1/api-registry', body => 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', body => body.match('form-data; name="spec"'))
.post('/api/v1/api-specification', { registryUUID })
.delayConnection(1000)
.basicAuth({ user: key })
.reply(400, '<html></html>');
Expand Down
Loading