Skip to content

Commit

Permalink
Merge branch 'master' into stateful-ingest-simplification
Browse files Browse the repository at this point in the history
  • Loading branch information
hsheth2 authored Nov 18, 2022
2 parents 7287b22 + b7c0373 commit 8230554
Show file tree
Hide file tree
Showing 25 changed files with 306 additions and 184 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import com.linkedin.datahub.graphql.generated.AcceptRoleInput;
import graphql.schema.DataFetcher;
import graphql.schema.DataFetchingEnvironment;
import java.util.Collections;
import java.util.concurrent.CompletableFuture;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -32,15 +33,14 @@ public CompletableFuture<Boolean> get(DataFetchingEnvironment environment) throw

return CompletableFuture.supplyAsync(() -> {
try {
Urn inviteTokenUrn = _inviteTokenService.getInviteTokenUrn(inviteTokenStr);
final Urn inviteTokenUrn = _inviteTokenService.getInviteTokenUrn(inviteTokenStr);
if (!_inviteTokenService.isInviteTokenValid(inviteTokenUrn, authentication)) {
throw new RuntimeException(String.format("Invite token %s is invalid", inviteTokenStr));
}

Urn roleUrn = _inviteTokenService.getInviteTokenRole(inviteTokenUrn, authentication);
if (roleUrn != null) {
_roleService.assignRoleToActor(authentication.getActor().toUrnStr(), roleUrn, authentication);
}
final Urn roleUrn = _inviteTokenService.getInviteTokenRole(inviteTokenUrn, authentication);
_roleService.batchAssignRoleToActors(Collections.singletonList(authentication.getActor().toUrnStr()), roleUrn,
authentication);

return true;
} catch (Exception e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,19 +37,8 @@ public CompletableFuture<Boolean> get(DataFetchingEnvironment environment) throw

return CompletableFuture.supplyAsync(() -> {
try {
Urn roleUrn = Urn.createFromString(roleUrnStr);
if (!_roleService.exists(roleUrn, authentication)) {
throw new RuntimeException(String.format("Role %s does not exist", roleUrnStr));
}

actors.forEach(actor -> {
try {
_roleService.assignRoleToActor(actor, roleUrn, authentication);
} catch (Exception e) {
log.warn(
String.format("Failed to assign role %s to actor %s. Skipping actor assignment", roleUrnStr, actor), e);
}
});
final Urn roleUrn = roleUrnStr == null ? null : Urn.createFromString(roleUrnStr);
_roleService.batchAssignRoleToActors(actors, roleUrn, authentication);
return true;
} catch (Exception e) {
throw new RuntimeException(String.format("Failed to perform update against input %s", input), e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.linkedin.common.urn.Urn;
import com.linkedin.datahub.graphql.QueryContext;
import com.linkedin.datahub.graphql.exception.AuthorizationException;
import com.linkedin.datahub.graphql.generated.DataHubRole;
import com.linkedin.datahub.graphql.generated.ListRolesInput;
import com.linkedin.datahub.graphql.generated.ListRolesResult;
Expand All @@ -24,7 +23,6 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import static com.linkedin.datahub.graphql.authorization.AuthorizationUtils.*;
import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.*;
import static com.linkedin.metadata.Constants.*;

Expand All @@ -41,10 +39,6 @@ public class ListRolesResolver implements DataFetcher<CompletableFuture<ListRole
@Override
public CompletableFuture<ListRolesResult> get(final DataFetchingEnvironment environment) throws Exception {
final QueryContext context = environment.getContext();
if (!canManagePolicies(context)) {
throw new AuthorizationException(
"Unauthorized to view roles. Please contact your DataHub administrator if this needs corrective action.");
}

final ListRolesInput input = bindArgument(environment.getArgument("input"), ListRolesInput.class);
final Integer start = input.getStart() == null ? DEFAULT_START : input.getStart();
Expand Down
4 changes: 2 additions & 2 deletions datahub-graphql-core/src/main/resources/entity.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -9328,9 +9328,9 @@ Input provided when batch assigning a role to a list of users
"""
input BatchAssignRoleInput {
"""
The urn of the role to assign to the actors
The urn of the role to assign to the actors. If undefined, will remove the role.
"""
roleUrn: String!
roleUrn: String

"""
The urns of the actors to assign the role to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,16 @@ public void testNoRoleUrn() throws Exception {
when(_inviteTokenService.getInviteTokenUrn(eq(INVITE_TOKEN_STRING))).thenReturn(inviteTokenUrn);
when(_inviteTokenService.isInviteTokenValid(eq(inviteTokenUrn), eq(_authentication))).thenReturn(true);
when(_inviteTokenService.getInviteTokenRole(eq(inviteTokenUrn), eq(_authentication))).thenReturn(null);
Actor actor = mock(Actor.class);
when(_authentication.getActor()).thenReturn(actor);
when(actor.toUrnStr()).thenReturn(ACTOR_URN_STRING);

AcceptRoleInput input = new AcceptRoleInput();
input.setInviteToken(INVITE_TOKEN_STRING);
when(_dataFetchingEnvironment.getArgument(eq("input"))).thenReturn(input);

assertTrue(_resolver.get(_dataFetchingEnvironment).join());
verify(_roleService, never()).assignRoleToActor(any(), any(), any());
verify(_roleService, times(1)).batchAssignRoleToActors(any(), any(), any());
}

@Test
Expand All @@ -97,6 +100,6 @@ public void testAssignRolePasses() throws Exception {
when(_dataFetchingEnvironment.getArgument(eq("input"))).thenReturn(input);

assertTrue(_resolver.get(_dataFetchingEnvironment).join());
verify(_roleService, times(1)).assignRoleToActor(any(), any(), any());
verify(_roleService, times(1)).batchAssignRoleToActors(any(), any(), any());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

import com.datahub.authentication.Authentication;
import com.datahub.authorization.role.RoleService;
import com.google.common.collect.ImmutableList;
import com.linkedin.common.urn.Urn;
import com.linkedin.datahub.graphql.QueryContext;
import com.linkedin.datahub.graphql.generated.BatchAssignRoleInput;
import graphql.schema.DataFetchingEnvironment;
import java.util.ArrayList;
import java.util.List;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
Expand Down Expand Up @@ -45,56 +45,30 @@ public void testNotAuthorizedFails() {
}

@Test
public void testRoleDoesNotExistFails() throws Exception {
public void testNullRole() throws Exception {
QueryContext mockContext = getMockAllowContext();
when(_dataFetchingEnvironment.getContext()).thenReturn(mockContext);
when(mockContext.getAuthentication()).thenReturn(_authentication);

BatchAssignRoleInput input = new BatchAssignRoleInput();
input.setRoleUrn(ROLE_URN_STRING);
List<String> actors = new ArrayList<>();
actors.add(FIRST_ACTOR_URN_STRING);
input.setActors(actors);
when(_dataFetchingEnvironment.getArgument("input")).thenReturn(input);
when(_roleService.exists(eq(roleUrn), eq(_authentication))).thenReturn(false);

assertThrows(() -> _resolver.get(_dataFetchingEnvironment).join());
}

@Test
public void testSomeActorsExist() throws Exception {
QueryContext mockContext = getMockAllowContext();
when(_dataFetchingEnvironment.getContext()).thenReturn(mockContext);
when(mockContext.getAuthentication()).thenReturn(_authentication);

BatchAssignRoleInput input = new BatchAssignRoleInput();
input.setRoleUrn(ROLE_URN_STRING);
List<String> actors = new ArrayList<>();
actors.add(FIRST_ACTOR_URN_STRING);
actors.add(SECOND_ACTOR_URN_STRING);
List<String> actors = ImmutableList.of(FIRST_ACTOR_URN_STRING, SECOND_ACTOR_URN_STRING);
input.setActors(actors);
when(_dataFetchingEnvironment.getArgument(eq("input"))).thenReturn(input);
doThrow(RuntimeException.class).when(_roleService)
.assignRoleToActor(eq(SECOND_ACTOR_URN_STRING), eq(roleUrn), eq(_authentication));
when(_roleService.exists(eq(Urn.createFromString(ROLE_URN_STRING)), eq(_authentication))).thenReturn(true);

assertTrue(_resolver.get(_dataFetchingEnvironment).join());
}

@Test
public void testAllActorsExist() throws Exception {
public void testNotNullRole() throws Exception {
QueryContext mockContext = getMockAllowContext();
when(_dataFetchingEnvironment.getContext()).thenReturn(mockContext);
when(mockContext.getAuthentication()).thenReturn(_authentication);

BatchAssignRoleInput input = new BatchAssignRoleInput();
input.setRoleUrn(ROLE_URN_STRING);
List<String> actors = new ArrayList<>();
actors.add(FIRST_ACTOR_URN_STRING);
actors.add(SECOND_ACTOR_URN_STRING);
List<String> actors = ImmutableList.of(FIRST_ACTOR_URN_STRING, SECOND_ACTOR_URN_STRING);
input.setActors(actors);
when(_dataFetchingEnvironment.getArgument(eq("input"))).thenReturn(input);
when(_roleService.exists(eq(Urn.createFromString(ROLE_URN_STRING)), eq(_authentication))).thenReturn(true);

assertTrue(_resolver.get(_dataFetchingEnvironment).join());
}
Expand Down
6 changes: 6 additions & 0 deletions datahub-web-react/src/app/analytics/event.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export enum EventType {
RecommendationImpressionEvent,
RecommendationClickEvent,
HomePageRecommendationClickEvent,
HomePageExploreAllClickEvent,
SearchAcrossLineageEvent,
SearchAcrossLineageResultsViewEvent,
DownloadAsCsvEvent,
Expand Down Expand Up @@ -338,6 +339,10 @@ export interface ShowStandardHomepageEvent extends BaseEvent {
type: EventType.ShowStandardHomepageEvent;
}

export interface HomePageExploreAllClickEvent extends BaseEvent {
type: EventType.HomePageExploreAllClickEvent;
}

// Business glossary events

export interface CreateGlossaryEntityEvent extends BaseEvent {
Expand Down Expand Up @@ -389,6 +394,7 @@ export type Event =
| ResetCredentialsEvent
| SearchEvent
| HomePageSearchEvent
| HomePageExploreAllClickEvent
| SearchResultsViewEvent
| SearchResultClickEvent
| BrowseResultClickEvent
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { filterSchemaRows } from '../utils/filterSchemaRows';

describe('filterSchemaRows', () => {
const testEntityRegistry = getTestEntityRegistry();
const rows = [{ fieldPath: 'customer' }, { fieldPath: 'testing' }, { fieldPath: 'shipment' }] as SchemaField[];
const rows = [
{ fieldPath: 'customer', description: 'customer description' },
{ fieldPath: 'testing', description: 'testing description' },
{ fieldPath: 'shipment', description: 'shipment description' },
] as SchemaField[];

it('should properly filter schema rows based on field name', () => {
const filterText = 'test';
Expand Down Expand Up @@ -35,6 +39,80 @@ describe('filterSchemaRows', () => {
expect(expandedRowsFromFilter).toMatchObject(new Set());
});

it('should properly filter schema rows based on description', () => {
const filterText = 'testing description';
const editableSchemaMetadata = { editableSchemaFieldInfo: [] };
const { filteredRows, expandedRowsFromFilter } = filterSchemaRows(
rows,
editableSchemaMetadata,
filterText,
testEntityRegistry,
);

expect(filteredRows).toMatchObject([{ fieldPath: 'testing' }]);
expect(expandedRowsFromFilter).toMatchObject(new Set());
});

it('should properly filter schema rows based on description regardless of capitalization', () => {
const editableSchemaMetadata = { editableSchemaFieldInfo: [] };
const filterText = 'TeSting DesCriptioN';
const { filteredRows, expandedRowsFromFilter } = filterSchemaRows(
rows,
editableSchemaMetadata,
filterText,
testEntityRegistry,
);

expect(filteredRows).toMatchObject([{ fieldPath: 'testing' }]);
expect(expandedRowsFromFilter).toMatchObject(new Set());
});

it('should properly filter schema rows based on editable description', () => {
const editableSchemaMetadata = {
editableSchemaFieldInfo: [
{
fieldPath: 'customer',
description: 'editable customer description',
globalTags: null,
glossaryTerms: null,
},
],
};
const filterText = 'editable customer description';
const { filteredRows, expandedRowsFromFilter } = filterSchemaRows(
rows,
editableSchemaMetadata,
filterText,
testEntityRegistry,
);

expect(filteredRows).toMatchObject([{ fieldPath: 'customer' }]);
expect(expandedRowsFromFilter).toMatchObject(new Set());
});

it('should properly filter schema rows based on editable description regardless of capitalization', () => {
const editableSchemaMetadata = {
editableSchemaFieldInfo: [
{
fieldPath: 'customer',
description: 'editable customer description',
globalTags: null,
glossaryTerms: null,
},
],
};
const filterText = 'EdiTable CuStoMer DesCriptioN';
const { filteredRows, expandedRowsFromFilter } = filterSchemaRows(
rows,
editableSchemaMetadata,
filterText,
testEntityRegistry,
);

expect(filteredRows).toMatchObject([{ fieldPath: 'customer' }]);
expect(expandedRowsFromFilter).toMatchObject(new Set());
});

it('should properly filter schema rows based on editable tags', () => {
const editableSchemaMetadata = {
editableSchemaFieldInfo: [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { EntityType, SchemaField } from '../../../../../../../types.generated';
import EntityRegistry from '../../../../../EntityRegistry';

function matchesTagsOrTerms(field: SchemaField, filterText: string, entityRegistry: EntityRegistry) {
function matchesTagsOrTermsOrDescription(field: SchemaField, filterText: string, entityRegistry: EntityRegistry) {
return (
field.globalTags?.tags?.find((tagAssociation) =>
entityRegistry.getDisplayName(EntityType.Tag, tagAssociation.tag).toLocaleLowerCase().includes(filterText),
Expand All @@ -11,20 +11,21 @@ function matchesTagsOrTerms(field: SchemaField, filterText: string, entityRegist
.getDisplayName(EntityType.GlossaryTerm, termAssociation.term)
.toLocaleLowerCase()
.includes(filterText),
)
) ||
field.description?.toLocaleLowerCase().includes(filterText)
);
}

// returns list of fieldPaths for fields that have Terms or Tags matching the filterText
// returns list of fieldPaths for fields that have Terms or Tags or Descriptions matching the filterText
function getFilteredFieldPathsByMetadata(editableSchemaMetadata: any, entityRegistry, filterText) {
return (
editableSchemaMetadata?.editableSchemaFieldInfo
.filter((fieldInfo) => matchesTagsOrTerms(fieldInfo, filterText, entityRegistry))
.filter((fieldInfo) => matchesTagsOrTermsOrDescription(fieldInfo, filterText, entityRegistry))
.map((fieldInfo) => fieldInfo.fieldPath) || []
);
}

function matchesEditableTagsOrTerms(field: SchemaField, filteredFieldPathsByEditableMetadata: any) {
function matchesEditableTagsOrTermsOrDescription(field: SchemaField, filteredFieldPathsByEditableMetadata: any) {
return filteredFieldPathsByEditableMetadata.includes(field.fieldPath);
}

Expand All @@ -47,23 +48,24 @@ export function filterSchemaRows(
entityRegistry,
formattedFilterText,
);

const finalFieldPaths = new Set();
const expandedRowsFromFilter = new Set();

rows.forEach((row) => {
if (
matchesFieldName(row.fieldPath, formattedFilterText) ||
matchesEditableTagsOrTerms(row, filteredFieldPathsByEditableMetadata) ||
matchesTagsOrTerms(row, formattedFilterText, entityRegistry) // non-editable tags and terms
matchesEditableTagsOrTermsOrDescription(row, filteredFieldPathsByEditableMetadata) ||
matchesTagsOrTermsOrDescription(row, formattedFilterText, entityRegistry) // non-editable tags, terms and description
) {
finalFieldPaths.add(row.fieldPath);
}
const splitFieldPath = row.fieldPath.split('.');
const fieldName = splitFieldPath.slice(-1)[0];
if (
matchesFieldName(fieldName, formattedFilterText) ||
matchesEditableTagsOrTerms(row, filteredFieldPathsByEditableMetadata) ||
matchesTagsOrTerms(row, formattedFilterText, entityRegistry)
matchesEditableTagsOrTermsOrDescription(row, filteredFieldPathsByEditableMetadata) ||
matchesTagsOrTermsOrDescription(row, formattedFilterText, entityRegistry) // non-editable tags, terms and description
) {
// if we match specifically on this field (not just its parent), add and expand all parents
splitFieldPath.reduce((previous, current) => {
Expand Down
5 changes: 1 addition & 4 deletions datahub-web-react/src/app/glossary/BusinessGlossaryPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ import React, { useState } from 'react';
import { Button, Typography } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import styled from 'styled-components/macro';

import { useGetRootGlossaryNodesQuery, useGetRootGlossaryTermsQuery } from '../../graphql/glossary.generated';
import TabToolbar from '../entity/shared/components/styled/TabToolbar';
import GlossaryEntitiesPath from './GlossaryEntitiesPath';
import GlossaryEntitiesList from './GlossaryEntitiesList';
import GlossaryBrowser from './GlossaryBrowser/GlossaryBrowser';
import GlossarySearch from './GlossarySearch';
Expand Down Expand Up @@ -93,9 +91,8 @@ function BusinessGlossaryPage() {
isSidebarOnLeft
/>
<MainContentWrapper>
<GlossaryEntitiesPath />
<HeaderWrapper>
<Typography.Title level={3}>Glossary</Typography.Title>
<Typography.Title level={3}>Business Glossary</Typography.Title>
<div>
<Button type="text" onClick={() => setIsCreateTermModalVisible(true)}>
<PlusOutlined /> Add Term
Expand Down
Loading

0 comments on commit 8230554

Please sign in to comment.