mirror of
https://github.com/datahub-project/datahub.git
synced 2025-12-25 00:48:45 +00:00
updates jit acl tab & navbar to use avatars/avatar-image component
This commit is contained in:
parent
97f08e0b81
commit
99e6337222
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
};
|
||||
};
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 };
|
||||
},
|
||||
|
||||
/**
|
||||
|
||||
@ -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')
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@ -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}}
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
{{dataset-aclaccess
|
||||
acls=acls
|
||||
aclMoreInfoLink=@aclMoreInfoLink
|
||||
avatarProperties=avatarProperties
|
||||
jitAclContact=jitAclContact
|
||||
hasValidAclRequest=hasValidAclRequest
|
||||
userAclRequest=userAclRequest
|
||||
|
||||
@ -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>
|
||||
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user