updates jit acl tab & navbar to use avatars/avatar-image component

This commit is contained in:
Seyi Adebajo 2018-09-13 16:43:29 -07:00
parent 97f08e0b81
commit 99e6337222
12 changed files with 98 additions and 47 deletions

View File

@ -2,12 +2,17 @@ import Component from '@ember/component';
import { get, set } from '@ember/object';
import ComputedProperty, { gte } from '@ember/object/computed';
import { TaskInstance, TaskProperty } from 'ember-concurrency';
import { action } from '@ember-decorators/object';
import { action, computed } from '@ember-decorators/object';
import {
IAccessControlAccessTypeOption,
IAccessControlEntry,
IRequestAccessControlEntry
} from 'wherehows-web/typings/api/datasets/aclaccess';
import { getDefaultRequestAccessControlEntry } from 'wherehows-web/utils/datasets/acl-access';
import { IAvatar } from 'wherehows-web/typings/app/avatars';
import { arrayMap } from 'wherehows-web/utils/array';
import { IAppConfig } from 'wherehows-web/typings/api/configurator/configurator';
import { getAvatarProps } from 'wherehows-web/constants/avatars/avatars';
/**
* Returns the number of days in milliseconds, default is 1 day
@ -30,6 +35,19 @@ const minSelectableExpirationDate = new Date(Date.now() + millisecondDays());
const maxSelectableExpirationDate = new Date(Date.now() + millisecondDays(7));
export default class DatasetAclAccess extends Component {
/**
* External component attribute with list of acls
* @type {Array<IAccessControlEntry>}
*/
acls: Array<IAccessControlEntry>;
/**
* External component attribute with properties to construct an acl's avatar image
* @type {(IAppConfig['userEntityProps'] | undefined)}
* @memberof DatasetAclAccess
*/
avatarProperties: IAppConfig['userEntityProps'] | undefined;
/**
* Named component argument with a string link reference to more information on acls
* @type {string}
@ -109,6 +127,23 @@ export default class DatasetAclAccess extends Component {
set(this, 'userAclRequest', getDefaultRequestAccessControlEntry());
}
/**
* Augments each acl in the list with properties for the user avatar
* @readonly
* @type {(Array<IAccessControlEntry & Record<'avatar', IAvatar>>)}
* @memberof DatasetAclAccess
*/
@computed('acls')
get aclsWithAvatarProps(): Array<IAccessControlEntry & Record<'avatar', IAvatar>> {
const { acls, avatarProperties } = this;
const aclToAvatar = (acl: IAccessControlEntry): IAccessControlEntry & Record<'avatar', IAvatar> => ({
...acl,
avatar: getAvatarProps(avatarProperties!)({ userName: acl.principal })
});
return avatarProperties ? arrayMap(aclToAvatar)(acls) : [];
}
/**
* Invokes external action when the accessType to be requested is modified
* @param {IAccessControlAccessTypeOption} arg

View File

@ -21,6 +21,9 @@ import Notifications, { NotificationEvent } from 'wherehows-web/services/notific
import { notificationDialogActionFactory } from 'wherehows-web/utils/notifications/notifications';
import { service } from '@ember-decorators/service';
import { equal } from '@ember-decorators/object/computed';
import { IAppConfig } from 'wherehows-web/typings/api/configurator/configurator';
import Configurator from 'wherehows-web/services/configurator';
import { containerDataSource } from 'wherehows-web/utils/components/containers/data-source';
/**
* Enumerates the string value for an acl access request
@ -31,6 +34,7 @@ enum AclRequestType {
Approval = 'approved'
}
@containerDataSource('getContainerDataTask')
export default class DatasetAclAccessContainer extends Component {
/**
* The currently logged in user service
@ -78,9 +82,17 @@ export default class DatasetAclAccessContainer extends Component {
/**
* Who to contact in case of error
* @type {string}
*/
jitAclContact: string;
/**
* Avatar properties used to generate the acl's avatar image
* @type {(IAppConfig['userEntityProps'] | undefined)}
* @memberof DatasetAclAccessContainer
*/
avatarProperties: IAppConfig['userEntityProps'] | undefined;
/**
* Request object for the current user requesting access control
* @type {IRequestAccessControlEntry}
@ -112,14 +124,6 @@ export default class DatasetAclAccessContainer extends Component {
*/
urn: string;
didInsertElement() {
get(this, 'getContainerDataTask').perform();
}
didUpdateAttrs() {
get(this, 'getContainerDataTask').perform();
}
constructor() {
super(...arguments);
@ -189,15 +193,17 @@ export default class DatasetAclAccessContainer extends Component {
* @memberof DatasetAclAccessContainer
*/
getContainerDataTask = task(function*(this: DatasetAclAccessContainer): IterableIterator<any> {
if (get(this, 'isJitAclAccessEnabled')) {
const { getCurrentUserTask, getDatasetAclsTask, checkUserAccessTask } = getProperties(this, [
if (this.isJitAclAccessEnabled) {
const { getCurrentUserTask, getDatasetAclsTask, checkUserAccessTask, getAvatarProperties } = getProperties(this, [
'getCurrentUserTask',
'getDatasetAclsTask',
'checkUserAccessTask'
'checkUserAccessTask',
'getAvatarProperties'
]);
const user: DatasetAclAccessContainer['user'] = yield getCurrentUserTask.perform();
if (user) {
yield getAvatarProperties.perform();
yield getDatasetAclsTask.perform();
yield checkUserAccessTask.perform();
}
@ -214,6 +220,16 @@ export default class DatasetAclAccessContainer extends Component {
return set(this, 'user', currentUser);
});
/**
* Fetches and sets the props used in constructing an acl's avatar
* @memberof DatasetAclAccessContainer
*/
getAvatarProperties = task(function*(
this: DatasetAclAccessContainer
): IterableIterator<IAppConfig['userEntityProps']> {
return set(this, 'avatarProperties', Configurator.getConfig('userEntityProps'));
});
/**
* Fetches the list of acls for this dataset
* @memberof DatasetAclAccessContainer

View File

@ -47,7 +47,7 @@ export default class DatasetOwnerListContainer extends Component {
* @type {IAppConfig.avatarEntityProps}
* @memberof DatasetOwnerListContainer
*/
avatarEntityProps!: IAppConfig['avatarEntityProps'];
avatarEntityProps!: IAppConfig['userEntityProps'];
/**
* Lists the avatar objects based off the dataset owners

View File

@ -5,23 +5,19 @@ import { IAppConfig } from 'wherehows-web/typings/api/configurator/configurator'
/**
* Takes a Partial<IAvatar> object and builds an IAvatar
* @param {Partial<IAvatar>} object
* @param {IAppConfig.avatarEntityProps.urlPrimary} urlPrimary
* @param {IAppConfig.avatarEntityProps.urlPrimary} urlFallback
* @param {IAppConfig.userEntityProps.aviUrlPrimary} aviUrlPrimary primary url for avatar image
* @param {IAppConfig.userEntityProps.aviUrlFallback} aviUrlFallback
* @return {IAvatar}
*/
const getAvatarProps = ({ urlPrimary, urlFallback }: IAppConfig['avatarEntityProps']) => (
const getAvatarProps = ({ aviUrlPrimary, aviUrlFallback = '' }: IAppConfig['userEntityProps']) => (
object: Partial<IAvatar>
): IAvatar => {
const props = pick(object, ['email', 'userName', 'name']);
let imageUrl = urlFallback || '';
if (props.userName && urlPrimary) {
imageUrl = urlPrimary.replace('[username]', props.userName);
}
const hasRequiredUrlElements = props.userName && aviUrlPrimary;
return {
imageUrl,
imageUrlFallback: urlFallback,
imageUrl: hasRequiredUrlElements ? aviUrlPrimary.replace('[username]', props.userName!) : aviUrlFallback,
imageUrlFallback: aviUrlFallback,
...props
};
};

View File

@ -78,7 +78,7 @@ export default class DatasetController extends Controller {
* References a collection of properties for avatar properties
* @type {IAppConfig.avatarEntityProps}
*/
avatarEntityProps: IAppConfig['avatarEntityProps'];
avatarEntityProps: IAppConfig['userEntityProps'];
/**
* Flag indicating the dataset policy is derived from an upstream source

View File

@ -5,9 +5,9 @@ import { get } from '@ember/object';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
import { feedback, avatar } from 'wherehows-web/constants';
import Configurator from 'wherehows-web/services/configurator';
import { getAvatarProps } from 'wherehows-web/constants/avatars/avatars';
const { mail, subject, title } = feedback;
const { url: avatarUrl } = avatar;
export default Route.extend(ApplicationRouteMixin, {
// Injected Ember#Service for the current user
@ -42,14 +42,13 @@ export default Route.extend(ApplicationRouteMixin, {
*/
async model() {
const { getConfig } = Configurator;
const [isInternal, showStagingBanner, showLiveDataWarning] = await Promise.all([
getConfig('isInternal'),
const [showStagingBanner, showLiveDataWarning, avatarEntityProps] = [
getConfig('isStagingBanner', { useDefault: true, default: false }),
getConfig('isLiveDataWarning', { useDefault: true, default: false })
]);
const { userName } = get(this, 'sessionUser.currentUser') || {};
getConfig('isLiveDataWarning', { useDefault: true, default: false }),
getConfig('userEntityProps')
];
const { userName, email, name } = get(this, 'sessionUser.currentUser') || {};
const avatar = getAvatarProps(avatarEntityProps)({ userName, email, name });
/**
* properties for the navigation link to allow a user to provide feedback
@ -61,12 +60,7 @@ export default Route.extend(ApplicationRouteMixin, {
target: '_blank'
};
const brand = {
logo: isInternal ? '/assets/assets/images/wherehows-logo.png' : '',
avatarUrl: isInternal ? avatarUrl.replace('[username]', userName) : '/assets/assets/images/default_avatar.png'
};
return { feedbackMail, brand, showStagingBanner, showLiveDataWarning };
return { feedbackMail, showStagingBanner, showLiveDataWarning, avatar };
},
/**

View File

@ -99,7 +99,7 @@ export default class DatasetRoute extends Route {
shouldShowDatasetLineage: getConfig('shouldShowDatasetLineage'),
shouldShowDatasetHealth: getConfig('shouldShowDatasetHealth'),
wikiLinks: getConfig('wikiLinks'),
avatarEntityProps: getConfig('avatarEntityProps')
avatarEntityProps: getConfig('userEntityProps')
});
}

View File

@ -118,3 +118,12 @@ $navbar-transition-speed: $banner-animation-speed;
border-radius: 0;
}
}
/**
* Adds styles for navigation bar avatar image
*/
.navbar-avatar-image {
&#{&} {
@include round-image(item-spacing(6));
}
}

View File

@ -161,11 +161,11 @@
</p>
{{/unless}}
</header>
{{#if acls}}
{{#if aclsWithAvatarProps}}
{{#dataset-table
class="nacho-table nacho-table--stripped"
fields=acls
fields=aclsWithAvatarProps
sortColumnWithName=sortColumnWithName
filterBy=filterBy as |table|
}}
@ -191,7 +191,7 @@
{{#each (sort-by table.sortBy table.data) as |field|}}
{{#body.row as |row|}}
{{#row.cell}}
{{user-avatar userName=field.principal}}
{{avatars/avatar-image avatar=field.avatar}}
{{field.principal}}
{{/row.cell}}

View File

@ -8,6 +8,7 @@
{{dataset-aclaccess
acls=acls
aclMoreInfoLink=@aclMoreInfoLink
avatarProperties=avatarProperties
jitAclContact=jitAclContact
hasValidAclRequest=hasValidAclRequest
userAclRequest=userAclRequest

View File

@ -10,7 +10,7 @@
{{#link-to "index" class="navbar-brand"}}
<div>
<img src="{{model.brand.logo}}"
<img src="/assets/assets/images/wherehows-logo.png"
alt="WhereHows Logo">
<sup class="beta-badge">(Beta)</sup>
</div>
@ -26,7 +26,7 @@
<li class="dropdown">
<a class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
<i class="caret"></i>
<img src="{{model.brand.avatarUrl}}" alt="User Avatar" class="user-avatar">
{{avatars/avatar-image avatar=model.avatar class="navbar-avatar-image"}}
</a>

View File

@ -18,9 +18,9 @@ interface IAppConfig {
// collection of links to external help resource pages
wikiLinks: Record<string, string>;
// properties for an avatar entity
avatarEntityProps: {
urlPrimary: string;
urlFallback: string;
userEntityProps: {
aviUrlPrimary: string;
aviUrlFallback: string;
};
tracking: {
isEnabled: boolean;