Merge pull request #845 from hashicorp/feature-auth-methods-filters
Auth methods filters
This commit is contained in:
18 files changed
+233
-29
No files matched your search
@@ -52,7 +52,7 @@ export class ResourceFilter {
|
||||
* @return {string}
|
||||
*/
|
||||
equals(key, value) {
|
||||
return `"/item/${key}" == "${value}"`;
|
||||
return value !== null ? `"/item/${key}" == "${value}"` : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -76,7 +76,7 @@ export class ResourceFilter {
|
||||
* @return {string}
|
||||
*/
|
||||
parenthetical(expression) {
|
||||
return `(${expression})`;
|
||||
return expression ? `(${expression})` : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,8 +55,9 @@ export default function () {
|
||||
|
||||
this.get(
|
||||
'/auth-methods',
|
||||
({ authMethods }, { queryParams: { scope_id: scopeId } }) => {
|
||||
return authMethods.where({ scopeId });
|
||||
({ authMethods }, { queryParams: { scope_id: scopeId, filter } }) => {
|
||||
const resultSet = authMethods.where({ scopeId });
|
||||
return resultSet.filter(makeBooleanFilter(filter));
|
||||
}
|
||||
);
|
||||
this.post('/auth-methods');
|
||||
|
||||
@@ -22,7 +22,7 @@ export default function (server) {
|
||||
// Auth
|
||||
const globalAuthMethods = server.createList(
|
||||
'auth-method',
|
||||
4,
|
||||
1,
|
||||
{ scope: globalScope },
|
||||
'withAccountsAndUsersAndManagedGroups'
|
||||
);
|
||||
|
||||
@@ -46,6 +46,9 @@ export function resourceFilterParam(allowedValues, defaultValue) {
|
||||
// If the route has no query params specified yet, declare them.
|
||||
if (!Object.keys(target.queryParams).length) target.queryParams = {};
|
||||
|
||||
// If the route has no resource filters list specified yet, create one.
|
||||
if (!target.resourceFilterParams) target.resourceFilterParams = [];
|
||||
|
||||
// Add the resource filter query parameter to the route. Resource filter
|
||||
// param changes refresh the model but do not contribute to browser history.
|
||||
// See https://guides.emberjs.com/release/routing/query-params/
|
||||
@@ -54,6 +57,9 @@ export function resourceFilterParam(allowedValues, defaultValue) {
|
||||
replace: true,
|
||||
};
|
||||
|
||||
// Note the name of this filter param.
|
||||
target.resourceFilterParams.push(name);
|
||||
|
||||
// Store the allowed values for future lookup
|
||||
// (via `ResourceFilterParamHelper`).
|
||||
target[filterAllowedValuesKey] = allowedValues;
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import Helper from '@ember/component/helper';
|
||||
import { inject as service } from '@ember/service';
|
||||
import { getOwner } from '@ember/application';
|
||||
import { action } from '@ember/object';
|
||||
|
||||
/**
|
||||
* Use this helper to determine if a resource filter for a route has any
|
||||
* currently selected value.
|
||||
*
|
||||
* @see resourceFilterParam (decorator)
|
||||
*
|
||||
* @example
|
||||
*
|
||||
* {{#if (has-resource-filter-param-selections 'route.name' 'field')}}
|
||||
* Field has filter selections.
|
||||
* {{/if}}
|
||||
*/
|
||||
export default class HasResourceFilterParamSelectionsHelper extends Helper {
|
||||
// =services
|
||||
|
||||
@service router;
|
||||
|
||||
// =methods
|
||||
|
||||
// =lifecycle management methods
|
||||
|
||||
/**
|
||||
* Recompute this helper when the route changes.
|
||||
*/
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.router.on('routeDidChange', this.routeDidChange);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop listening to route events when this helper is destroyed.
|
||||
*/
|
||||
willDestroy() {
|
||||
this.router.off('routeDidChange', this.routeDidChange);
|
||||
super.willDestroy();
|
||||
}
|
||||
|
||||
// =compute method
|
||||
|
||||
/**
|
||||
* Returns true if the specified route has a value set for
|
||||
* any resource filter.
|
||||
* @param {string} routeName
|
||||
* @return {boolean}
|
||||
*/
|
||||
compute([routeName]) {
|
||||
const owner = getOwner(this);
|
||||
const route = owner.lookup(`route:${routeName}`);
|
||||
const names = route.resourceFilterParams;
|
||||
const selectedValues = names.map((name) => route[name] || null).flat();
|
||||
const anyTruthy = selectedValues.reduce(
|
||||
(previousValue, currentValue) => previousValue || currentValue,
|
||||
false
|
||||
);
|
||||
return Boolean(anyTruthy);
|
||||
}
|
||||
|
||||
// =actions
|
||||
|
||||
/**
|
||||
* Triggers a recompute (see above) when the route changes, especially when
|
||||
* route query params change.
|
||||
*/
|
||||
@action
|
||||
routeDidChange() {
|
||||
this.recompute();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
export { default } from 'core/helpers/has-resource-filter-param-selections';
|
||||
@@ -37,10 +37,10 @@
|
||||
"ember-intl": "^5.7.0",
|
||||
"ember-loading": "^0.4.0",
|
||||
"ember-route-action-helper": "^2.0.8",
|
||||
"ember-truth-helpers": "^3.0.0",
|
||||
"rose": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"js-bexpr": "hashicorp/js-bexpr#9b4a4b54d85eba67fdfc0990133d1518d890b1e1",
|
||||
"@ember/optional-features": "^2.0.0",
|
||||
"@ember/test-helpers": "^2.4.2",
|
||||
"@glimmer/component": "^1.0.4",
|
||||
@@ -73,6 +73,7 @@
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"eslint-plugin-qunit": "^6.2.0",
|
||||
"faker": "^5.5.3",
|
||||
"js-bexpr": "hashicorp/js-bexpr#9b4a4b54d85eba67fdfc0990133d1518d890b1e1",
|
||||
"loader.js": "^4.7.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^2.4.0",
|
||||
|
||||
@@ -35,3 +35,4 @@ close: Close
|
||||
toggle-secret: Toggle secret visibility
|
||||
minimize: Minimize
|
||||
fullscreen: Fullscreen
|
||||
clear-all-filters: Clear all filters
|
||||
@@ -29,11 +29,13 @@ titles:
|
||||
welcome-to-boundary: Welcome to Boundary
|
||||
label: Label
|
||||
value: Value
|
||||
no-results: No Results
|
||||
descriptions:
|
||||
empty-set: There are no items to display yet. You may be able to add items or try back later.
|
||||
origin-initialization: To get started, please enter your origin URL
|
||||
create-but-not-list: You do not have permission to list {resource}, but you can create new instances.
|
||||
neither-list-nor-create: You do not have permission to list or create {resource}.
|
||||
no-results: We found no matching records.
|
||||
questions:
|
||||
delete-confirm: Are you sure you want to delete this resource?
|
||||
remove-confirm: Are you sure you want to remove this association?
|
||||
|
||||
@@ -18,6 +18,9 @@
|
||||
|
||||
<span class="rose-dropdown-trigger-text">
|
||||
{{@text}}
|
||||
{{#if @count}}
|
||||
<Rose::Badge>{{@count}}</Rose::Badge>
|
||||
{{/if}}
|
||||
</span>
|
||||
|
||||
</summary>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Meta, Story, Preview } from '@storybook/addon-docs/blocks';
|
||||
import { hbs } from 'ember-cli-htmlbars';
|
||||
import { boolean, object } from '@storybook/addon-knobs';
|
||||
import { number, boolean, object } from '@storybook/addon-knobs';
|
||||
import { action } from '@storybook/addon-actions';
|
||||
|
||||
<Meta title="Rose|Dropdown" component="Dropdown" />
|
||||
@@ -12,7 +12,7 @@ A simple dropdown component with support for links and buttons.
|
||||
<Preview>
|
||||
<Story name="Basic Dropdown" height="7rem">{{
|
||||
template: hbs`
|
||||
<Rose::Dropdown @text="Choose One…" @icon="flight-icons/svg/org-16" @showCaret={{showCaret}} as |dropdown|>
|
||||
<Rose::Dropdown @text="Choose One…" @count={{count}} @icon="flight-icons/svg/org-16" @showCaret={{showCaret}} as |dropdown|>
|
||||
<dropdown.link @route="index">Menu item</dropdown.link>
|
||||
<dropdown.link @route="about">Menu item</dropdown.link>
|
||||
<dropdown.separator />
|
||||
@@ -24,6 +24,7 @@ A simple dropdown component with support for links and buttons.
|
||||
</Rose::Dropdown>
|
||||
`,
|
||||
context: {
|
||||
count: number('count', 0),
|
||||
showCaret: boolean('showCaret', true),
|
||||
},
|
||||
}}</Story>
|
||||
|
||||
@@ -39,6 +39,11 @@ $chevronGrey: url("data:image/svg+xml;utf-8,<svg viewBox='0 0 24 24' fill='%2362
|
||||
border-radius: sizing.rems(xxxs);
|
||||
white-space: nowrap;
|
||||
|
||||
.rose-badge {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
// Hide default twistie triangle
|
||||
// Webkit / Chromium
|
||||
&::-webkit-details-marker {
|
||||
@@ -98,6 +103,10 @@ $chevronGrey: url("data:image/svg+xml;utf-8,<svg viewBox='0 0 24 24' fill='%2362
|
||||
|
||||
&[open] > .rose-dropdown-trigger {
|
||||
background-color: var(--ui-gray-subtler-4);
|
||||
|
||||
.rose-badge {
|
||||
--background-color: var(--ui-gray-subtler-3);
|
||||
}
|
||||
}
|
||||
|
||||
.rose-dropdown-content {
|
||||
|
||||
@@ -6,8 +6,11 @@ $xxxxs: sizing.rems(xxxxs); // 1
|
||||
|
||||
.rose-toolbar {
|
||||
background-color: var(--ui-gray-subtler-6);
|
||||
padding: $xxs + $xxxxs;
|
||||
|
||||
.rose-form {
|
||||
padding: $xxs + $xxxxs;
|
||||
button {
|
||||
display: inline-block;
|
||||
font-weight: normal;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,8 @@
|
||||
"ember-named-blocks-polyfill": "^0.2.4",
|
||||
"ember-radio-button": "^2.0.1",
|
||||
"ember-uuid": "^2.1.0",
|
||||
"emberx-select": "github:adopted-ember-addons/emberx-select#bc5f774"
|
||||
"emberx-select": "github:adopted-ember-addons/emberx-select#bc5f774",
|
||||
"ember-composable-helpers": "^4.5.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.5",
|
||||
@@ -65,7 +66,6 @@
|
||||
"ember-cli-sass": "^10.0.1",
|
||||
"ember-cli-sri": "^2.1.1",
|
||||
"ember-cli-terser": "^4.0.2",
|
||||
"ember-composable-helpers": "^4.5.0",
|
||||
"ember-disable-prototype-extensions": "^1.1.3",
|
||||
"ember-export-application-global": "^2.0.1",
|
||||
"ember-inline-svg": "^1.0.0",
|
||||
|
||||
@@ -23,6 +23,11 @@ module('Integration | Component | rose/dropdown', function (hooks) {
|
||||
assert.equal(find('.rose-dropdown-trigger').textContent.trim(), 'Click me');
|
||||
});
|
||||
|
||||
test('it supports a count', async function (assert) {
|
||||
await render(hbs`<Rose::Dropdown @text="Click me" @count="2" />`);
|
||||
assert.equal(find('.rose-badge').textContent.trim(), '2');
|
||||
});
|
||||
|
||||
test('it supports an icon', async function (assert) {
|
||||
await render(
|
||||
hbs`<Rose::Dropdown @icon="flight-icons/svg/user-circle-16" />`
|
||||
|
||||
@@ -4,6 +4,7 @@ import { action } from '@ember/object';
|
||||
import loading from 'ember-loading/decorator';
|
||||
import { confirm } from 'core/decorators/confirm';
|
||||
import { notifySuccess, notifyError } from 'core/decorators/notify';
|
||||
import { resourceFilterParam } from 'core/decorators/resource-filter-param';
|
||||
|
||||
export default class ScopesScopeAuthMethodsRoute extends Route {
|
||||
// =services
|
||||
@@ -12,6 +13,11 @@ export default class ScopesScopeAuthMethodsRoute extends Route {
|
||||
@service notify;
|
||||
@service session;
|
||||
@service can;
|
||||
@service resourceFilterStore;
|
||||
|
||||
// =attributes
|
||||
|
||||
@resourceFilterParam(['password', 'oidc']) type;
|
||||
|
||||
// =methods
|
||||
|
||||
@@ -32,7 +38,12 @@ export default class ScopesScopeAuthMethodsRoute extends Route {
|
||||
if (
|
||||
this.can.can('list collection', scope, { collection: 'auth-methods' })
|
||||
) {
|
||||
return this.store.query('auth-method', { scope_id });
|
||||
const { type } = this;
|
||||
return this.resourceFilterStore.queryBy(
|
||||
'auth-method',
|
||||
{ type },
|
||||
{ scope_id }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -185,4 +196,22 @@ export default class ScopesScopeAuthMethodsRoute extends Route {
|
||||
const value = { from, to };
|
||||
array.addObject(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the specified resource filter field to the specified value.
|
||||
* @param {string} field
|
||||
* @param value
|
||||
*/
|
||||
@action
|
||||
filterBy(field, value) {
|
||||
this[field] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears and filter selections.
|
||||
*/
|
||||
@action
|
||||
clearAllFilters() {
|
||||
this.type = [];
|
||||
}
|
||||
}
|
||||
@@ -35,8 +35,58 @@
|
||||
</page.actions>
|
||||
|
||||
<page.body>
|
||||
{{#if @model}}
|
||||
|
||||
{{#if
|
||||
(or
|
||||
@model
|
||||
(has-resource-filter-param-selections 'scopes.scope.auth-methods')
|
||||
)
|
||||
}}
|
||||
<Rose::Toolbar>
|
||||
<Rose::Form as |form|>
|
||||
{{#with
|
||||
(resource-filter-param 'scopes.scope.auth-methods' 'type')
|
||||
as |filter|
|
||||
}}
|
||||
<Rose::Dropdown
|
||||
@text={{t (concat 'form.' filter.name '.label')}}
|
||||
@count={{filter.selectedValue.length}}
|
||||
@ignoreClickInside={{true}}
|
||||
as |dropdown|
|
||||
>
|
||||
<form.checkboxGroup
|
||||
@name={{filter.name}}
|
||||
@items={{filter.allowedValues}}
|
||||
@selectedItems={{filter.selectedValue}}
|
||||
@onChange={{route-action 'filterBy' filter.name}}
|
||||
as |group|
|
||||
>
|
||||
<dropdown.item>
|
||||
<group.checkbox
|
||||
@label={{t
|
||||
(concat 'resources.auth-method.types.' group.item)
|
||||
}}
|
||||
value={{group.item}}
|
||||
/>
|
||||
</dropdown.item>
|
||||
</form.checkboxGroup>
|
||||
</Rose::Dropdown>
|
||||
{{/with}}
|
||||
|
||||
{{#if
|
||||
(has-resource-filter-param-selections 'scopes.scope.auth-methods')
|
||||
}}
|
||||
<Rose::Button
|
||||
@style='inline-link-action'
|
||||
@iconLeft='flight-icons/svg/x-16'
|
||||
{{on 'click' (route-action 'clearAllFilters')}}
|
||||
>{{t 'actions.clear-all-filters'}}</Rose::Button>
|
||||
{{/if}}
|
||||
</Rose::Form>
|
||||
</Rose::Toolbar>
|
||||
{{/if}}
|
||||
|
||||
{{#if @model}}
|
||||
{{#if (feature-flag 'primary-auth-method')}}
|
||||
{{#unless this.hasPrimaryAuthMethod}}
|
||||
<Rose::Notification
|
||||
@@ -157,6 +207,18 @@
|
||||
</table.body>
|
||||
</Rose::Table>
|
||||
|
||||
{{else if (has-resource-filter-param-selections
|
||||
'scopes.scope.auth-methods'
|
||||
)}}
|
||||
|
||||
<Rose::Layout::Centered>
|
||||
<Rose::Message @title={{t 'titles.no-results'}} as |message|>
|
||||
<message.description>
|
||||
{{t 'descriptions.no-results'}}
|
||||
</message.description>
|
||||
</Rose::Message>
|
||||
</Rose::Layout::Centered>
|
||||
|
||||
{{else}}
|
||||
|
||||
<Rose::Layout::Centered>
|
||||
|
||||
@@ -6993,14 +6993,14 @@ browserify-zlib@^0.2.0:
|
||||
pako "~1.0.5"
|
||||
|
||||
browserslist@4.14.2, browserslist@^4.0.0, browserslist@^4.12.0, browserslist@^4.14.5, browserslist@^4.16.5, browserslist@^4.16.6, browserslist@^4.16.7, browserslist@^4.17.3:
|
||||
version "4.17.4"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.4.tgz#72e2508af2a403aec0a49847ef31bd823c57ead4"
|
||||
integrity sha512-Zg7RpbZpIJRW3am9Lyckue7PLytvVxxhJj1CaJVlCWENsGEAOlnlt8X0ZxGRPp7Bt9o8tIRM5SEXy4BCPMJjLQ==
|
||||
version "4.17.6"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.6.tgz#c76be33e7786b497f66cad25a73756c8b938985d"
|
||||
integrity sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw==
|
||||
dependencies:
|
||||
caniuse-lite "^1.0.30001265"
|
||||
electron-to-chromium "^1.3.867"
|
||||
caniuse-lite "^1.0.30001274"
|
||||
electron-to-chromium "^1.3.886"
|
||||
escalade "^3.1.1"
|
||||
node-releases "^2.0.0"
|
||||
node-releases "^2.0.1"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
bser@2.1.1:
|
||||
@@ -7311,10 +7311,10 @@ caniuse-api@^3.0.0:
|
||||
lodash.memoize "^4.1.2"
|
||||
lodash.uniq "^4.5.0"
|
||||
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001157, caniuse-lite@^1.0.30001265:
|
||||
version "1.0.30001271"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001271.tgz#0dda0c9bcae2cf5407cd34cac304186616cc83e8"
|
||||
integrity sha512-BBruZFWmt3HFdVPS8kceTBIguKxu4f99n5JNp06OlPD/luoAMIaIK5ieV5YjnBLH3Nysai9sxj9rpJj4ZisXOA==
|
||||
caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001157, caniuse-lite@^1.0.30001274:
|
||||
version "1.0.30001279"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001279.tgz#eb06818da481ef5096a3b3760f43e5382ed6b0ce"
|
||||
integrity sha512-VfEHpzHEXj6/CxggTwSFoZBBYGQfQv9Cf42KPlO79sWXCD1QNKWKsKzFeWL7QpZHJQYAvocqV6Rty1yJMkqWLQ==
|
||||
|
||||
capture-exit@^2.0.0:
|
||||
version "2.0.0"
|
||||
@@ -9151,10 +9151,10 @@ electron-rebuild@^2.0.3:
|
||||
tar "^6.0.5"
|
||||
yargs "^16.0.0"
|
||||
|
||||
electron-to-chromium@^1.3.867:
|
||||
version "1.3.884"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.884.tgz#0cd8c3a80271fd84a81f284c60fb3c9ecb33c166"
|
||||
integrity sha512-kOaCAa+biA98PwH5BpCkeUeTL6mCeg8p3Q3OhqzPyqhu/5QUnWAN2wr/3IK8xMQxIV76kfoQpP+Bn/wij/jXrg==
|
||||
electron-to-chromium@^1.3.886:
|
||||
version "1.3.895"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.895.tgz#9b0f8f2e32d8283bbb200156fd5d8dfd775f31ed"
|
||||
integrity sha512-9Ww3fB8CWctjqHwkOt7DQbMZMpal2x2reod+/lU4b9axO1XJEDUpPMBxs7YnjLhhqpKXIIB5SRYN/B4K0QpvyQ==
|
||||
|
||||
element-resize-detector@^1.2.2:
|
||||
version "1.2.3"
|
||||
@@ -10335,6 +10335,13 @@ ember-template-recast@^5.0.1, ember-template-recast@^5.0.3:
|
||||
tmp "^0.2.1"
|
||||
workerpool "^6.1.4"
|
||||
|
||||
ember-truth-helpers@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-truth-helpers/-/ember-truth-helpers-3.0.0.tgz#86766bdca4ac9b86bce3d262dff2aabc4a0ea384"
|
||||
integrity sha512-hPKG9QqruAELh0li5xaiLZtr88ioWYxWCXisAWHWE0qCP4a2hgtuMzKUPpiTCkltvKjuqpzTZCU4VhQ+IlRmew==
|
||||
dependencies:
|
||||
ember-cli-babel "^7.22.1"
|
||||
|
||||
ember-try-config@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ember-try-config/-/ember-try-config-3.0.0.tgz#012d8c90cae9eb624e2b62040bf7e76a1aa58edc"
|
||||
@@ -16669,7 +16676,7 @@ node-pre-gyp@^0.11.0:
|
||||
semver "^5.3.0"
|
||||
tar "^4"
|
||||
|
||||
node-releases@^2.0.0:
|
||||
node-releases@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.1.tgz#3d1d395f204f1f2f29a54358b9fb678765ad2fc5"
|
||||
integrity sha512-CqyzN6z7Q6aMeF/ktcMVTzhAHCEpf8SOarwpzpf8pNBY2k5/oM34UHldUwp8VKI7uxct2HxSRdJjBaZeESzcxA==
|
||||
|
||||
Reference in new issue
Block a user