From 84511ba6d42b82c43589eff7de5462ecb0ab8a5f Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 9 Nov 2021 20:09:23 -0500 Subject: [PATCH 01/19] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20initial=20resource?= =?UTF-8?q?=20filter=20param=20system?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../addon/decorators/resource-filter-param.js | 83 +++++++++++++++++++ .../addon/helpers/resource-filter-param.js | 73 ++++++++++++++++ .../core/app/helpers/resource-filter-param.js | 1 + 3 files changed, 157 insertions(+) create mode 100644 addons/core/addon/decorators/resource-filter-param.js create mode 100644 addons/core/addon/helpers/resource-filter-param.js create mode 100644 addons/core/app/helpers/resource-filter-param.js diff --git a/addons/core/addon/decorators/resource-filter-param.js b/addons/core/addon/decorators/resource-filter-param.js new file mode 100644 index 000000000..db80bd832 --- /dev/null +++ b/addons/core/addon/decorators/resource-filter-param.js @@ -0,0 +1,83 @@ +/** + * A resource filter param is a concise way to express a route query parameter. + * Resource filter params abstract away a route's query parameter + * interface, treating a query param as a simple attribute on a route instance. + * Getting the attribute value is equivalent to looking up the associated query + * parameter. Setting the attribute is equivalent to transitioning to the route + * and passing the associated query parameter value. + * + * Additionally, resource filter params may declare `allowedValues`, an array + * containing the set of values the parameter may take. While unenforced, + * `allowedValues` are useful metadata about a resource filter and may be fetch + * via `ResourceFilterParamHelper`. + * + * @see ResourceFilterParamHelper + * + * @example + * + * export default class ScopesScopeAuthMethodsRoute extends Route { + * + * @resourceFilterParam(['active', 'pending']) + * status; + * + * @action + * changeStatus(status) { + * // Sets query param `filter-status` on the current route, which occurs + * // via a route transition. + * this.status = status; + * // Log the value of query param `filter-status`. + * console.log(this.status); + * } + * } + * + */ +export function resourceFilterParam(allowedValues) { + /** + * @param {object} target + * @param {string} name + * @param {object} descriptor + * @return {object{get, set}} + */ + return function (target, name /*, descriptor*/) { + const filterName = `filter-${name}`; + const filterOptionsName = `filter-options-${name}`; + + // If the route has no query params specified yet, declare them. + if (!Object.keys(target.queryParams).length) target.queryParams = {}; + + // 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/ + target.queryParams[filterName] = { + refreshModel: true, + replace: true, + }; + + // Store the allowed values for future lookup + // (via `ResourceFilterParamHelper`). + target[filterOptionsName] = allowedValues; + + // Override the decorated attribute with a getter and setter. + return { + /** + * Returns the JSON-parsed query parameter value. + */ + get() { + const value = this.currentRoute.queryParams[filterName]; + return value ? JSON.parse(value) : null; + }, + + /** + * Sets the resource filter param to the stringified representation + * of `value`. NOTE: this is equivalent to transitioning into the + * current route, changing the associated query param. + * @param value + */ + set(value) { + const queryParams = {}; + queryParams[filterName] = JSON.stringify(value); + this.transitionTo({ queryParams }); + }, + }; + }; +} diff --git a/addons/core/addon/helpers/resource-filter-param.js b/addons/core/addon/helpers/resource-filter-param.js new file mode 100644 index 000000000..98eb791bf --- /dev/null +++ b/addons/core/addon/helpers/resource-filter-param.js @@ -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 fetch the current value of a resource filter param, + * as well as its allowedValues. + * + * @see resourceFilterParam (decorator) + * + * @example + * + * {{#with resource-filter-param 'status' as |param|}} + * {{param.name}} / {{param.value}} / {{param.selectedValue}} + * {{/with}} + */ +export default class ResourceFilterParamHelper extends Helper { + // =services + + @service router; + + // =methods + + // =lifecycle management methods + + /** + * Recompute this helper when the route changes. + */ + constructor() { + super(...arguments); + this.router.on('routeWillChange', this.routeWillChange); + } + + /** + * Stop listening to route events when this helper is destroyed. + */ + willDestroy() { + this.router.off('routeWillChange', this.routeWillChange); + super.willDestroy(); + } + + // =compute method + + /** + * Returns an object containing `name`, `value`, and `selectedValue`. + * `name` - The name of the resource filter param. + * `value` - The value of the resource filter param + * @return {object} + */ + compute([routeName, name]) { + const owner = getOwner(this); + // Filter options + const route = owner.lookup(`route:${routeName}`); + const filterOptionsName = `filter-options-${name}`; + const value = route[filterOptionsName]; + // Selected filters + const rawValue = route[name]; + const selectedValue = rawValue ? JSON.parse(rawValue) : null; + return { name, value, selectedValue }; + } + + // =actions + + /** + * Triggers a recompute (see above) when the route changes, especially when + * route query params change. + */ + @action + routeWillChange() { + this.recompute(); + } +} diff --git a/addons/core/app/helpers/resource-filter-param.js b/addons/core/app/helpers/resource-filter-param.js new file mode 100644 index 000000000..8977c55f0 --- /dev/null +++ b/addons/core/app/helpers/resource-filter-param.js @@ -0,0 +1 @@ +export { default } from 'core/helpers/resource-filter-param'; From 9ae05e3cc4e736344f8a1765210f40fc3129f56d Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 10 Nov 2021 09:12:02 -0500 Subject: [PATCH 02/19] chore: add support for defaultValue --- .../core/addon/decorators/resource-filter-param.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/addons/core/addon/decorators/resource-filter-param.js b/addons/core/addon/decorators/resource-filter-param.js index db80bd832..2eeb3bf76 100644 --- a/addons/core/addon/decorators/resource-filter-param.js +++ b/addons/core/addon/decorators/resource-filter-param.js @@ -9,7 +9,8 @@ * Additionally, resource filter params may declare `allowedValues`, an array * containing the set of values the parameter may take. While unenforced, * `allowedValues` are useful metadata about a resource filter and may be fetch - * via `ResourceFilterParamHelper`. + * via `ResourceFilterParamHelper`. An optional default value may be specified + * via `defaultValue` passed to the decorator. * * @see ResourceFilterParamHelper * @@ -17,7 +18,7 @@ * * export default class ScopesScopeAuthMethodsRoute extends Route { * - * @resourceFilterParam(['active', 'pending']) + * @resourceFilterParam(['active', 'pending'], ['active']) * status; * * @action @@ -31,7 +32,7 @@ * } * */ -export function resourceFilterParam(allowedValues) { +export function resourceFilterParam(allowedValues, defaultValue) { /** * @param {object} target * @param {string} name @@ -55,16 +56,16 @@ export function resourceFilterParam(allowedValues) { // Store the allowed values for future lookup // (via `ResourceFilterParamHelper`). - target[filterOptionsName] = allowedValues; + target[filterOptionsName] = allowedValue; // Override the decorated attribute with a getter and setter. return { /** - * Returns the JSON-parsed query parameter value. + * Returns the JSON-parsed query parameter value OR defaultValue. */ get() { const value = this.currentRoute.queryParams[filterName]; - return value ? JSON.parse(value) : null; + return value ? JSON.parse(value) : defaultValue || null; }, /** From 4472b17fc5c02676b81aa4a9056e1b67f37198f9 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 10 Nov 2021 09:31:59 -0500 Subject: [PATCH 03/19] chore: add export --- addons/core/app/decorators/resource-filter-param.js | 1 + 1 file changed, 1 insertion(+) create mode 100644 addons/core/app/decorators/resource-filter-param.js diff --git a/addons/core/app/decorators/resource-filter-param.js b/addons/core/app/decorators/resource-filter-param.js new file mode 100644 index 000000000..4e5cebc3c --- /dev/null +++ b/addons/core/app/decorators/resource-filter-param.js @@ -0,0 +1 @@ +export { default } from 'core/decorators/resource-filter-param'; From 4f8ea30d9dd61df42aa60bc690c362e7ae0e9b56 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 10 Nov 2021 09:35:19 -0500 Subject: [PATCH 04/19] fix: bad variable name --- addons/core/addon/decorators/resource-filter-param.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/core/addon/decorators/resource-filter-param.js b/addons/core/addon/decorators/resource-filter-param.js index 2eeb3bf76..3815c94bb 100644 --- a/addons/core/addon/decorators/resource-filter-param.js +++ b/addons/core/addon/decorators/resource-filter-param.js @@ -56,7 +56,7 @@ export function resourceFilterParam(allowedValues, defaultValue) { // Store the allowed values for future lookup // (via `ResourceFilterParamHelper`). - target[filterOptionsName] = allowedValue; + target[filterOptionsName] = allowedValues; // Override the decorated attribute with a getter and setter. return { From 1562f0ff073d7664f583386fa961d0384d275b25 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 10 Nov 2021 09:37:46 -0500 Subject: [PATCH 05/19] fix: router --- addons/core/addon/decorators/resource-filter-param.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/core/addon/decorators/resource-filter-param.js b/addons/core/addon/decorators/resource-filter-param.js index 3815c94bb..3312ed744 100644 --- a/addons/core/addon/decorators/resource-filter-param.js +++ b/addons/core/addon/decorators/resource-filter-param.js @@ -64,7 +64,7 @@ export function resourceFilterParam(allowedValues, defaultValue) { * Returns the JSON-parsed query parameter value OR defaultValue. */ get() { - const value = this.currentRoute.queryParams[filterName]; + const value = this.router.currentRoute.queryParams[filterName]; return value ? JSON.parse(value) : defaultValue || null; }, From c1b60c634e11071277f36a4f50b0882bc7f4dbef Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 10 Nov 2021 09:45:53 -0500 Subject: [PATCH 06/19] fix: currentroute again --- addons/core/addon/decorators/resource-filter-param.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/core/addon/decorators/resource-filter-param.js b/addons/core/addon/decorators/resource-filter-param.js index 3312ed744..d87e00e4f 100644 --- a/addons/core/addon/decorators/resource-filter-param.js +++ b/addons/core/addon/decorators/resource-filter-param.js @@ -64,7 +64,7 @@ export function resourceFilterParam(allowedValues, defaultValue) { * Returns the JSON-parsed query parameter value OR defaultValue. */ get() { - const value = this.router.currentRoute.queryParams[filterName]; + const value = this._router.currentRoute.queryParams[filterName]; return value ? JSON.parse(value) : defaultValue || null; }, From dca54b2040a44b59a3888915a8a64015dbcc0ab2 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 10 Nov 2021 09:59:28 -0500 Subject: [PATCH 07/19] fix: key names --- .../core/addon/decorators/resource-filter-param.js | 12 ++++++------ addons/core/addon/helpers/resource-filter-param.js | 14 ++++++++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/addons/core/addon/decorators/resource-filter-param.js b/addons/core/addon/decorators/resource-filter-param.js index d87e00e4f..fda969722 100644 --- a/addons/core/addon/decorators/resource-filter-param.js +++ b/addons/core/addon/decorators/resource-filter-param.js @@ -40,8 +40,8 @@ export function resourceFilterParam(allowedValues, defaultValue) { * @return {object{get, set}} */ return function (target, name /*, descriptor*/) { - const filterName = `filter-${name}`; - const filterOptionsName = `filter-options-${name}`; + const filterKey = `filter-${name}`; + const filterAllowedValuesKey = `filter-allowed-values-${name}`; // If the route has no query params specified yet, declare them. if (!Object.keys(target.queryParams).length) target.queryParams = {}; @@ -49,14 +49,14 @@ export function resourceFilterParam(allowedValues, defaultValue) { // 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/ - target.queryParams[filterName] = { + target.queryParams[filterKey] = { refreshModel: true, replace: true, }; // Store the allowed values for future lookup // (via `ResourceFilterParamHelper`). - target[filterOptionsName] = allowedValues; + target[filterAllowedValuesKey] = allowedValues; // Override the decorated attribute with a getter and setter. return { @@ -64,7 +64,7 @@ export function resourceFilterParam(allowedValues, defaultValue) { * Returns the JSON-parsed query parameter value OR defaultValue. */ get() { - const value = this._router.currentRoute.queryParams[filterName]; + const value = this._router.currentRoute.queryParams[filterKey]; return value ? JSON.parse(value) : defaultValue || null; }, @@ -76,7 +76,7 @@ export function resourceFilterParam(allowedValues, defaultValue) { */ set(value) { const queryParams = {}; - queryParams[filterName] = JSON.stringify(value); + queryParams[filterKey] = JSON.stringify(value); this.transitionTo({ queryParams }); }, }; diff --git a/addons/core/addon/helpers/resource-filter-param.js b/addons/core/addon/helpers/resource-filter-param.js index 98eb791bf..5cf4e40d1 100644 --- a/addons/core/addon/helpers/resource-filter-param.js +++ b/addons/core/addon/helpers/resource-filter-param.js @@ -52,12 +52,18 @@ export default class ResourceFilterParamHelper extends Helper { const owner = getOwner(this); // Filter options const route = owner.lookup(`route:${routeName}`); - const filterOptionsName = `filter-options-${name}`; - const value = route[filterOptionsName]; + const filterAllowedValuesKey = `filter-allowed-values-${name}`; + const allowedValues = route[filterAllowedValuesKey]; // Selected filters const rawValue = route[name]; - const selectedValue = rawValue ? JSON.parse(rawValue) : null; - return { name, value, selectedValue }; + let value = rawValue; + try { + value = JSON.parse(rawValue); + } catch (e) { + /* ignore parse errors */ + } + const selectedValue = rawValue ? value : null; + return { name, allowedValues, selectedValue }; } // =actions From f21627a9ba9e32766650e2a5f68ebc8fe0a7ade3 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 10 Nov 2021 10:38:16 -0500 Subject: [PATCH 08/19] fix: value handling and helper recomputation --- addons/api/addon/services/resource-filter-store.js | 2 +- .../core/addon/decorators/resource-filter-param.js | 8 +++++--- addons/core/addon/helpers/resource-filter-param.js | 14 ++++---------- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/addons/api/addon/services/resource-filter-store.js b/addons/api/addon/services/resource-filter-store.js index 94d934359..8bd4130ae 100644 --- a/addons/api/addon/services/resource-filter-store.js +++ b/addons/api/addon/services/resource-filter-store.js @@ -10,7 +10,7 @@ import { inject as service } from '@ember/service'; * type: 'oidc', * status: ['active', 'pending'] * }); - * console.log(filter.queryExpression); + * log(filter.queryExpression); * // ("/item/type" == "oidc") and ("/item/status" == "active" or "/item/status" == "pending") */ export class ResourceFilter { diff --git a/addons/core/addon/decorators/resource-filter-param.js b/addons/core/addon/decorators/resource-filter-param.js index fda969722..bf48c2d4b 100644 --- a/addons/core/addon/decorators/resource-filter-param.js +++ b/addons/core/addon/decorators/resource-filter-param.js @@ -27,7 +27,7 @@ * // via a route transition. * this.status = status; * // Log the value of query param `filter-status`. - * console.log(this.status); + * log(this.status); * } * } * @@ -64,8 +64,10 @@ export function resourceFilterParam(allowedValues, defaultValue) { * Returns the JSON-parsed query parameter value OR defaultValue. */ get() { - const value = this._router.currentRoute.queryParams[filterKey]; - return value ? JSON.parse(value) : defaultValue || null; + const value = + this._router.currentRoute.queryParams[filterKey] || + JSON.stringify(defaultValue); + return value ? JSON.parse(value) : null; }, /** diff --git a/addons/core/addon/helpers/resource-filter-param.js b/addons/core/addon/helpers/resource-filter-param.js index 5cf4e40d1..e78ac20a7 100644 --- a/addons/core/addon/helpers/resource-filter-param.js +++ b/addons/core/addon/helpers/resource-filter-param.js @@ -29,14 +29,14 @@ export default class ResourceFilterParamHelper extends Helper { */ constructor() { super(...arguments); - this.router.on('routeWillChange', this.routeWillChange); + this.router.on('routeDidChange', this.routeDidChange); } /** * Stop listening to route events when this helper is destroyed. */ willDestroy() { - this.router.off('routeWillChange', this.routeWillChange); + this.router.off('routeDidChange', this.routeDidChange); super.willDestroy(); } @@ -56,13 +56,7 @@ export default class ResourceFilterParamHelper extends Helper { const allowedValues = route[filterAllowedValuesKey]; // Selected filters const rawValue = route[name]; - let value = rawValue; - try { - value = JSON.parse(rawValue); - } catch (e) { - /* ignore parse errors */ - } - const selectedValue = rawValue ? value : null; + const selectedValue = rawValue || null; return { name, allowedValues, selectedValue }; } @@ -73,7 +67,7 @@ export default class ResourceFilterParamHelper extends Helper { * route query params change. */ @action - routeWillChange() { + routeDidChange() { this.recompute(); } } From 80a82ea925e5fcdf40d54658be7aa55432866812 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 10 Nov 2021 11:01:00 -0500 Subject: [PATCH 09/19] fix: use paramsFor --- addons/core/addon/decorators/resource-filter-param.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/core/addon/decorators/resource-filter-param.js b/addons/core/addon/decorators/resource-filter-param.js index bf48c2d4b..61978ee06 100644 --- a/addons/core/addon/decorators/resource-filter-param.js +++ b/addons/core/addon/decorators/resource-filter-param.js @@ -16,7 +16,7 @@ * * @example * - * export default class ScopesScopeAuthMethodsRoute extends Route { + * export default class MyRoute extends Route { * * @resourceFilterParam(['active', 'pending'], ['active']) * status; @@ -65,7 +65,7 @@ export function resourceFilterParam(allowedValues, defaultValue) { */ get() { const value = - this._router.currentRoute.queryParams[filterKey] || + this.paramsFor(this.routeName)[filterKey] || JSON.stringify(defaultValue); return value ? JSON.parse(value) : null; }, From 8a11a76b49261fdeb76acc00278ad495509cd2fb Mon Sep 17 00:00:00 2001 From: Carlos Alcaide Corvo Date: Wed, 10 Nov 2021 14:07:19 -0800 Subject: [PATCH 10/19] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Add=20capabilities?= =?UTF-8?q?=20to=20managed=20groups?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add capabilities to managed groups --- addons/api/mirage/factories/auth-method.js | 1 + addons/core/translations/resources/en-us.yaml | 4 ++ .../components/form/managed-group/index.hbs | 17 ++++---- .../auth-method/managed-groups.js | 8 +++- .../auth-methods/auth-method/-actions.hbs | 17 ++++---- .../auth-methods/auth-method/-navigation.hbs | 10 +++-- .../auth-method/managed-groups/index.hbs | 42 +++++++++++++++++++ 7 files changed, 78 insertions(+), 21 deletions(-) diff --git a/addons/api/mirage/factories/auth-method.js b/addons/api/mirage/factories/auth-method.js index 755154b09..865c10b3d 100644 --- a/addons/api/mirage/factories/auth-method.js +++ b/addons/api/mirage/factories/auth-method.js @@ -15,6 +15,7 @@ export default factory.extend({ authorized_collection_actions: () => { return { accounts: ['create', 'list'], + 'managed-groups': ['create', 'list'], }; }, id: (i) => `auth-method-id-${i}`, diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index ba97605d8..4e8453ff9 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -358,3 +358,7 @@ managed-group: actions: delete: Delete Managed Group create: Create Managed Group + messages: + welcome: + title: Welcome to Managed Groups + description: Create and manage managed groups for an auth method. diff --git a/ui/admin/app/components/form/managed-group/index.hbs b/ui/admin/app/components/form/managed-group/index.hbs index 7918dd146..93075ca84 100644 --- a/ui/admin/app/components/form/managed-group/index.hbs +++ b/ui/admin/app/components/form/managed-group/index.hbs @@ -69,11 +69,12 @@ {{/if}} - - - \ No newline at end of file + {{#if (can 'save model' @model)}} + + {{/if}} + diff --git a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups.js b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups.js index cef4017d2..737dc4728 100644 --- a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups.js +++ b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups.js @@ -21,7 +21,13 @@ export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsRoute extends model() { const authMethod = this.modelFor('scopes.scope.auth-methods.auth-method'); const { id: auth_method_id } = authMethod; - return this.store.query('managed-group', { auth_method_id }); + const canListManagedGroups = this.can.can('list collection', authMethod, { + collection: 'managed-groups', + }); + + if (canListManagedGroups) { + return this.store.query('managed-group', { auth_method_id }); + } } // =actions diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs index 865bdfeec..5626026a6 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs @@ -68,14 +68,15 @@ {{/if}} {{#if (feature-flag 'managed-groups')}} - {{! TODO: Add capabilities }} - {{#if @model.isOIDC}} - - {{t 'resources.managed-group.actions.create'}} - + {{#if (can 'create collection' @model collection='managed-groups')}} + {{#if @model.isOIDC}} + + {{t 'resources.managed-group.actions.create'}} + + {{/if}} {{/if}} {{/if}} diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-navigation.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-navigation.hbs index 6c5d6bd70..00c2b814d 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-navigation.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-navigation.hbs @@ -10,10 +10,12 @@ {{/if}} {{#if (feature-flag 'managed-groups')}} - {{#if @model.isOIDC}} - - {{t 'resources.managed-group.title_plural'}} - + {{#if (can 'navigate collection' @model collection='managed-groups')}} + {{#if @model.isOIDC}} + + {{t 'resources.managed-group.title_plural'}} + + {{/if}} {{/if}} {{/if}} \ No newline at end of file diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/index.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/index.hbs index 8165f109d..c482fd414 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/index.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/index.hbs @@ -41,4 +41,46 @@ {{/each}} +{{else}} + + + + + {{#if + (can 'list collection' this.authMethod collection='managed-groups') + }} + {{! Can list (at least): show default welcome message }} + {{t 'resources.managed-group.description'}} + {{else if (can + 'create collection' this.authMethod collection='managed-groups' + )}} + {{! Can create only: show create but not list welcome message }} + {{t + 'descriptions.create-but-not-list' + resource=(t 'resources.managed-group.title_plural') + }} + {{else}} + {{! Can neither list nor create }} + {{t + 'descriptions.neither-list-nor-create' + resource=(t 'resources.managed-group.title_plural') + }} + {{/if}} + + + {{#if + (can 'create collection' this.authMethod collection='managed-groups') + }} + + + {{t 'resources.managed-group.actions.create'}} + + {{/if}} + + {{/if}} \ No newline at end of file From 1225774806a31fe8e52c2b0fe387f08a6335faeb Mon Sep 17 00:00:00 2001 From: Carlos Alcaide Corvo Date: Wed, 10 Nov 2021 14:51:17 -0800 Subject: [PATCH 11/19] Fix lint error --- ui/admin/app/components/form/managed-group/index.hbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/admin/app/components/form/managed-group/index.hbs b/ui/admin/app/components/form/managed-group/index.hbs index 93075ca84..523bb52bb 100644 --- a/ui/admin/app/components/form/managed-group/index.hbs +++ b/ui/admin/app/components/form/managed-group/index.hbs @@ -77,4 +77,4 @@ @cancelText={{t 'actions.cancel'}} /> {{/if}} - + \ No newline at end of file From ba73a89d8f8ac1cc9b821893bd1d0a221e0c029b Mon Sep 17 00:00:00 2001 From: Carlos Alcaide Corvo Date: Wed, 10 Nov 2021 15:10:51 -0800 Subject: [PATCH 12/19] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Add=20breadcrumbs?= =?UTF-8?q?=20to=20managed=20groups?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add breadcrumbs to managed groups --- .../auth-methods/auth-method/managed-groups.js | 18 ++++++++++++++++++ .../managed-groups/managed-group.js | 13 +++++++++++++ .../auth-method/managed-groups/new.js | 17 +++++++++++++++++ .../auth-method/managed-groups-test.js | 17 +++++++++++++++++ .../managed-groups/managed-group-test.js | 17 +++++++++++++++++ .../auth-method/managed-groups/new-test.js | 17 +++++++++++++++++ 6 files changed, 99 insertions(+) create mode 100644 ui/admin/app/controllers/scopes/scope/auth-methods/auth-method/managed-groups.js create mode 100644 ui/admin/app/controllers/scopes/scope/auth-methods/auth-method/managed-groups/managed-group.js create mode 100644 ui/admin/app/controllers/scopes/scope/auth-methods/auth-method/managed-groups/new.js create mode 100644 ui/admin/tests/unit/controllers/scopes/scope/auth-methods/auth-method/managed-groups-test.js create mode 100644 ui/admin/tests/unit/controllers/scopes/scope/auth-methods/auth-method/managed-groups/managed-group-test.js create mode 100644 ui/admin/tests/unit/controllers/scopes/scope/auth-methods/auth-method/managed-groups/new-test.js diff --git a/ui/admin/app/controllers/scopes/scope/auth-methods/auth-method/managed-groups.js b/ui/admin/app/controllers/scopes/scope/auth-methods/auth-method/managed-groups.js new file mode 100644 index 000000000..8d72eb80d --- /dev/null +++ b/ui/admin/app/controllers/scopes/scope/auth-methods/auth-method/managed-groups.js @@ -0,0 +1,18 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsController extends Controller { + // =services + + @service intl; + + // =attributes + + /** + * Translated managed groups breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.intl.t('resources.managed-group.title_plural'); + } +} diff --git a/ui/admin/app/controllers/scopes/scope/auth-methods/auth-method/managed-groups/managed-group.js b/ui/admin/app/controllers/scopes/scope/auth-methods/auth-method/managed-groups/managed-group.js new file mode 100644 index 000000000..4b770231b --- /dev/null +++ b/ui/admin/app/controllers/scopes/scope/auth-methods/auth-method/managed-groups/managed-group.js @@ -0,0 +1,13 @@ +import Controller from '@ember/controller'; + +export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsManagedGroupController extends Controller { + // =attributes + + /** + * A managed group breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.model.displayName; + } +} diff --git a/ui/admin/app/controllers/scopes/scope/auth-methods/auth-method/managed-groups/new.js b/ui/admin/app/controllers/scopes/scope/auth-methods/auth-method/managed-groups/new.js new file mode 100644 index 000000000..cf16a4e94 --- /dev/null +++ b/ui/admin/app/controllers/scopes/scope/auth-methods/auth-method/managed-groups/new.js @@ -0,0 +1,17 @@ +import Controller from '@ember/controller'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsNewController extends Controller { + // =services + + @service intl; + + // =attributes + /** + * Translated new managed group breadcrumb + * @type {string} + */ + get breadCrumb() { + return this.intl.t('titles.new'); + } +} diff --git a/ui/admin/tests/unit/controllers/scopes/scope/auth-methods/auth-method/managed-groups-test.js b/ui/admin/tests/unit/controllers/scopes/scope/auth-methods/auth-method/managed-groups-test.js new file mode 100644 index 000000000..7f976529b --- /dev/null +++ b/ui/admin/tests/unit/controllers/scopes/scope/auth-methods/auth-method/managed-groups-test.js @@ -0,0 +1,17 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module( + 'Unit | Controller | scopes/scope/auth-methods/auth-method/managed-groups', + function (hooks) { + setupTest(hooks); + + // TODO: Replace this with your real tests. + test('it exists', function (assert) { + let controller = this.owner.lookup( + 'controller:scopes/scope/auth-methods/auth-method/managed-groups' + ); + assert.ok(controller); + }); + } +); diff --git a/ui/admin/tests/unit/controllers/scopes/scope/auth-methods/auth-method/managed-groups/managed-group-test.js b/ui/admin/tests/unit/controllers/scopes/scope/auth-methods/auth-method/managed-groups/managed-group-test.js new file mode 100644 index 000000000..22284a9cf --- /dev/null +++ b/ui/admin/tests/unit/controllers/scopes/scope/auth-methods/auth-method/managed-groups/managed-group-test.js @@ -0,0 +1,17 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module( + 'Unit | Controller | scopes/scope/auth-methods/auth-method/managed-groups/managed-group', + function (hooks) { + setupTest(hooks); + + // TODO: Replace this with your real tests. + test('it exists', function (assert) { + let controller = this.owner.lookup( + 'controller:scopes/scope/auth-methods/auth-method/managed-groups/managed-group' + ); + assert.ok(controller); + }); + } +); diff --git a/ui/admin/tests/unit/controllers/scopes/scope/auth-methods/auth-method/managed-groups/new-test.js b/ui/admin/tests/unit/controllers/scopes/scope/auth-methods/auth-method/managed-groups/new-test.js new file mode 100644 index 000000000..699d52e58 --- /dev/null +++ b/ui/admin/tests/unit/controllers/scopes/scope/auth-methods/auth-method/managed-groups/new-test.js @@ -0,0 +1,17 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module( + 'Unit | Controller | scopes/scope/auth-methods/auth-method/managed-groups/new', + function (hooks) { + setupTest(hooks); + + // TODO: Replace this with your real tests. + test('it exists', function (assert) { + let controller = this.owner.lookup( + 'controller:scopes/scope/auth-methods/auth-method/managed-groups/new' + ); + assert.ok(controller); + }); + } +); From b646e78e0fbf6636b172a49947f9d7878ad58267 Mon Sep 17 00:00:00 2001 From: Carlos Alcaide Corvo Date: Wed, 10 Nov 2021 16:07:18 -0800 Subject: [PATCH 13/19] =?UTF-8?q?test:=20=F0=9F=92=8D=20Add=20acceptance?= =?UTF-8?q?=20test=20to=20list=20managed=20groups?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add acceptance tests to list managed groups --- .../acceptance/managed-groups/list-test.js | 92 +++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 ui/admin/tests/acceptance/managed-groups/list-test.js diff --git a/ui/admin/tests/acceptance/managed-groups/list-test.js b/ui/admin/tests/acceptance/managed-groups/list-test.js new file mode 100644 index 000000000..dc2ad9e98 --- /dev/null +++ b/ui/admin/tests/acceptance/managed-groups/list-test.js @@ -0,0 +1,92 @@ +import { module, test } from 'qunit'; +import { visit, click, find } from '@ember/test-helpers'; +import { setupApplicationTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import { authenticateSession } from 'ember-simple-auth/test-support'; + +module('Acceptance | managed-groups | list', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + const instances = { + scopes: { + global: null, + org: null, + }, + authMethod: null, + managedGroup: null, + }; + const urls = { + orgScope: null, + authMethods: null, + managedGroups: null, + newManagedGroup: null, + managedGroup: null, + }; + + hooks.beforeEach(function () { + authenticateSession({}); + instances.scopes.global = this.server.create('scope', { id: 'global' }); + instances.scopes.org = this.server.create('scope', { + type: 'org', + scope: { id: 'global', type: 'global' }, + }); + instances.authMethod = this.server.create('auth-method', { + scope: instances.scopes.org, + type: 'oidc', + }); + instances.managedGroup = this.server.create('managed-group', { + scope: instances.scopes.org, + authMethod: instances.authMethod, + }); + // Generate route URLs for resources + urls.orgScope = `/scopes/${instances.scopes.org.id}`; + urls.authMethods = `${urls.orgScope}/auth-methods`; + urls.authMethod = `${urls.authMethods}/${instances.authMethod.id}`; + urls.managedGroups = `${urls.authMethod}/managed-groups`; + urls.newManagedGroup = `${urls.managedGroups}/new`; + urls.managedGroup = `${urls.managedGroups}/${instances.authMethod.id}`; + }); + + hooks.afterEach(async function () { + const notification = find('.rose-notification'); + if (notification) { + await click('.rose-notification-dismiss'); + } + }); + + test('User can navigate to managed groups with proper authorization', async function (assert) { + assert.expect(2); + await visit(urls.authMethod); + assert.ok( + instances.authMethod.authorized_collection_actions[ + 'managed-groups' + ].includes('list') + ); + assert.ok(find(`[href="${urls.managedGroups}"]`)); + }); + + test('User cannot navigate to index without either list or create actions', async function (assert) { + assert.expect(2); + instances.authMethod.authorized_collection_actions['managed-groups'] = []; + await visit(urls.authMethod); + assert.notOk( + instances.authMethod.authorized_collection_actions[ + 'managed-groups' + ].includes('list') + ); + assert.notOk(find(`[href="${urls.managedGroups}"]`)); + }); + + test('User can navigate to index with only create action', async function (assert) { + assert.expect(2); + instances.authMethod.authorized_collection_actions['managed-groups'] = [ + 'create', + ]; + await visit(urls.authMethod); + assert.ok(find(`[href="${urls.managedGroups}"]`)); + assert.ok( + find(`.rose-layout-page-actions [href="${urls.newManagedGroup}"]`) + ); + }); +}); From 74d161f6f59a7ae66d88509f1127a3d8095ad8ff Mon Sep 17 00:00:00 2001 From: Carlos Alcaide Corvo Date: Wed, 10 Nov 2021 16:49:41 -0800 Subject: [PATCH 14/19] =?UTF-8?q?test:=20=F0=9F=92=8D=20Add=20acceptance?= =?UTF-8?q?=20tests=20to=20create=20managed=20groups?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add acceptance test to create managed groups --- .../acceptance/managed-groups/create-test.js | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 ui/admin/tests/acceptance/managed-groups/create-test.js diff --git a/ui/admin/tests/acceptance/managed-groups/create-test.js b/ui/admin/tests/acceptance/managed-groups/create-test.js new file mode 100644 index 000000000..eeae7cef7 --- /dev/null +++ b/ui/admin/tests/acceptance/managed-groups/create-test.js @@ -0,0 +1,160 @@ +import { module, test } from 'qunit'; +import { visit, currentURL, click, find, fillIn } from '@ember/test-helpers'; +import { setupApplicationTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import { authenticateSession } from 'ember-simple-auth/test-support'; +import a11yAudit from 'ember-a11y-testing/test-support/audit'; + +module('Acceptance | managed-groups | create', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + const instances = { + scopes: { + global: null, + org: null, + }, + authMethod: null, + managedGroup: null, + }; + const urls = { + orgScope: null, + authMethods: null, + managedGroups: null, + newManagedGroup: null, + managedGroup: null, + }; + + hooks.beforeEach(function () { + authenticateSession({}); + instances.scopes.global = this.server.create('scope', { id: 'global' }); + instances.scopes.org = this.server.create('scope', { + type: 'org', + scope: { id: 'global', type: 'global' }, + }); + instances.authMethod = this.server.create('auth-method', { + scope: instances.scopes.org, + type: 'oidc', + }); + instances.managedGroup = this.server.create('managed-group', { + scope: instances.scopes.org, + authMethod: instances.authMethod, + }); + // Generate route URLs for resources + urls.orgScope = `/scopes/${instances.scopes.org.id}`; + urls.authMethods = `${urls.orgScope}/auth-methods`; + urls.authMethod = `${urls.authMethods}/${instances.authMethod.id}`; + urls.managedGroups = `${urls.authMethod}/managed-groups`; + urls.newManagedGroup = `${urls.managedGroups}/new`; + urls.managedGroup = `${urls.managedGroups}/${instances.authMethod.id}`; + }); + + hooks.afterEach(async function () { + const notification = find('.rose-notification'); + if (notification) { + await click('.rose-notification-dismiss'); + } + }); + + test('visiting managed groups', async function (assert) { + assert.expect(1); + await visit(urls.managedGroups); + await a11yAudit(); + assert.equal(currentURL(), urls.managedGroups); + }); + + test('can create a new managed group', async function (assert) { + assert.expect(1); + const managedGroupsCount = this.server.db.managedGroups.length; + await visit(urls.newManagedGroup); + await fillIn('[name="name"]', 'Managed group name'); + await fillIn('[name="description"]', 'description'); + await click('form [type="submit"]:not(:disabled)'); + assert.equal(this.server.db.managedGroups.length, managedGroupsCount + 1); + }); + + test('User can not create a new managed group without proper authorization', async function (assert) { + assert.expect(2); + instances.authMethod.authorized_collection_actions['managed-groups'] = []; + await visit(urls.authMethod); + assert.notOk( + instances.authMethod.authorized_collection_actions[ + 'managed-groups' + ].includes('create') + ); + assert.notOk( + find(`.rose-layout-page-actions [href="${urls.newManagedGroup}"]`) + ); + }); + + test('User can navigate to new managed group route with proper authorization', async function (assert) { + assert.expect(2); + await visit(urls.managedGroups); + assert.ok( + instances.authMethod.authorized_collection_actions[ + 'managed-groups' + ].includes('create') + ); + assert.ok(find(`[href="${urls.newManagedGroup}"]`)); + }); + + test('User cannot navigate to new managed group route without proper authorization', async function (assert) { + assert.expect(2); + instances.authMethod.authorized_collection_actions['managed-groups'] = []; + await visit(urls.managedGroups); + assert.notOk( + instances.authMethod.authorized_collection_actions[ + 'managed-groups' + ].includes('create') + ); + assert.notOk(find(`[href="${urls.newManagedGroup}"]`)); + }); + + test('User can cancel a new managed group creation', async function (assert) { + assert.expect(2); + const managedGroupsCount = this.server.db.managedGroups.length; + await visit(urls.newManagedGroup); + await fillIn('[name="name"]', 'Managed group name'); + await click('form button:not([type="submit"])'); + assert.equal(this.server.db.managedGroups.length, managedGroupsCount); + assert.equal(currentURL(), urls.managedGroups); + }); + + // Temporary skipping this test because we did not implement mandatory fields in manageg groups + test.skip('When user saving a new managed group with invalid fields displays error message', async function (assert) { + assert.expect(2); + this.server.post('/managed-groups', () => { + return new Response( + 400, + {}, + { + status: 400, + code: 'invalid_argument', + message: 'The request was invalid.', + details: { + request_fields: [ + { + name: 'name', + description: 'Name is required.', + }, + ], + }, + } + ); + }); + await visit(urls.newManagedGroup); + await fillIn('[name="name"]', 'new managed group'); + await click('form [type="submit"]'); + await a11yAudit(); + assert.equal( + find('.rose-notification-body').textContent.trim(), + 'The request was invalid.', + 'Displays primary error message.' + ); + assert.equal( + find('.rose-form-error-message').textContent.trim(), + 'Name is required.', + 'Displays field-level errors.' + ); + }); +}); From bf8392ff9f95ddb45d1f086b04a35417ea352545 Mon Sep 17 00:00:00 2001 From: Carlos Alcaide Corvo Date: Wed, 10 Nov 2021 17:19:04 -0800 Subject: [PATCH 15/19] =?UTF-8?q?test:=20=F0=9F=92=8D=20Add=20acceptance?= =?UTF-8?q?=20test=20to=20delete=20managed=20groups?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add acceptance test to delete managed groups --- .../acceptance/managed-groups/delete-test.js | 103 ++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 ui/admin/tests/acceptance/managed-groups/delete-test.js diff --git a/ui/admin/tests/acceptance/managed-groups/delete-test.js b/ui/admin/tests/acceptance/managed-groups/delete-test.js new file mode 100644 index 000000000..e0e324cf5 --- /dev/null +++ b/ui/admin/tests/acceptance/managed-groups/delete-test.js @@ -0,0 +1,103 @@ +import { module, test } from 'qunit'; +import { visit, currentURL, click, find, fillIn } from '@ember/test-helpers'; +import { setupApplicationTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import { authenticateSession } from 'ember-simple-auth/test-support'; +import a11yAudit from 'ember-a11y-testing/test-support/audit'; +import { Response } from 'miragejs'; + +module('Acceptance | managed-groups | delete', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + const instances = { + scopes: { + global: null, + org: null, + }, + authMethod: null, + managedGroup: null, + }; + const urls = { + orgScope: null, + authMethods: null, + managedGroups: null, + newManagedGroup: null, + managedGroup: null, + }; + + hooks.beforeEach(function () { + authenticateSession({}); + instances.scopes.global = this.server.create('scope', { id: 'global' }); + instances.scopes.org = this.server.create('scope', { + type: 'org', + scope: { id: 'global', type: 'global' }, + }); + instances.authMethod = this.server.create('auth-method', { + scope: instances.scopes.org, + type: 'oidc', + }); + instances.managedGroup = this.server.create('managed-group', { + scope: instances.scopes.org, + authMethod: instances.authMethod, + }); + // Generate route URLs for resources + urls.orgScope = `/scopes/${instances.scopes.org.id}`; + urls.authMethods = `${urls.orgScope}/auth-methods`; + urls.authMethod = `${urls.authMethods}/${instances.authMethod.id}`; + urls.managedGroups = `${urls.authMethod}/managed-groups`; + urls.newManagedGroup = `${urls.managedGroups}/new`; + // TODO refactor in the rest of the test this url + urls.managedGroup = `${urls.managedGroups}/${instances.managedGroup.id}`; + }); + + hooks.afterEach(async function () { + const notification = find('.rose-notification'); + if (notification) { + await click('.rose-notification-dismiss'); + } + }); + + test('User can delete a managed group', async function (assert) { + assert.expect(1); + const managedGroupsCount = this.server.db.managedGroups.length; + await visit(urls.managedGroup); + await click('.rose-layout-page-actions .rose-dropdown-button-danger'); + assert.equal(this.server.db.managedGroups.length, managedGroupsCount - 1); + }); + + test('User cannot delete a managed-group without proper authorization', async function (assert) { + assert.expect(1); + instances.managedGroup.authorized_actions = + instances.managedGroup.authorized_actions.filter( + (item) => item !== 'delete' + ); + await visit(urls.managedGroup); + assert.notOk( + find('.rose-layout-page-actions .rose-dropdown-button-danger') + ); + }); + + test('Errors are displayed when delete on managed group fails', async function (assert) { + assert.expect(1); + this.server.del('/managed-groups/:id', () => { + return new Response( + 490, + {}, + { + status: 490, + code: 'error', + message: 'Oops.', + } + ); + }); + await visit(urls.managedGroup); + await click('.rose-layout-page-actions .rose-dropdown-button-danger'); + await a11yAudit(); + assert.equal( + find('.rose-notification-body').textContent.trim(), + 'Oops.', + 'Displays primary error message.' + ); + }); +}); From 19d8bc44c8a5a9300302a444761c045e3554c58e Mon Sep 17 00:00:00 2001 From: Carlos Alcaide Corvo Date: Wed, 10 Nov 2021 17:26:27 -0800 Subject: [PATCH 16/19] Fix lint error --- ui/admin/tests/acceptance/managed-groups/delete-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/admin/tests/acceptance/managed-groups/delete-test.js b/ui/admin/tests/acceptance/managed-groups/delete-test.js index e0e324cf5..7be2263ab 100644 --- a/ui/admin/tests/acceptance/managed-groups/delete-test.js +++ b/ui/admin/tests/acceptance/managed-groups/delete-test.js @@ -1,5 +1,5 @@ import { module, test } from 'qunit'; -import { visit, currentURL, click, find, fillIn } from '@ember/test-helpers'; +import { visit, click, find } from '@ember/test-helpers'; import { setupApplicationTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import { authenticateSession } from 'ember-simple-auth/test-support'; From 50a9a980ed4d63b953d4cdfbfd96803a065890c4 Mon Sep 17 00:00:00 2001 From: Carlos Alcaide Corvo Date: Wed, 10 Nov 2021 17:34:31 -0800 Subject: [PATCH 17/19] =?UTF-8?q?test:=20=F0=9F=92=8D=20Add=20acceptance?= =?UTF-8?q?=20test=20to=20read=20managed=20groups?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add acceptance test to read managed groups --- .../acceptance/managed-groups/read-test.js | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 ui/admin/tests/acceptance/managed-groups/read-test.js diff --git a/ui/admin/tests/acceptance/managed-groups/read-test.js b/ui/admin/tests/acceptance/managed-groups/read-test.js new file mode 100644 index 000000000..ef11179c8 --- /dev/null +++ b/ui/admin/tests/acceptance/managed-groups/read-test.js @@ -0,0 +1,77 @@ +import { module, test } from 'qunit'; +import { visit, click, find, currentURL } from '@ember/test-helpers'; +import { setupApplicationTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import { authenticateSession } from 'ember-simple-auth/test-support'; +import a11yAudit from 'ember-a11y-testing/test-support/audit'; + +module('Acceptance | managed-groups | read', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + const instances = { + scopes: { + global: null, + org: null, + }, + authMethod: null, + managedGroup: null, + }; + const urls = { + orgScope: null, + authMethods: null, + managedGroups: null, + newManagedGroup: null, + managedGroup: null, + }; + + hooks.beforeEach(function () { + authenticateSession({}); + instances.scopes.global = this.server.create('scope', { id: 'global' }); + instances.scopes.org = this.server.create('scope', { + type: 'org', + scope: { id: 'global', type: 'global' }, + }); + instances.authMethod = this.server.create('auth-method', { + scope: instances.scopes.org, + type: 'oidc', + }); + instances.managedGroup = this.server.create('managed-group', { + scope: instances.scopes.org, + authMethod: instances.authMethod, + }); + // Generate route URLs for resources + urls.orgScope = `/scopes/${instances.scopes.org.id}`; + urls.authMethods = `${urls.orgScope}/auth-methods`; + urls.authMethod = `${urls.authMethods}/${instances.authMethod.id}`; + urls.managedGroups = `${urls.authMethod}/managed-groups`; + urls.newManagedGroup = `${urls.managedGroups}/new`; + // TODO refactor in the rest of the test this url + urls.managedGroup = `${urls.managedGroups}/${instances.managedGroup.id}`; + }); + + hooks.afterEach(async function () { + const notification = find('.rose-notification'); + if (notification) { + await click('.rose-notification-dismiss'); + } + }); + + test('User can navigate to a managed group form', async function (assert) { + assert.expect(1); + await visit(urls.managedGroups); + await click('main tbody .rose-table-header-cell:nth-child(1) a'); + await a11yAudit(); + assert.equal(currentURL(), urls.managedGroup); + }); + + test('User cannot navigate to a managed group form without proper authorization', async function (assert) { + assert.expect(1); + instances.managedGroup.authorized_actions = + instances.managedGroup.authorized_actions.filter( + (item) => item !== 'read' + ); + await visit(urls.managedGroups); + assert.notOk(find('main tbody .rose-table-header-cell:nth-child(1) a')); + }); +}); From 6ba9ffc3af49538a166ebd9c0b9565652c4fc899 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Thu, 11 Nov 2021 17:58:08 -0500 Subject: [PATCH 18/19] chore: Managed groups edit integration fix --- addons/api/addon/serializers/managed-group.js | 9 ++++++++- addons/api/app/serializers/managed-group.js | 1 + .../auth-methods/auth-method/managed-groups.js | 15 +++++++++++---- .../managed-groups/managed-group/-actions.hbs | 2 +- .../managed-groups/managed-group/index.hbs | 6 +++++- 5 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 addons/api/app/serializers/managed-group.js diff --git a/addons/api/addon/serializers/managed-group.js b/addons/api/addon/serializers/managed-group.js index cb03236af..f42fc78d2 100644 --- a/addons/api/addon/serializers/managed-group.js +++ b/addons/api/addon/serializers/managed-group.js @@ -1,3 +1,10 @@ import ApplicationSerializer from './application'; -export default class ManagedGroupSerializer extends ApplicationSerializer {} +export default class ManagedGroupSerializer extends ApplicationSerializer { + // =properties + + /** + * @type {boolean} + */ + serializeScopeID = false; +} diff --git a/addons/api/app/serializers/managed-group.js b/addons/api/app/serializers/managed-group.js new file mode 100644 index 000000000..799590738 --- /dev/null +++ b/addons/api/app/serializers/managed-group.js @@ -0,0 +1 @@ +export { default } from 'api/serializers/managed-group'; diff --git a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups.js b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups.js index 737dc4728..770d1357e 100644 --- a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups.js +++ b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method/managed-groups.js @@ -56,9 +56,16 @@ export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsRoute extends ) async save(managedGroup) { await managedGroup.save(); - await this.transitionTo( - 'scopes.scope.auth-methods.auth-method.managed-groups' - ); + if (this.can.can('read model', managedGroup)) { + await this.transitionTo( + 'scopes.scope.auth-methods.auth-method.managed-groups.managed-group', + managedGroup + ); + } else { + await this.transitionTo( + 'scopes.scope.auth-methods.auth-method.managed-groups' + ); + } this.refresh(); } @@ -71,7 +78,7 @@ export default class ScopesScopeAuthMethodsAuthMethodManagedGroupsRoute extends @confirm('questions.delete-confirm') @notifyError(({ message }) => message, { catch: true }) @notifySuccess('notifications.delete-success') - async deleteManagedGroup(managedGroup) { + async delete(managedGroup) { await managedGroup.destroyRecord(); await this.replaceWith( 'scopes.scope.auth-methods.auth-method.managed-groups' diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/-actions.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/-actions.hbs index 98a51d476..3340d97f2 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/-actions.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/-actions.hbs @@ -7,7 +7,7 @@ {{t 'resources.managed-group.actions.delete'}} diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/index.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/index.hbs index 19ab29fcb..bcaa893ac 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/index.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/managed-groups/managed-group/index.hbs @@ -1 +1,5 @@ - \ No newline at end of file + \ No newline at end of file From cc151b001ba2dafe8954cb550e2d101e41920227 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Fri, 12 Nov 2021 09:27:07 -0500 Subject: [PATCH 19/19] test: Removed skipped managed group error notification test --- ui/admin/tests/acceptance/managed-groups/create-test.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/admin/tests/acceptance/managed-groups/create-test.js b/ui/admin/tests/acceptance/managed-groups/create-test.js index eeae7cef7..8d07893e4 100644 --- a/ui/admin/tests/acceptance/managed-groups/create-test.js +++ b/ui/admin/tests/acceptance/managed-groups/create-test.js @@ -4,6 +4,7 @@ import { setupApplicationTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import { authenticateSession } from 'ember-simple-auth/test-support'; import a11yAudit from 'ember-a11y-testing/test-support/audit'; +import { Response } from 'miragejs'; module('Acceptance | managed-groups | create', function (hooks) { setupApplicationTest(hooks); @@ -120,8 +121,7 @@ module('Acceptance | managed-groups | create', function (hooks) { assert.equal(currentURL(), urls.managedGroups); }); - // Temporary skipping this test because we did not implement mandatory fields in manageg groups - test.skip('When user saving a new managed group with invalid fields displays error message', async function (assert) { + test('When user saving a new managed group with invalid fields displays error message', async function (assert) { assert.expect(2); this.server.post('/managed-groups', () => { return new Response(