Skip to content

Commit e0ff89d

Browse files
authored
Merge pull request datahub-project#10 from aditigup-visa/business-attributes-v1
Cypress Test Cases, Preview Test Case, updating delete BA api, Removi…
2 parents 8bc9669 + 70054ea commit e0ff89d

12 files changed

Lines changed: 483 additions & 38 deletions

File tree

datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/businessattribute/RemoveBusinessAttributeResolver.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,13 @@ public CompletableFuture<Boolean> get(DataFetchingEnvironment environment) throw
4343
if (!isAuthorizeToUpdateDataset(context, Urn.createFromString(resourceRefInput.getResourceUrn()))) {
4444
throw new AuthorizationException("Unauthorized to perform this action. Please contact your DataHub administrator.");
4545
}
46-
if (!_entityClient.exists(businessAttributeUrn, context.getAuthentication())) {
47-
throw new RuntimeException(String.format("This urn does not exist: %s", businessAttributeUrn));
48-
}
4946
return CompletableFuture.supplyAsync(() -> {
5047
try {
48+
if (!businessAttributeUrn.getEntityType().equals("businessAttribute")) {
49+
log.error("Failed to remove {}. It is not a business attribute urn.", businessAttributeUrn.toString());
50+
return false;
51+
}
52+
5153
validateInputResource(resourceRefInput, context);
5254

5355
removeBusinessAttribute(resourceRefInput, context);

datahub-web-react/src/Mocks.tsx

Lines changed: 114 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,45 @@
1-
import { GetDatasetDocument, UpdateDatasetDocument, GetDatasetSchemaDocument } from './graphql/dataset.generated';
2-
import { GetDataFlowDocument } from './graphql/dataFlow.generated';
3-
import { GetDataJobDocument } from './graphql/dataJob.generated';
4-
import { GetBrowsePathsDocument, GetBrowseResultsDocument } from './graphql/browse.generated';
1+
import {GetDatasetDocument, GetDatasetSchemaDocument, UpdateDatasetDocument} from './graphql/dataset.generated';
2+
import {GetDataFlowDocument} from './graphql/dataFlow.generated';
3+
import {GetDataJobDocument} from './graphql/dataJob.generated';
4+
import {GetBrowsePathsDocument, GetBrowseResultsDocument} from './graphql/browse.generated';
55
import {
6-
GetAutoCompleteResultsDocument,
76
GetAutoCompleteMultipleResultsDocument,
7+
GetAutoCompleteResultsDocument,
88
GetSearchResultsDocument,
9-
GetSearchResultsQuery,
109
GetSearchResultsForMultipleDocument,
1110
GetSearchResultsForMultipleQuery,
11+
GetSearchResultsQuery,
1212
} from './graphql/search.generated';
13-
import { GetUserDocument } from './graphql/user.generated';
13+
import {GetUserDocument} from './graphql/user.generated';
1414
import {
15-
Dataset,
15+
AppConfig,
16+
BusinessAttribute,
17+
Container,
1618
DataFlow,
1719
DataJob,
18-
GlossaryTerm,
19-
GlossaryNode,
20+
Dataset,
2021
EntityType,
21-
PlatformType,
22+
FilterOperator,
23+
GlossaryNode,
24+
GlossaryTerm,
2225
MlModel,
2326
MlModelGroup,
24-
SchemaFieldDataType,
25-
ScenarioType,
27+
PlatformPrivileges,
28+
PlatformType,
2629
RecommendationRenderType,
2730
RelationshipDirection,
28-
Container,
29-
PlatformPrivileges,
30-
FilterOperator,
31-
AppConfig,
31+
ScenarioType,
32+
SchemaFieldDataType,
3233
} from './types.generated';
33-
import { GetTagDocument } from './graphql/tag.generated';
34-
import { GetMlModelDocument } from './graphql/mlModel.generated';
35-
import { GetMlModelGroupDocument } from './graphql/mlModelGroup.generated';
36-
import { GetGlossaryTermDocument, GetGlossaryTermQuery } from './graphql/glossaryTerm.generated';
37-
import { GetEntityCountsDocument, AppConfigDocument } from './graphql/app.generated';
38-
import { GetMeDocument } from './graphql/me.generated';
39-
import { ListRecommendationsDocument } from './graphql/recommendations.generated';
40-
import { FetchedEntity } from './app/lineage/types';
41-
import { DEFAULT_APP_CONFIG } from './appConfigContext';
34+
import {GetTagDocument} from './graphql/tag.generated';
35+
import {GetMlModelDocument} from './graphql/mlModel.generated';
36+
import {GetMlModelGroupDocument} from './graphql/mlModelGroup.generated';
37+
import {GetGlossaryTermDocument, GetGlossaryTermQuery} from './graphql/glossaryTerm.generated';
38+
import {AppConfigDocument, GetEntityCountsDocument} from './graphql/app.generated';
39+
import {GetMeDocument} from './graphql/me.generated';
40+
import {ListRecommendationsDocument} from './graphql/recommendations.generated';
41+
import {FetchedEntity} from './app/lineage/types';
42+
import {DEFAULT_APP_CONFIG} from './appConfigContext';
4243

4344
export const user1 = {
4445
username: 'sdas',
@@ -1321,6 +1322,92 @@ export const dataJob1 = {
13211322
deprecation: null,
13221323
} as DataJob;
13231324

1325+
export const businessAttribute = {
1326+
urn: 'urn:li:businessAttribute:ba1',
1327+
type: EntityType.BusinessAttribute,
1328+
__typename: 'BusinessAttribute',
1329+
properties: {
1330+
name: 'TestBusinessAtt-2',
1331+
description: 'lorem upsum updated 12',
1332+
created: {
1333+
time: 1705857132786
1334+
},
1335+
lastModified: {
1336+
time: 1705857132786
1337+
},
1338+
glossaryTerms: {
1339+
terms: [
1340+
{
1341+
term: {
1342+
urn: 'urn:li:glossaryTerm:1'
1343+
},
1344+
associatedUrn: 'urn:li:businessAttribute:ba1'
1345+
}
1346+
],
1347+
__typename: 'GlossaryTerms',
1348+
},
1349+
tags: {
1350+
__typename: 'GlobalTags',
1351+
tags: [
1352+
{
1353+
tag: {
1354+
urn: 'urn:li:tag:abc-sample-tag',
1355+
__typename: 'Tag'
1356+
},
1357+
__typename: 'TagAssociation',
1358+
associatedUrn: 'urn:li:businessAttribute:ba1'
1359+
},
1360+
{
1361+
tag: {
1362+
urn: 'urn:li:tag:TestTag',
1363+
__typename: 'Tag'
1364+
},
1365+
__typename: 'TagAssociation',
1366+
associatedUrn: 'urn:li:businessAttribute:ba1'
1367+
}
1368+
]
1369+
},
1370+
customProperties: [
1371+
{
1372+
key: 'prop2',
1373+
value: 'val2',
1374+
__typename: 'CustomPropertiesEntry'
1375+
},
1376+
{
1377+
key: 'prop1',
1378+
value: 'val1',
1379+
__typename: 'CustomPropertiesEntry'
1380+
},
1381+
{
1382+
key: 'prop3',
1383+
value: 'val3',
1384+
__typename: 'CustomPropertiesEntry'
1385+
}
1386+
]
1387+
},
1388+
ownership: {
1389+
owners: [
1390+
{
1391+
owner: {
1392+
...user1,
1393+
},
1394+
associatedUrn: 'urn:li:businessAttribute:ba',
1395+
type: 'DATAOWNER',
1396+
},
1397+
{
1398+
owner: {
1399+
...user2,
1400+
},
1401+
associatedUrn: 'urn:li:businessAttribute:ba',
1402+
type: 'DELEGATE',
1403+
},
1404+
],
1405+
lastModified: {
1406+
time: 0,
1407+
},
1408+
},
1409+
} as BusinessAttribute;
1410+
13241411
export const dataJob2 = {
13251412
__typename: 'DataJob',
13261413
urn: 'urn:li:dataJob:2',
@@ -1686,7 +1773,7 @@ export const recommendationModules = [
16861773
];
16871774

16881775
/*
1689-
Define mock data to be returned by Apollo MockProvider.
1776+
Define mock data to be returned by Apollo MockProvider.
16901777
*/
16911778
export const mocks = [
16921779
{

datahub-web-react/src/app/businessAttribute/BusinessAttributeItemMenu.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22
import { DeleteOutlined } from '@ant-design/icons';
33
import { Dropdown, Menu, message, Modal } from 'antd';
44
import { MenuIcon } from '../entity/shared/EntityDropdown/EntityDropdown';
5-
import { useDeletePostMutation } from '../../graphql/post.generated';
5+
import { useDeleteBusinessAttributeMutation } from '../../graphql/businessAttribute.generated';
66

77
type Props = {
88
urn: string;
@@ -11,10 +11,10 @@ type Props = {
1111
};
1212

1313
export default function BusinessAttributeItemMenu({ title, urn, onDelete }: Props) {
14-
const [deletePostMutation] = useDeletePostMutation();
14+
const [deleteBusinessAttributeMutation] = useDeleteBusinessAttributeMutation();
1515

1616
const deletePost = () => {
17-
deletePostMutation({
17+
deleteBusinessAttributeMutation({
1818
variables: {
1919
urn,
2020
},
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import {MockedProvider} from '@apollo/client/testing';
2+
import {render} from '@testing-library/react';
3+
import React from 'react';
4+
import {mocks} from '../../../../../Mocks';
5+
import TestPageContainer from '../../../../../utils/test-utils/TestPageContainer';
6+
import {Preview} from '../Preview';
7+
import {PreviewType} from "../../../Entity";
8+
9+
describe('Preview', () => {
10+
it('renders', () => {
11+
const { getByText } = render(
12+
<MockedProvider mocks={mocks} addTypename={false}>
13+
<TestPageContainer>
14+
<Preview
15+
urn="urn:li:businessAttribute:ba1"
16+
name="name"
17+
description="definition"
18+
owners={null}
19+
previewType={PreviewType.PREVIEW}
20+
/>
21+
</TestPageContainer>
22+
</MockedProvider>,
23+
);
24+
expect(getByText('definition')).toBeInTheDocument();
25+
});
26+
});

datahub-web-react/src/app/entity/businessAttribute/profile/BusinessAttributeDataTypeSection.tsx

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,25 @@ export const BusinessAttributeDataTypeSection = ({ readOnly }: Props) => {
6565
<SidebarHeader
6666
title="Data Type"
6767
actions={
68-
originalDescription &&
6968
!readOnly && (
70-
<Button onClick={handleEditClick} type="text" shape="circle">
69+
<Button
70+
data-testid="edit-data-type-button"
71+
onClick={handleEditClick}
72+
type="text"
73+
shape="circle"
74+
>
7175
<EditOutlined />
7276
</Button>
7377
)
7478
}
7579
/>
7680
{originalDescription}
7781
{isEditing && (
78-
<DataTypeSelect placeholder="A data type for business attribute" onChange={handleChange}>
82+
<DataTypeSelect
83+
data-testid="add-data-type-option"
84+
placeholder="A data type for business attribute"
85+
onChange={handleChange}
86+
>
7987
{DATA_TYPES.map((dataType: SchemaFieldDataType) => (
8088
<Select.Option key={dataType} value={dataType}>
8189
{dataType}

datahub-web-react/src/utils/test-utils/TestPageContainer.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import UserContextProvider from '../../app/context/UserContextProvider';
2525
import { DataPlatformEntity } from '../../app/entity/dataPlatform/DataPlatformEntity';
2626
import { ContainerEntity } from '../../app/entity/container/ContainerEntity';
2727
import AppConfigProvider from '../../AppConfigProvider';
28+
import {BusinessAttributeEntity} from "../../app/entity/businessAttribute/BusinessAttributeEntity";
2829

2930
type Props = {
3031
children: React.ReactNode;
@@ -47,6 +48,7 @@ export function getTestEntityRegistry() {
4748
entityRegistry.register(new MLModelGroupEntity());
4849
entityRegistry.register(new DataPlatformEntity());
4950
entityRegistry.register(new ContainerEntity());
51+
entityRegistry.register(new BusinessAttributeEntity());
5052
return entityRegistry;
5153
}
5254

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
describe("attribute list adding tags and terms", () => {
2+
it("can create and add a tag to business attribute and visit new tag page", () => {
3+
cy.login();
4+
cy.goToBusinessAttributeList();
5+
6+
cy.mouseover('[data-testid="schema-field-cypressTestAttribute-tags"]');
7+
cy.get('[data-testid="schema-field-cypressTestAttribute-tags"]').within(() =>
8+
cy.contains("Add Tags").click()
9+
);
10+
11+
cy.enterTextInTestId("tag-term-modal-input", "CypressAddTagToAttribute");
12+
13+
cy.contains("Create CypressAddTagToAttribute").click({ force: true });
14+
15+
cy.get("textarea").type("CypressAddTagToAttribute Test Description");
16+
17+
cy.contains(/Create$/).click({ force: true });
18+
19+
// wait a breath for elasticsearch to index the tag being applied to the business attribute- if we navigate too quick ES
20+
// wont know and we'll see applied to 0 entities
21+
cy.wait(2000);
22+
23+
// go to tag drawer
24+
cy.contains("CypressAddTagToAttribute").click({ force: true });
25+
26+
cy.wait(1000);
27+
28+
// Click the Tag Details to launch full profile
29+
cy.contains("Tag Details").click({ force: true });
30+
31+
cy.wait(1000);
32+
33+
// title of tag page
34+
cy.contains("CypressAddTagToAttribute");
35+
36+
// description of tag page
37+
cy.contains("CypressAddTagToAttribute Test Description");
38+
39+
// used by panel - click to search
40+
cy.contains("1 Business Attributes").click({ force: true });
41+
42+
// verify business attribute shows up in search now
43+
cy.contains("of 1 result").click({ force: true });
44+
cy.contains("cypressTestAttribute").click({ force: true });
45+
cy.get('[data-testid="tag-CypressAddTagToAttribute"]').within(() =>
46+
cy.get("span[aria-label=close]").click()
47+
);
48+
cy.contains("Yes").click();
49+
50+
cy.contains("CypressAddTagToAttribute").should("not.exist");
51+
52+
cy.goToTag("urn:li:tag:CypressAddTagToAttribute", "CypressAddTagToAttribute");
53+
cy.deleteFromDropdown();
54+
55+
});
56+
57+
it("can add and remove terms from a business attribute", () => {
58+
cy.login();
59+
cy.addTermToBusinessAttribute(
60+
"urn:li:businessAttribute:cypressTestAttribute",
61+
"cypressTestAttribute",
62+
"CypressTerm"
63+
)
64+
65+
cy.goToBusinessAttributeList();
66+
cy.get('[data-testid="schema-field-cypressTestAttribute-terms"]').contains("CypressTerm");
67+
68+
cy.get('[data-testid="schema-field-cypressTestAttribute-terms"]').within(() =>
69+
cy
70+
.get("span[aria-label=close]")
71+
.trigger("mouseover", { force: true })
72+
.click({ force: true })
73+
);
74+
cy.contains("Yes").click({ force: true });
75+
76+
cy.get('[data-testid="schema-field-cypressTestAttribute-terms"]').contains("CypressTerm").should("not.exist");
77+
});
78+
});

0 commit comments

Comments
 (0)