mirror of
https://github.com/open-metadata/OpenMetadata.git
synced 2025-10-11 16:58:38 +00:00
fix contract status button not visible in persona having contract tab (#23237)
* fix contract status button not visible in persona having contract tab * supported playwright test for the same * fix the unit test failing
This commit is contained in:
parent
1f4186b661
commit
999af800b3
@ -54,7 +54,6 @@ const test = base.extend<{ page: Page }>({
|
|||||||
|
|
||||||
test.describe('Data Contracts', () => {
|
test.describe('Data Contracts', () => {
|
||||||
const table = new TableClass();
|
const table = new TableClass();
|
||||||
const table2 = new TableClass();
|
|
||||||
const testClassification = new ClassificationClass();
|
const testClassification = new ClassificationClass();
|
||||||
const testTag = new TagClass({
|
const testTag = new TagClass({
|
||||||
classification: testClassification.data.name,
|
classification: testClassification.data.name,
|
||||||
@ -68,7 +67,6 @@ test.describe('Data Contracts', () => {
|
|||||||
|
|
||||||
const { apiContext, afterAction } = await performAdminLogin(browser);
|
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||||
await table.create(apiContext);
|
await table.create(apiContext);
|
||||||
await table2.create(apiContext);
|
|
||||||
await testClassification.create(apiContext);
|
await testClassification.create(apiContext);
|
||||||
await testTag.create(apiContext);
|
await testTag.create(apiContext);
|
||||||
await testGlossary.create(apiContext);
|
await testGlossary.create(apiContext);
|
||||||
@ -111,7 +109,6 @@ test.describe('Data Contracts', () => {
|
|||||||
|
|
||||||
const { apiContext, afterAction } = await performAdminLogin(browser);
|
const { apiContext, afterAction } = await performAdminLogin(browser);
|
||||||
await table.delete(apiContext);
|
await table.delete(apiContext);
|
||||||
await table2.delete(apiContext);
|
|
||||||
await testClassification.delete(apiContext);
|
await testClassification.delete(apiContext);
|
||||||
await testTag.delete(apiContext);
|
await testTag.delete(apiContext);
|
||||||
await testGlossary.delete(apiContext);
|
await testGlossary.delete(apiContext);
|
||||||
@ -541,7 +538,7 @@ test.describe('Data Contracts', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('Contract Status badge should not be visible if Contract Tab is hidden by Person', async ({
|
test('Contract Status badge should be visible on condition if Contract Tab is present/hidden by Persona', async ({
|
||||||
page,
|
page,
|
||||||
}) => {
|
}) => {
|
||||||
test.slow(true);
|
test.slow(true);
|
||||||
@ -549,7 +546,7 @@ test.describe('Data Contracts', () => {
|
|||||||
await test.step(
|
await test.step(
|
||||||
'Create Data Contract in Table and validate it fails',
|
'Create Data Contract in Table and validate it fails',
|
||||||
async () => {
|
async () => {
|
||||||
await table2.visitEntityPage(page);
|
await table.visitEntityPage(page);
|
||||||
|
|
||||||
// Open contract section and start adding contract
|
// Open contract section and start adding contract
|
||||||
await page.click('[data-testid="contract"]');
|
await page.click('[data-testid="contract"]');
|
||||||
@ -679,6 +676,35 @@ test.describe('Data Contracts', () => {
|
|||||||
await personaResponse;
|
await personaResponse;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await test.step(
|
||||||
|
'Verify Contract tab and status badge are visible if persona is set',
|
||||||
|
async () => {
|
||||||
|
await redirectToHomePage(page);
|
||||||
|
await table.visitEntityPage(page);
|
||||||
|
await page.waitForLoadState('networkidle');
|
||||||
|
await page.waitForSelector('[data-testid="loader"]', {
|
||||||
|
state: 'detached',
|
||||||
|
});
|
||||||
|
|
||||||
|
// Verify Contract tab is not visible (should be hidden by persona customization)
|
||||||
|
await expect(page.getByTestId('contract')).toBeVisible();
|
||||||
|
|
||||||
|
// Verify Contract status badge is not visible in header
|
||||||
|
await expect(
|
||||||
|
page.getByTestId('data-contract-latest-result-btn')
|
||||||
|
).toBeVisible();
|
||||||
|
|
||||||
|
// Additional verification: Check that other tabs are still visible
|
||||||
|
await expect(page.getByTestId('schema')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('activity_feed')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('sample_data')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('table_queries')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('profiler')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('lineage')).toBeVisible();
|
||||||
|
await expect(page.getByTestId('custom_properties')).toBeVisible();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
await test.step('Customize Table page to hide Contract tab', async () => {
|
await test.step('Customize Table page to hide Contract tab', async () => {
|
||||||
await settingClick(page, GlobalSettingOptions.PERSONA);
|
await settingClick(page, GlobalSettingOptions.PERSONA);
|
||||||
await page.waitForLoadState('networkidle');
|
await page.waitForLoadState('networkidle');
|
||||||
|
@ -429,14 +429,14 @@ export const DataAssetsHeader = ({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
const dataContractLatestResultButton = useMemo(() => {
|
const dataContractLatestResultButton = useMemo(() => {
|
||||||
const entityContainContractTab =
|
const entityContainContractTabVisible =
|
||||||
isUndefined(customizedPage?.tabs) ??
|
isUndefined(customizedPage?.tabs) ||
|
||||||
Boolean(
|
Boolean(
|
||||||
customizedPage?.tabs?.find((item) => item.id === EntityTabs.CONTRACT)
|
customizedPage?.tabs?.find((item) => item.id === EntityTabs.CONTRACT)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
entityContainContractTab &&
|
entityContainContractTabVisible &&
|
||||||
dataContract?.latestResult?.status &&
|
dataContract?.latestResult?.status &&
|
||||||
[
|
[
|
||||||
ContractExecutionStatus.Aborted,
|
ContractExecutionStatus.Aborted,
|
||||||
|
@ -12,21 +12,26 @@
|
|||||||
*/
|
*/
|
||||||
import { act, fireEvent, render, screen } from '@testing-library/react';
|
import { act, fireEvent, render, screen } from '@testing-library/react';
|
||||||
import { AUTO_PILOT_APP_NAME } from '../../../constants/Applications.constant';
|
import { AUTO_PILOT_APP_NAME } from '../../../constants/Applications.constant';
|
||||||
import { EntityType } from '../../../enums/entity.enum';
|
import { EntityTabs, EntityType } from '../../../enums/entity.enum';
|
||||||
import { ServiceCategory } from '../../../enums/service.enum';
|
import { ServiceCategory } from '../../../enums/service.enum';
|
||||||
import {
|
import {
|
||||||
Container,
|
Container,
|
||||||
StorageServiceType,
|
StorageServiceType,
|
||||||
} from '../../../generated/entity/data/container';
|
} from '../../../generated/entity/data/container';
|
||||||
|
import { ContractExecutionStatus } from '../../../generated/entity/data/dataContract';
|
||||||
import { DatabaseServiceType } from '../../../generated/entity/services/databaseService';
|
import { DatabaseServiceType } from '../../../generated/entity/services/databaseService';
|
||||||
import { LabelType, State, TagSource } from '../../../generated/tests/testCase';
|
import { LabelType, State, TagSource } from '../../../generated/tests/testCase';
|
||||||
import { AssetCertification } from '../../../generated/type/assetCertification';
|
import { AssetCertification } from '../../../generated/type/assetCertification';
|
||||||
|
import { useCustomPages } from '../../../hooks/useCustomPages';
|
||||||
|
import { MOCK_DATA_CONTRACT } from '../../../mocks/DataContract.mock';
|
||||||
import { MOCK_TIER_DATA } from '../../../mocks/TableData.mock';
|
import { MOCK_TIER_DATA } from '../../../mocks/TableData.mock';
|
||||||
import { triggerOnDemandApp } from '../../../rest/applicationAPI';
|
import { triggerOnDemandApp } from '../../../rest/applicationAPI';
|
||||||
import { getDataQualityLineage } from '../../../rest/lineageAPI';
|
import { getDataQualityLineage } from '../../../rest/lineageAPI';
|
||||||
import { getContainerByName } from '../../../rest/storageAPI';
|
import { getContainerByName } from '../../../rest/storageAPI';
|
||||||
import { ExtraInfoLink } from '../../../utils/DataAssetsHeader.utils';
|
import { ExtraInfoLink } from '../../../utils/DataAssetsHeader.utils';
|
||||||
|
import { getDataContractStatusIcon } from '../../../utils/DataContract/DataContractUtils';
|
||||||
import { DEFAULT_ENTITY_PERMISSION } from '../../../utils/PermissionsUtils';
|
import { DEFAULT_ENTITY_PERMISSION } from '../../../utils/PermissionsUtils';
|
||||||
|
import { getEntityDetailsPath } from '../../../utils/RouterUtils';
|
||||||
import { useRequiredParams } from '../../../utils/useRequiredParams';
|
import { useRequiredParams } from '../../../utils/useRequiredParams';
|
||||||
import { DataAssetsHeader } from './DataAssetsHeader.component';
|
import { DataAssetsHeader } from './DataAssetsHeader.component';
|
||||||
import { DataAssetsHeaderProps } from './DataAssetsHeader.interface';
|
import { DataAssetsHeaderProps } from './DataAssetsHeader.interface';
|
||||||
@ -58,9 +63,11 @@ const mockProps: DataAssetsHeaderProps = {
|
|||||||
onOwnerUpdate: jest.fn(),
|
onOwnerUpdate: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mockNavigate = jest.fn();
|
||||||
|
|
||||||
jest.mock('react-router-dom', () => ({
|
jest.mock('react-router-dom', () => ({
|
||||||
...jest.requireActual('react-router-dom'),
|
...jest.requireActual('react-router-dom'),
|
||||||
useNavigate: jest.fn().mockReturnValue(jest.fn()),
|
useNavigate: jest.fn().mockImplementation(() => mockNavigate),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
jest.mock('../../../utils/useRequiredParams', () => ({
|
jest.mock('../../../utils/useRequiredParams', () => ({
|
||||||
@ -179,6 +186,18 @@ jest.mock('../../../rest/lineageAPI', () => ({
|
|||||||
getDataQualityLineage: jest.fn(),
|
getDataQualityLineage: jest.fn(),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../utils/DataContract/DataContractUtils', () => ({
|
||||||
|
getDataContractStatusIcon: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../hooks/useCustomPages', () => ({
|
||||||
|
useCustomPages: jest.fn().mockReturnValue({ customizedPage: null }),
|
||||||
|
}));
|
||||||
|
|
||||||
|
jest.mock('../../../utils/RouterUtils', () => ({
|
||||||
|
getEntityDetailsPath: jest.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
describe('ExtraInfoLink component', () => {
|
describe('ExtraInfoLink component', () => {
|
||||||
const mockProps = {
|
const mockProps = {
|
||||||
label: 'myLabel',
|
label: 'myLabel',
|
||||||
@ -475,4 +494,220 @@ describe('DataAssetsHeader component', () => {
|
|||||||
|
|
||||||
expect(button).toBeEnabled();
|
expect(button).toBeEnabled();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('dataContractLatestResultButton', () => {
|
||||||
|
const mockGetDataContractStatusIcon =
|
||||||
|
getDataContractStatusIcon as jest.Mock;
|
||||||
|
const mockUseCustomPages = useCustomPages as jest.Mock;
|
||||||
|
const mockGetEntityDetailsPath = getEntityDetailsPath as jest.Mock;
|
||||||
|
|
||||||
|
it('should render data contract button when contract tab is visible and status is in allowed list', () => {
|
||||||
|
mockUseCustomPages.mockReturnValue({
|
||||||
|
customizedPage: {
|
||||||
|
tabs: [{ id: EntityTabs.CONTRACT }],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<DataAssetsHeader {...mockProps} dataContract={MOCK_DATA_CONTRACT} />
|
||||||
|
);
|
||||||
|
|
||||||
|
const button = screen.getByTestId('data-contract-latest-result-btn');
|
||||||
|
|
||||||
|
expect(button).toBeInTheDocument();
|
||||||
|
expect(button).toHaveClass('data-contract-latest-result-button');
|
||||||
|
expect(button).toHaveClass('failed');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render data contract button when customizedPage tabs is undefined', () => {
|
||||||
|
mockUseCustomPages.mockReturnValue({
|
||||||
|
customizedPage: {
|
||||||
|
tabs: undefined,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockDataContract = MOCK_DATA_CONTRACT;
|
||||||
|
|
||||||
|
render(
|
||||||
|
<DataAssetsHeader {...mockProps} dataContract={mockDataContract} />
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.getByTestId('data-contract-latest-result-btn')
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should navigate to contract tab when button is clicked', () => {
|
||||||
|
mockUseCustomPages.mockReturnValue({
|
||||||
|
customizedPage: {
|
||||||
|
tabs: [{ id: EntityTabs.CONTRACT }],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockDataContract = {
|
||||||
|
...MOCK_DATA_CONTRACT,
|
||||||
|
latestResult: {
|
||||||
|
status: ContractExecutionStatus.Running,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
render(
|
||||||
|
<DataAssetsHeader {...mockProps} dataContract={mockDataContract} />
|
||||||
|
);
|
||||||
|
|
||||||
|
const button = screen.getByTestId('data-contract-latest-result-btn');
|
||||||
|
fireEvent.click(button);
|
||||||
|
|
||||||
|
expect(mockGetEntityDetailsPath).toHaveBeenCalledWith(
|
||||||
|
EntityType.CONTAINER,
|
||||||
|
'fullyQualifiedName',
|
||||||
|
EntityTabs.CONTRACT
|
||||||
|
);
|
||||||
|
expect(mockNavigate).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render data contract button when contract tab is not visible', () => {
|
||||||
|
mockUseCustomPages.mockReturnValue({
|
||||||
|
customizedPage: {
|
||||||
|
tabs: [{ id: EntityTabs.ACTIVITY_FEED }],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
render(
|
||||||
|
<DataAssetsHeader {...mockProps} dataContract={MOCK_DATA_CONTRACT} />
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.queryByTestId('data-contract-latest-result-btn')
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render data contract button when status is not in allowed list', () => {
|
||||||
|
mockUseCustomPages.mockReturnValue({
|
||||||
|
customizedPage: {
|
||||||
|
tabs: [{ id: EntityTabs.CONTRACT }],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockDataContract = {
|
||||||
|
...MOCK_DATA_CONTRACT,
|
||||||
|
latestResult: {
|
||||||
|
status: ContractExecutionStatus.Success,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
render(
|
||||||
|
<DataAssetsHeader {...mockProps} dataContract={mockDataContract} />
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.queryByTestId('data-contract-latest-result-btn')
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render data contract button when dataContract is undefined', () => {
|
||||||
|
mockUseCustomPages.mockReturnValue({
|
||||||
|
customizedPage: {
|
||||||
|
tabs: [{ id: EntityTabs.CONTRACT }],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<DataAssetsHeader {...mockProps} dataContract={undefined} />);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.queryByTestId('data-contract-latest-result-btn')
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not render data contract button when latestResult is undefined', () => {
|
||||||
|
mockUseCustomPages.mockReturnValue({
|
||||||
|
customizedPage: {
|
||||||
|
tabs: [{ id: EntityTabs.CONTRACT }],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockDataContract = {
|
||||||
|
...MOCK_DATA_CONTRACT,
|
||||||
|
latestResult: undefined,
|
||||||
|
};
|
||||||
|
|
||||||
|
render(
|
||||||
|
<DataAssetsHeader {...mockProps} dataContract={mockDataContract} />
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
screen.queryByTestId('data-contract-latest-result-btn')
|
||||||
|
).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render button with correct class names for each status', () => {
|
||||||
|
mockUseCustomPages.mockReturnValue({
|
||||||
|
customizedPage: { tabs: [{ id: EntityTabs.CONTRACT }] },
|
||||||
|
});
|
||||||
|
|
||||||
|
const statuses = [
|
||||||
|
ContractExecutionStatus.Failed,
|
||||||
|
ContractExecutionStatus.Aborted,
|
||||||
|
ContractExecutionStatus.Running,
|
||||||
|
];
|
||||||
|
|
||||||
|
statuses.forEach((status) => {
|
||||||
|
const { unmount } = render(
|
||||||
|
<DataAssetsHeader
|
||||||
|
{...mockProps}
|
||||||
|
dataContract={{ ...MOCK_DATA_CONTRACT, latestResult: { status } }}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const button = screen.getByTestId('data-contract-latest-result-btn');
|
||||||
|
|
||||||
|
expect(button).toHaveClass(`data-contract-latest-result-button`);
|
||||||
|
expect(button).toHaveClass(status.toLowerCase());
|
||||||
|
|
||||||
|
unmount();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render button with icon when getDataContractStatusIcon returns an icon', () => {
|
||||||
|
mockUseCustomPages.mockReturnValue({
|
||||||
|
customizedPage: { tabs: [{ id: EntityTabs.CONTRACT }] },
|
||||||
|
});
|
||||||
|
mockGetDataContractStatusIcon.mockReturnValue('TestIcon');
|
||||||
|
|
||||||
|
render(
|
||||||
|
<DataAssetsHeader
|
||||||
|
{...mockProps}
|
||||||
|
dataContract={{
|
||||||
|
...MOCK_DATA_CONTRACT,
|
||||||
|
latestResult: { status: ContractExecutionStatus.Failed },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const button = screen.getByTestId('data-contract-latest-result-btn');
|
||||||
|
|
||||||
|
expect(button.querySelector('.anticon')).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should render button without icon when getDataContractStatusIcon returns null', () => {
|
||||||
|
mockUseCustomPages.mockReturnValue({
|
||||||
|
customizedPage: { tabs: [{ id: EntityTabs.CONTRACT }] },
|
||||||
|
});
|
||||||
|
mockGetDataContractStatusIcon.mockReturnValue(null);
|
||||||
|
|
||||||
|
render(
|
||||||
|
<DataAssetsHeader
|
||||||
|
{...mockProps}
|
||||||
|
dataContract={{
|
||||||
|
...MOCK_DATA_CONTRACT,
|
||||||
|
latestResult: { status: ContractExecutionStatus.Failed },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
|
const button = screen.getByTestId('data-contract-latest-result-btn');
|
||||||
|
|
||||||
|
expect(button.querySelector('.anticon')).not.toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -0,0 +1,125 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2025 Collate.
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
import {
|
||||||
|
Constraint,
|
||||||
|
DataType,
|
||||||
|
LabelType,
|
||||||
|
State,
|
||||||
|
} from '../generated/entity/data/table';
|
||||||
|
import { ContractExecutionStatus } from '../generated/type/contractExecutionStatus';
|
||||||
|
import { TagSource } from '../generated/type/tagLabel';
|
||||||
|
|
||||||
|
export const MOCK_DATA_CONTRACT = {
|
||||||
|
id: 'bf7b3a0d-6a85-4dd6-95e3-243a1769d1a9',
|
||||||
|
name: 'Customer 360',
|
||||||
|
fullyQualifiedName:
|
||||||
|
'redshift prod.dev.dbt_jaffle.customers.dataContract_Customer 360',
|
||||||
|
description: '<strong>Customer 360 Data Contract</strong> ',
|
||||||
|
version: 0.2,
|
||||||
|
updatedAt: 1755860378527,
|
||||||
|
updatedBy: 'joseph',
|
||||||
|
status: 'Active',
|
||||||
|
entity: {
|
||||||
|
id: 'ee9d44a0-815d-4ac9-8422-4f9d02ddf04d',
|
||||||
|
type: 'table',
|
||||||
|
href: 'https://demo.getcollate.io/v1/tables/ee9d44a0-815d-4ac9-8422-4f9d02ddf04d',
|
||||||
|
},
|
||||||
|
testSuite: {
|
||||||
|
id: '24859b7c-a2ef-4e0e-b3b7-67a61ed14bc9',
|
||||||
|
type: 'testSuite',
|
||||||
|
fullyQualifiedName: 'bf7b3a0d-6a85-4dd6-95e3-243a1769d1a9',
|
||||||
|
},
|
||||||
|
schema: [
|
||||||
|
{
|
||||||
|
name: 'customer_id',
|
||||||
|
dataType: DataType.Array,
|
||||||
|
dataLength: 1,
|
||||||
|
dataTypeDisplay: 'integer',
|
||||||
|
description: 'Unique identifier for each customer',
|
||||||
|
fullyQualifiedName: 'redshift prod.dev.dbt_jaffle.customers.customer_id',
|
||||||
|
tags: [
|
||||||
|
{
|
||||||
|
tagFQN: 'PII.NonSensitive',
|
||||||
|
name: 'NonSensitive',
|
||||||
|
displayName: 'Non Sensitive ',
|
||||||
|
description:
|
||||||
|
'PII which is easily accessible from public sources and can include zip code, race, gender, and date of birth.',
|
||||||
|
style: {},
|
||||||
|
source: TagSource.Classification,
|
||||||
|
labelType: LabelType.Automated,
|
||||||
|
state: State.Suggested,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
constraint: Constraint.Null,
|
||||||
|
children: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
semantics: [
|
||||||
|
{
|
||||||
|
name: 'Tiering Elements',
|
||||||
|
description: '',
|
||||||
|
// eslint-disable-next-line max-len
|
||||||
|
rule: '{"and":[{"some":[{"var":"owners"},{"==":[{"var":"fullyQualifiedName"},"customer team"]}]},{"some":[{"var":"dataProduct"},{"==":[{"var":"fullyQualifiedName"},"C360"]}]},{"==":[{"var":"tier.tagFQN"},"Tier.Tier1"]}]}',
|
||||||
|
enabled: true,
|
||||||
|
ignoredEntities: [],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
qualityExpectations: [
|
||||||
|
{
|
||||||
|
id: 'f496f0d9-58c3-4a0e-a836-2479b457c68e',
|
||||||
|
type: 'testCase',
|
||||||
|
name: 'CLV Must be Positive',
|
||||||
|
description:
|
||||||
|
'<p>The customer lifetime value must always be greater or equal to zero </p>',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'e57ffd73-b8f1-4f5f-91b1-7ebe614dc26e',
|
||||||
|
type: 'testCase',
|
||||||
|
name: 'Customer ID To Be Unique',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '484e016e-ed87-4f6c-ae77-7d5b16f07ad0',
|
||||||
|
type: 'testCase',
|
||||||
|
name: 'Table Row Count To Equal',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
reviewers: [],
|
||||||
|
changeDescription: {
|
||||||
|
fieldsAdded: [
|
||||||
|
{
|
||||||
|
name: 'latestResult',
|
||||||
|
newValue: {
|
||||||
|
status: ContractExecutionStatus.Failed,
|
||||||
|
resultId: '04551202-c2b9-4d7b-aecf-6c9c8fe22c1c',
|
||||||
|
timestamp: 1756944000095,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
fieldsUpdated: [],
|
||||||
|
fieldsDeleted: [],
|
||||||
|
previousVersion: 0.1,
|
||||||
|
changeSummary: {},
|
||||||
|
},
|
||||||
|
incrementalChangeDescription: {
|
||||||
|
fieldsAdded: [],
|
||||||
|
fieldsUpdated: [],
|
||||||
|
fieldsDeleted: [],
|
||||||
|
previousVersion: 0.2,
|
||||||
|
},
|
||||||
|
deleted: false,
|
||||||
|
latestResult: {
|
||||||
|
timestamp: 1756944000095,
|
||||||
|
status: ContractExecutionStatus.Failed,
|
||||||
|
resultId: '04551202-c2b9-4d7b-aecf-6c9c8fe22c1c',
|
||||||
|
},
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user