mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-28 17:22:41 +00:00
[UI] Display Camelized Operation ID in API Explorer (#29785)
* updates swagger-ui to display camelized operation ids in development * attempt to fix test timing issue * fixes issue stubbing environment in swagger-ui test * adds test for operation ids in production for swagger-ui component
This commit is contained in:
@@ -8,4 +8,9 @@
|
||||
/* align list items with container */
|
||||
.swagger-ember .swagger-ui .wrapper {
|
||||
padding: 0;
|
||||
|
||||
.opblock-summary-path-description-wrapper {
|
||||
width: min-content;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,11 +8,13 @@ import { service } from '@ember/service';
|
||||
import { action } from '@ember/object';
|
||||
import { tracked } from '@glimmer/tracking';
|
||||
import parseURL from 'core/utils/parse-url';
|
||||
import config from 'open-api-explorer/config/environment';
|
||||
import config from 'vault/config/environment';
|
||||
import openApiExplorerConfig from 'open-api-explorer/config/environment';
|
||||
import { guidFor } from '@ember/object/internals';
|
||||
import SwaggerUIBundle from 'swagger-ui-dist/swagger-ui-bundle.js';
|
||||
import { camelize } from '@ember/string';
|
||||
|
||||
const { APP } = config;
|
||||
const { APP } = openApiExplorerConfig;
|
||||
|
||||
export default class SwaggerUiComponent extends Component {
|
||||
@service auth;
|
||||
@@ -49,16 +51,41 @@ export default class SwaggerUiComponent extends Component {
|
||||
};
|
||||
}
|
||||
|
||||
// the operationId values in the spec are dasherized
|
||||
// camelize the values so they match the function names in the generated API client SDK
|
||||
CamelizeOperationIdPlugin() {
|
||||
return {
|
||||
wrapComponents: {
|
||||
operation:
|
||||
(Original, { React }) =>
|
||||
(props) => {
|
||||
const { operation } = props;
|
||||
const operationId = operation.get('operationId');
|
||||
|
||||
if (operationId) {
|
||||
return React.createElement(Original, {
|
||||
...props,
|
||||
operation: operation.set('operationId', camelize(operationId)),
|
||||
});
|
||||
}
|
||||
|
||||
return React.createElement(Original, props);
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
CONFIG = (SwaggerUIBundle, componentInstance) => {
|
||||
return {
|
||||
dom_id: `#${componentInstance.inputId}`,
|
||||
url: '/v1/sys/internal/specs/openapi',
|
||||
deepLinking: false,
|
||||
presets: [SwaggerUIBundle.presets.apis],
|
||||
plugins: [SwaggerUIBundle.plugins.DownloadUrl, this.SearchFilterPlugin],
|
||||
plugins: [SwaggerUIBundle.plugins.DownloadUrl, this.SearchFilterPlugin, this.CamelizeOperationIdPlugin],
|
||||
// 'list' expands tags, but not operations
|
||||
docExpansion: 'list',
|
||||
operationsSorter: 'alpha',
|
||||
displayOperationId: config.environment === 'development',
|
||||
filter: true,
|
||||
// this makes sure we show the x-vault- options
|
||||
showExtensions: true,
|
||||
|
||||
@@ -4,42 +4,51 @@
|
||||
*/
|
||||
|
||||
import { Factory } from 'miragejs';
|
||||
/* eslint-disable ember/avoid-leaking-state-in-ember-objects */
|
||||
|
||||
export default Factory.extend({
|
||||
openapi: '3.0.2',
|
||||
info: {
|
||||
title: 'HashiCorp Vault API',
|
||||
description: 'HTTP API that gives you full access to Vault. All API routes are prefixed with `/v1/`.',
|
||||
version: '1.0.0',
|
||||
license: {
|
||||
name: 'Mozilla Public License 2.0',
|
||||
url: 'https://www.mozilla.org/en-US/MPL/2.0',
|
||||
},
|
||||
},
|
||||
paths: {
|
||||
'/auth/token/create': {
|
||||
description: 'The token create path is used to create new tokens.',
|
||||
post: {
|
||||
summary: 'The token create path is used to create new tokens.',
|
||||
tags: ['auth'],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'OK',
|
||||
// set in afterCreate to avoid leaking state lint error
|
||||
info: null,
|
||||
paths: null,
|
||||
|
||||
afterCreate(spec) {
|
||||
spec.info = {
|
||||
title: 'HashiCorp Vault API',
|
||||
description: 'HTTP API that gives you full access to Vault. All API routes are prefixed with `/v1/`.',
|
||||
version: '1.0.0',
|
||||
license: {
|
||||
name: 'Mozilla Public License 2.0',
|
||||
url: 'https://www.mozilla.org/en-US/MPL/2.0',
|
||||
},
|
||||
};
|
||||
|
||||
spec.paths = {
|
||||
'/auth/token/create': {
|
||||
description: 'The token create path is used to create new tokens.',
|
||||
post: {
|
||||
summary: 'The token create path is used to create new tokens.',
|
||||
tags: ['auth'],
|
||||
operationId: 'token-create',
|
||||
responses: {
|
||||
200: {
|
||||
description: 'OK',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
'/secret/data/{path}': {
|
||||
description: 'Location of a secret.',
|
||||
post: {
|
||||
summary: 'Location of a secret.',
|
||||
tags: ['secret'],
|
||||
responses: {
|
||||
200: {
|
||||
description: 'OK',
|
||||
'/secret/data/{path}': {
|
||||
description: 'Location of a secret.',
|
||||
post: {
|
||||
summary: 'Location of a secret.',
|
||||
tags: ['secret'],
|
||||
operationId: 'kv-v2-write',
|
||||
responses: {
|
||||
200: {
|
||||
description: 'OK',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
@@ -5,50 +5,48 @@
|
||||
|
||||
import { module, test } from 'qunit';
|
||||
import { setupRenderingTest } from 'vault/tests/helpers';
|
||||
import { fillIn, render, typeIn, waitFor } from '@ember/test-helpers';
|
||||
import { fillIn, render, typeIn } from '@ember/test-helpers';
|
||||
import { setupEngine } from 'ember-engines/test-support';
|
||||
import { setupMirage } from 'ember-cli-mirage/test-support';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import sinon from 'sinon';
|
||||
import config from 'vault/config/environment';
|
||||
import { camelize } from '@ember/string';
|
||||
|
||||
const SELECTORS = {
|
||||
container: '[data-test-swagger-ui]',
|
||||
searchInput: 'input.operation-filter-input',
|
||||
apiPathBlock: '.opblock-post',
|
||||
operationId: '.opblock-summary-operation-id',
|
||||
};
|
||||
|
||||
module('Integration | Component | open-api-explorer | swagger-ui', function (hooks) {
|
||||
setupRenderingTest(hooks);
|
||||
setupEngine(hooks, 'open-api-explorer');
|
||||
setupMirage(hooks);
|
||||
|
||||
hooks.beforeEach(function () {
|
||||
this.store = this.owner.lookup('service:store');
|
||||
|
||||
const openApiResponse = this.server.create('open-api-explorer');
|
||||
this.openApiResponse = this.server.create('open-api-explorer');
|
||||
this.server.get('sys/internal/specs/openapi', () => {
|
||||
return openApiResponse;
|
||||
return this.openApiResponse;
|
||||
});
|
||||
|
||||
this.totalApiPaths = Object.keys(openApiResponse.paths).length;
|
||||
this.totalApiPaths = Object.keys(this.openApiResponse.paths).length;
|
||||
|
||||
this.renderComponent = async () => {
|
||||
await render(hbs`<SwaggerUi/>`, {
|
||||
owner: this.engine,
|
||||
});
|
||||
};
|
||||
this.renderComponent = () => render(hbs`<SwaggerUi/>`, { owner: this.engine });
|
||||
});
|
||||
|
||||
test(`it renders`, async function (assert) {
|
||||
test('it renders', async function (assert) {
|
||||
await this.renderComponent();
|
||||
|
||||
await waitFor(SELECTORS.container);
|
||||
|
||||
assert.dom(SELECTORS.container).exists('renders component');
|
||||
assert.dom(SELECTORS.apiPathBlock).exists({ count: this.totalApiPaths }, 'renders all api paths');
|
||||
});
|
||||
|
||||
test(`it can search`, async function (assert) {
|
||||
test('it can search', async function (assert) {
|
||||
await this.renderComponent();
|
||||
|
||||
// in testing only the input is not filling correctly except after the second time
|
||||
await fillIn(SELECTORS.searchInput, 'moot');
|
||||
await typeIn(SELECTORS.searchInput, 'token');
|
||||
@@ -57,4 +55,25 @@ module('Integration | Component | open-api-explorer | swagger-ui', function (hoo
|
||||
// if the search fn breaks, this test will fail
|
||||
assert.dom(SELECTORS.searchInput).hasValue('token', 'search input has value');
|
||||
});
|
||||
|
||||
test('it should render camelized operation ids', async function (assert) {
|
||||
const envStub = sinon.stub(config, 'environment').value('development');
|
||||
|
||||
await this.renderComponent();
|
||||
|
||||
const id = this.openApiResponse.paths['/auth/token/create'].post.operationId;
|
||||
assert.dom(SELECTORS.operationId).hasText(camelize(id), 'renders camelized operation id');
|
||||
|
||||
envStub.restore();
|
||||
});
|
||||
|
||||
test('it should not render operation ids in production', async function (assert) {
|
||||
const envStub = sinon.stub(config, 'environment').value('production');
|
||||
|
||||
await this.renderComponent();
|
||||
|
||||
assert.dom(SELECTORS.operationId).doesNotExist('operation ids are hidden in production environment');
|
||||
|
||||
envStub.restore();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user