vault/ui/tests/unit/utils/openapi-helpers-test.js
Chelsea Shaw 8d6d26e531
UI: Refactor path-help service (#28444)
* Add helper combineOpenApiAttrs + test

* hydrateModel working with upgradeModelSchema

* new registerNewModelWithAttrs method for generated models

* Add newFields to generated models

* copyright

* Glimmerize path-help service

* update generated-item-list adapter and path-help usage of it

* remove unused methods combineAttributes and combineFields

* move expandOpenApiProps to ts helper file

* fix auth test

* fix bug where adding user to second userpass mount saves to first mount

* Add mutableId

* fix ent test

* remove addressed deprecation

* Address PR comments

* [VAULT-31208] remove deprecation early-static from decorator tests
2024-09-25 18:17:48 +00:00

295 lines
8.1 KiB
JavaScript

/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import {
_getPathParam,
combineOpenApiAttrs,
expandOpenApiProps,
getHelpUrlForModel,
pathToHelpUrlSegment,
} from 'vault/utils/openapi-helpers';
import Model, { attr } from '@ember-data/model';
import { setupTest } from 'ember-qunit';
import { camelize } from '@ember/string';
module('Unit | Utility | OpenAPI helper utils', function (hooks) {
setupTest(hooks);
test(`pathToHelpUrlSegment`, function (assert) {
[
{ path: '/auth/{username}', result: '/auth/example' },
{ path: '{username}/foo', result: 'example/foo' },
{ path: 'foo/{username}/bar', result: 'foo/example/bar' },
{ path: '', result: '' },
{ path: undefined, result: '' },
].forEach((test) => {
assert.strictEqual(pathToHelpUrlSegment(test.path), test.result, `translates ${test.path}`);
});
});
test(`_getPathParam`, function (assert) {
[
{ path: '/auth/{username}', result: 'username' },
{ path: '{unicorn}/foo', result: 'unicorn' },
{ path: 'foo/{bigfoot}/bar', result: 'bigfoot' },
{ path: '{alphabet}/bowl/{soup}', result: 'alphabet' },
{ path: 'no/params', result: false },
{ path: '', result: false },
{ path: undefined, result: false },
].forEach((test) => {
assert.strictEqual(_getPathParam(test.path), test.result, `returns first param for ${test.path}`);
});
});
test(`getHelpUrlForModel`, function (assert) {
[
{ modelType: 'kmip/config', result: '/v1/foobar/config?help=1' },
{ modelType: 'does-not-exist', result: null },
{ modelType: 4, result: null },
{ modelType: '', result: null },
{ modelType: undefined, result: null },
].forEach((test) => {
assert.strictEqual(
getHelpUrlForModel(test.modelType, 'foobar'),
test.result,
`returns first param for ${test.path}`
);
});
});
test('combineOpenApiAttrs should combine attributes correctly', async function (assert) {
class FooModel extends Model {
@attr('string', {
label: 'Foo',
subText: 'A form field',
})
foo;
@attr('boolean', {
label: 'Bar',
subText: 'Maybe a checkbox',
})
bar;
@attr('number', {
label: 'Baz',
subText: 'A number field',
})
baz;
}
this.owner.register('model:foo', FooModel);
const myModel = this.owner.lookup('service:store').modelFor('foo');
const newProps = {
foo: {
editType: 'ttl',
},
baz: {
type: 'number',
editType: 'slider',
label: 'Old label',
},
foobar: {
type: 'string',
label: 'Foo-bar',
},
};
const expected = [
{
name: 'foo',
type: 'string',
options: {
label: 'Foo',
subText: 'A form field',
editType: 'ttl',
},
},
{
name: 'bar',
type: 'boolean',
options: {
label: 'Bar',
subText: 'Maybe a checkbox',
},
},
{
name: 'baz',
type: 'number',
options: {
label: 'Baz', // uses the value we set on the model
editType: 'slider',
subText: 'A number field',
},
},
{
name: 'foobar',
type: 'string',
options: {
label: 'Foo-bar',
},
},
];
const { attrs, newFields } = combineOpenApiAttrs(myModel.attributes, newProps);
assert.deepEqual(newFields, ['foobar'], 'correct newFields added');
// When combineOpenApiAttrs
assert.strictEqual(attrs.length, 4, 'correct number of attributes returned');
expected.forEach((exp) => {
const name = exp.name;
const attr = attrs.find((a) => a.name === name);
assert.deepEqual(attr, exp, `${name} combined properly`);
});
});
module('expandopenApiProps', function () {
const OPENAPI_RESPONSE_PROPS = {
ttl: {
type: 'string',
format: 'seconds',
description: 'this is a TTL!',
'x-vault-displayAttrs': {
name: 'TTL',
},
},
'awesome-people': {
type: 'array',
items: {
type: 'string',
},
'x-vault-displayAttrs': {
value: 'Grace Hopper,Lady Ada',
},
},
'favorite-ice-cream': {
type: 'string',
enum: ['vanilla', 'chocolate', 'strawberry'],
},
'default-value': {
default: 30,
'x-vault-displayAttrs': {
value: 300,
},
type: 'integer',
},
default: {
'x-vault-displayAttrs': {
value: 30,
},
type: 'integer',
},
'super-secret': {
type: 'string',
'x-vault-displayAttrs': {
sensitive: true,
},
description: 'A really secret thing',
},
};
const EXPANDED_PROPS = {
ttl: {
helpText: 'this is a TTL!',
editType: 'ttl',
label: 'TTL',
fieldGroup: 'default',
},
awesomePeople: {
editType: 'stringArray',
defaultValue: 'Grace Hopper,Lady Ada',
fieldGroup: 'default',
},
favoriteIceCream: {
editType: 'string',
type: 'string',
possibleValues: ['vanilla', 'chocolate', 'strawberry'],
fieldGroup: 'default',
},
defaultValue: {
editType: 'number',
type: 'number',
defaultValue: 300,
fieldGroup: 'default',
},
default: {
editType: 'number',
type: 'number',
defaultValue: 30,
fieldGroup: 'default',
},
superSecret: {
type: 'string',
editType: 'string',
sensitive: true,
helpText: 'A really secret thing',
fieldGroup: 'default',
},
};
const OPENAPI_DESCRIPTIONS = {
token_bound_cidrs: {
type: 'array',
description:
'Comma separated string or JSON list of CIDR blocks. If set, specifies the blocks of IP addresses which are allowed to use the generated token.',
items: {
type: 'string',
},
'x-vault-displayAttrs': {
description:
'List of CIDR blocks. If set, specifies the blocks of IP addresses which are allowed to use the generated token.',
name: "Generated Token's Bound CIDRs",
group: 'Tokens',
},
},
blah_blah: {
type: 'array',
description: 'Comma-separated list of policies',
items: {
type: 'string',
},
'x-vault-displayAttrs': {
name: "Generated Token's Policies",
group: 'Tokens',
},
},
only_display_description: {
type: 'array',
items: {
type: 'string',
},
'x-vault-displayAttrs': {
description: 'Hello there, you look nice today',
},
},
};
const STRING_ARRAY_DESCRIPTIONS = {
token_bound_cidrs: {
helpText:
'List of CIDR blocks. If set, specifies the blocks of IP addresses which are allowed to use the generated token.',
},
blah_blah: {
helpText: 'Comma-separated list of policies',
},
only_display_description: {
helpText: 'Hello there, you look nice today',
},
};
test('it creates objects from OpenAPI schema props', function (assert) {
assert.expect(6);
const generatedProps = expandOpenApiProps(OPENAPI_RESPONSE_PROPS);
for (const propName in EXPANDED_PROPS) {
assert.deepEqual(EXPANDED_PROPS[propName], generatedProps[propName], `correctly expands ${propName}`);
}
});
test('it uses the description from the display attrs block if it exists', async function (assert) {
assert.expect(3);
const generatedProps = expandOpenApiProps(OPENAPI_DESCRIPTIONS);
for (const propName in STRING_ARRAY_DESCRIPTIONS) {
assert.strictEqual(
generatedProps[camelize(propName)].helpText,
STRING_ARRAY_DESCRIPTIONS[propName].helpText,
`correctly updates helpText for ${propName}`
);
}
});
});
});