Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(UI): AccessManagement UI to access the role metadata for a dataset #8541

Merged
merged 14 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ public class FeatureFlags {
private boolean showBrowseV2 = false;
private PreProcessHooks preProcessHooks;
private boolean showAcrylInfo = false;
private boolean showAccessManagement = false;
jjoyce0510 marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ public CompletableFuture<AppConfig> get(final DataFetchingEnvironment environmen
.setReadOnlyModeEnabled(_featureFlags.isReadOnlyModeEnabled())
.setShowBrowseV2(_featureFlags.isShowBrowseV2())
.setShowAcrylInfo(_featureFlags.isShowAcrylInfo())
.setShowAccessManagement(_featureFlags.isShowAccessManagement())
.build();

appConfig.setFeatureFlags(featureFlagsConfig);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ private SearchUtils() {
EntityType.GLOSSARY_TERM,
EntityType.GLOSSARY_NODE,
EntityType.TAG,
EntityType.ROLE,
EntityType.CORP_USER,
EntityType.CORP_GROUP,
EntityType.CONTAINER,
Expand All @@ -94,6 +95,7 @@ private SearchUtils() {
EntityType.TAG,
EntityType.CORP_USER,
EntityType.CORP_GROUP,
EntityType.ROLE,
EntityType.NOTEBOOK,
EntityType.DATA_PRODUCT);

Expand Down Expand Up @@ -386,4 +388,4 @@ public static List<String> getEntityNames(List<EntityType> inputTypes) {
(inputTypes == null || inputTypes.isEmpty()) ? SEARCHABLE_ENTITY_TYPES : inputTypes;
return entityTypes.stream().map(EntityTypeMapper::getName).collect(Collectors.toList());
}
}
}
4 changes: 4 additions & 0 deletions datahub-graphql-core/src/main/resources/app.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,10 @@ type FeatureFlagsConfig {
Whether we should show CTAs in the UI related to moving to Managed DataHub by Acryl.
"""
showAcrylInfo: Boolean!
"""
Whether we should show AccessManagement tab in the datahub UI.
"""
showAccessManagement: Boolean!
}

"""
Expand Down
10 changes: 7 additions & 3 deletions datahub-graphql-core/src/main/resources/entity.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ type Query {
Fetch a Tag by primary key (urn)
"""
tag(urn: String!): Tag
"""
Fetch a Role by primary key (urn)
"""
role(urn: String!): Role

"""
Fetch a Glossary Term by primary key (urn)
Expand Down Expand Up @@ -1451,12 +1455,12 @@ type Role implements Entity {
"""
Role properties to include Request Access Url
"""
properties: RoleProperties!
properties: RoleProperties
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this no longer an optional field? What happens if someone wishes to create a role without properties?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @anshbansal we are facing issues in search and autocomplete in web-react search.graphql if we are making it as optional field. i.e why we update the same.


"""
A standard Entity Type
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm getting an error when trying locally if I don't set any role actors on a role saying that this is a required field that was not set.

I'd make this field not required for graphql since we aren't always setting actors on the result as you can see in RoleMapper.java

        final EnvelopedAspect envelopedUsers = aspects.get(Constants.ROLE_ACTORS_ASPECT_NAME);
        if (envelopedUsers != null) {
            result.setActors(mapActor(new Actors(envelopedUsers.getValue().data())));
        }

Or alternatively we could always set an empty list of actors and always return it saying we don't have any actors. either way we need to prevent this error from popping up:
image

Copy link
Contributor Author

@Ramendra761 Ramendra761 Sep 8, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated the actor as optional.

"""
actors: Actor!
actors: Actor


}
Expand Down Expand Up @@ -11164,4 +11168,4 @@ input UpdateOwnershipTypeInput {
The description of the Custom Ownership Type
"""
description: String
}
}
2 changes: 2 additions & 0 deletions datahub-web-react/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import GlossaryNodeEntity from './app/entity/glossaryNode/GlossaryNodeEntity';
import { DataPlatformEntity } from './app/entity/dataPlatform/DataPlatformEntity';
import { DataProductEntity } from './app/entity/dataProduct/DataProductEntity';
import { DataPlatformInstanceEntity } from './app/entity/dataPlatformInstance/DataPlatformInstanceEntity';
import { RoleEntity } from './app/entity/Access/RoleEntity';

/*
Construct Apollo Client
Expand Down Expand Up @@ -116,6 +117,7 @@ const App: React.VFC = () => {
register.register(new DomainEntity());
register.register(new ContainerEntity());
register.register(new GlossaryNodeEntity());
register.register(new RoleEntity());
register.register(new DataPlatformEntity());
register.register(new DataProductEntity());
register.register(new DataPlatformInstanceEntity());
Expand Down
88 changes: 88 additions & 0 deletions datahub-web-react/src/app/entity/Access/RoleEntity.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { TagOutlined, TagFilled } from '@ant-design/icons';
import * as React from 'react';
import styled from 'styled-components';
import { Role, EntityType, SearchResult } from '../../../types.generated';
import DefaultPreviewCard from '../../preview/DefaultPreviewCard';
import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '../Entity';
import { getDataForEntityType } from '../shared/containers/profile/utils';
import { urlEncodeUrn } from '../shared/utils';
import RoleEntityProfile from './RoleEntityProfile';

const PreviewTagIcon = styled(TagOutlined)`
font-size: 20px;
`;
// /**
// * Definition of the DataHub Access Role entity.
// */
export class RoleEntity implements Entity<Role> {
type: EntityType = EntityType.Role;

icon = (fontSize: number, styleType: IconStyleType, color?: string) => {
if (styleType === IconStyleType.TAB_VIEW) {
return <TagFilled style={{ fontSize, color }} />;
}

if (styleType === IconStyleType.HIGHLIGHT) {
return <TagFilled style={{ fontSize, color: color || '#B37FEB' }} />;
}

return (
<TagOutlined
style={{
fontSize,
color: color || '#BFBFBF',
}}
/>
);
};

isSearchEnabled = () => true;

isBrowseEnabled = () => false;

isLineageEnabled = () => false;

getAutoCompleteFieldName = () => 'name';

getPathName: () => string = () => 'role';

getCollectionName: () => string = () => 'Roles';

getEntityName: () => string = () => 'Role';

renderProfile: (urn: string) => JSX.Element = (_) => <RoleEntityProfile />;

renderPreview = (_: PreviewType, data: Role) => (
<DefaultPreviewCard
description={data?.properties?.description || ''}
name={this.displayName(data)}
urn={data.urn}
url={`/${this.getPathName()}/${urlEncodeUrn(data.urn)}`}
logoComponent={<PreviewTagIcon />}
type="Role"
typeIcon={this.icon(14, IconStyleType.ACCENT)}
/>
);

renderSearch = (result: SearchResult) => {
return this.renderPreview(PreviewType.SEARCH, result.entity as Role);
};

displayName = (data: Role) => {
return data.properties?.name || data.urn;
};

getOverridePropertiesFromEntity = (data: Role) => {
return {
name: data.properties?.name,
};
};

getGenericEntityProperties = (role: Role) => {
return getDataForEntityType({ data: role, entityType: this.type, getOverrideProperties: (data) => data });
};

supportedCapabilities = () => {
return new Set([EntityCapabilityType.OWNERS]);
};
}
75 changes: 75 additions & 0 deletions datahub-web-react/src/app/entity/Access/RoleEntityProfile.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import React from 'react';

import { useParams } from 'react-router';
import { Divider, Typography } from 'antd';
import { grey } from '@ant-design/colors';
import styled from 'styled-components';

import { Message } from '../../shared/Message';
import { decodeUrn } from '../shared/utils';
import { useGetExternalRoleQuery } from '../../../graphql/accessrole.generated';

const PageContainer = styled.div`
padding: 32px 100px;
`;

const LoadingMessage = styled(Message)`
margin-top: 10%;
`;

type RolePageParams = {
urn: string;
};

const TitleLabel = styled(Typography.Text)`
&&& {
color: ${grey[2]};
font-size: 12px;
display: block;
line-height: 20px;
font-weight: 700;
}
`;

const DescriptionLabel = styled(Typography.Text)`
&&& {
text-align: left;
font-weight: bold;
font-size: 14px;
line-height: 28px;
color: rgb(38, 38, 38);
}
`;

const TitleText = styled(Typography.Text)`
&&& {
color: ${grey[10]};
font-weight: 700;
font-size: 20px;
line-height: 28px;
display: inline-block;
margin: 0px 7px;
}
`;

const { Paragraph } = Typography;

export default function RoleEntityProfile() {
const { urn: encodedUrn } = useParams<RolePageParams>();
const urn = decodeUrn(encodedUrn);
const { data, loading } = useGetExternalRoleQuery({ variables: { urn } });

return (
<PageContainer>
{loading && <LoadingMessage type="loading" content="Loading..." />}
<TitleLabel>Role</TitleLabel>
<TitleText>{data?.role?.properties?.name}</TitleText>
<Divider />
{/* Role Description */}
<DescriptionLabel>About</DescriptionLabel>
<Paragraph style={{ fontSize: '12px', lineHeight: '15px', padding: '5px 0px' }}>
{data?.role?.properties?.description}
</Paragraph>
Comment on lines +70 to +72
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so you don't want to allow users to edit role entity descriptions through the UI? you could get the same functionality as other entities if you used EntityProfile and passed in a list of tabs and sidebar sections instead of defining your own entity profile page for this one entity

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i'm just thinking this is pretty bare bones but maybe that is alright for now.

image

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hi @chriscollins3456 for Roles the description is not editable. We have description for the accessRole coming from an external source we are updating in datahub if we provide option to edit the dscription we would not be able to update the same at source.

</PageContainer>
);
}
12 changes: 12 additions & 0 deletions datahub-web-react/src/app/entity/dataset/DatasetEntity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';
import { DatabaseFilled, DatabaseOutlined } from '@ant-design/icons';
import { Dataset, DatasetProperties, EntityType, OwnershipType, SearchResult } from '../../../types.generated';
import { Entity, EntityCapabilityType, IconStyleType, PreviewType } from '../Entity';
import { useAppConfig } from '../../useAppConfig';
import { Preview } from './preview/Preview';
import { EntityProfile } from '../shared/containers/profile/EntityProfile';
import { GetDatasetQuery, useGetDatasetQuery, useUpdateDatasetMutation } from '../../../graphql/dataset.generated';
Expand Down Expand Up @@ -30,6 +31,7 @@ import { EmbedTab } from '../shared/tabs/Embed/EmbedTab';
import EmbeddedProfile from '../shared/embed/EmbeddedProfile';
import DataProductSection from '../shared/containers/profile/sidebar/DataProduct/DataProductSection';
import { getDataProduct } from '../shared/utils';
import AccessManagement from '../shared/tabs/Dataset/AccessManagement/AccessManagement';
import { matchedFieldPathsRenderer } from '../../search/matches/matchedFieldPathsRenderer';

const SUBTYPES = {
Expand Down Expand Up @@ -69,6 +71,8 @@ export class DatasetEntity implements Entity<Dataset> {

isSearchEnabled = () => true;

appconfig = useAppConfig;

isBrowseEnabled = () => true;

isLineageEnabled = () => true;
Expand Down Expand Up @@ -176,6 +180,14 @@ export class DatasetEntity implements Entity<Dataset> {
},
},
},
{
name: 'Access Management',
component: AccessManagement,
display: {
visible: (_, _1) => this.appconfig().config.featureFlags.showAccessManagement,
enabled: (_, _2) => true,
},
},
]}
sidebarSections={[
{
Expand Down
Loading
Loading