diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/GmsGraphQLEngine.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/GmsGraphQLEngine.java index 077230ba298492..f74b580b2fa80b 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/GmsGraphQLEngine.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/GmsGraphQLEngine.java @@ -143,9 +143,9 @@ import com.linkedin.datahub.graphql.resolvers.mutate.RemoveTagResolver; import com.linkedin.datahub.graphql.resolvers.mutate.RemoveTermResolver; import com.linkedin.datahub.graphql.resolvers.mutate.UpdateDescriptionResolver; -import com.linkedin.datahub.graphql.resolvers.operation.ReportOperationResolver; import com.linkedin.datahub.graphql.resolvers.mutate.UpdateNameResolver; import com.linkedin.datahub.graphql.resolvers.mutate.UpdateParentNodeResolver; +import com.linkedin.datahub.graphql.resolvers.operation.ReportOperationResolver; import com.linkedin.datahub.graphql.resolvers.policy.DeletePolicyResolver; import com.linkedin.datahub.graphql.resolvers.policy.GetGrantedPrivilegesResolver; import com.linkedin.datahub.graphql.resolvers.policy.ListPoliciesResolver; @@ -165,6 +165,7 @@ import com.linkedin.datahub.graphql.resolvers.test.TestResultsResolver; import com.linkedin.datahub.graphql.resolvers.test.UpdateTestResolver; import com.linkedin.datahub.graphql.resolvers.timeline.GetSchemaBlameResolver; +import com.linkedin.datahub.graphql.resolvers.timeline.GetSchemaVersionListResolver; import com.linkedin.datahub.graphql.resolvers.type.AspectInterfaceTypeResolver; import com.linkedin.datahub.graphql.resolvers.type.EntityInterfaceTypeResolver; import com.linkedin.datahub.graphql.resolvers.type.HyperParameterValueTypeResolver; @@ -214,8 +215,8 @@ import com.linkedin.entity.client.EntityClient; import com.linkedin.metadata.config.DatahubConfiguration; import com.linkedin.metadata.config.IngestionConfiguration; -import com.linkedin.metadata.config.VisualConfiguration; import com.linkedin.metadata.config.TestsConfiguration; +import com.linkedin.metadata.config.VisualConfiguration; import com.linkedin.metadata.entity.EntityService; import com.linkedin.metadata.graph.GraphClient; import com.linkedin.metadata.graph.SiblingGraphService; @@ -232,12 +233,6 @@ import graphql.schema.DataFetchingEnvironment; import graphql.schema.StaticDataFetcher; import graphql.schema.idl.RuntimeWiring; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.io.IOUtils; -import org.dataloader.BatchLoaderContextProvider; -import org.dataloader.DataLoader; -import org.dataloader.DataLoaderOptions; - import java.io.IOException; import java.io.InputStream; import java.nio.charset.StandardCharsets; @@ -250,10 +245,15 @@ import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.IOUtils; +import org.dataloader.BatchLoaderContextProvider; +import org.dataloader.DataLoader; +import org.dataloader.DataLoaderOptions; import static com.linkedin.datahub.graphql.Constants.*; -import static com.linkedin.metadata.Constants.DATA_PROCESS_INSTANCE_RUN_EVENT_ASPECT_NAME; -import static graphql.Scalars.GraphQLLong; +import static com.linkedin.metadata.Constants.*; +import static graphql.Scalars.*; /** @@ -635,6 +635,7 @@ private void configureQueryResolvers(final RuntimeWiring.Builder builder) { .dataFetcher("ingestionSource", new GetIngestionSourceResolver(this.entityClient)) .dataFetcher("executionRequest", new GetIngestionExecutionRequestResolver(this.entityClient)) .dataFetcher("getSchemaBlame", new GetSchemaBlameResolver(this.timelineService)) + .dataFetcher("getSchemaVersionList", new GetSchemaVersionListResolver(this.timelineService)) .dataFetcher("test", getResolver(testType)) .dataFetcher("listTests", new ListTestsResolver(entityClient)) .dataFetcher("getRootGlossaryTerms", new GetRootGlossaryTermsResolver(this.entityClient)) diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/timeline/GetSchemaBlameResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/timeline/GetSchemaBlameResolver.java index 005c60fcd32f97..5690a4d1a9e027 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/timeline/GetSchemaBlameResolver.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/timeline/GetSchemaBlameResolver.java @@ -3,7 +3,7 @@ import com.linkedin.common.urn.Urn; import com.linkedin.datahub.graphql.generated.GetSchemaBlameInput; import com.linkedin.datahub.graphql.generated.GetSchemaBlameResult; -import com.linkedin.datahub.graphql.types.timeline.mappers.SchemaFieldBlameMapper; +import com.linkedin.datahub.graphql.types.timeline.mappers.SchemaBlameMapper; import com.linkedin.metadata.timeline.TimelineService; import com.linkedin.metadata.timeline.data.ChangeCategory; import com.linkedin.metadata.timeline.data.ChangeTransaction; @@ -46,7 +46,7 @@ public CompletableFuture get(final DataFetchingEnvironment Urn datasetUrn = Urn.createFromString(datasetUrnString); List changeTransactionList = _timelineService.getTimeline(datasetUrn, changeCategorySet, startTime, endTime, null, null, false); - return SchemaFieldBlameMapper.map(changeTransactionList, version); + return SchemaBlameMapper.map(changeTransactionList, version); } catch (URISyntaxException u) { log.error( String.format("Failed to list schema blame data, likely due to the Urn %s being invalid", datasetUrnString), diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/timeline/GetSchemaVersionListResolver.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/timeline/GetSchemaVersionListResolver.java new file mode 100644 index 00000000000000..cfad1395a61a88 --- /dev/null +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/resolvers/timeline/GetSchemaVersionListResolver.java @@ -0,0 +1,61 @@ +package com.linkedin.datahub.graphql.resolvers.timeline; + +import com.linkedin.common.urn.Urn; +import com.linkedin.datahub.graphql.generated.GetSchemaVersionListInput; +import com.linkedin.datahub.graphql.generated.GetSchemaVersionListResult; +import com.linkedin.datahub.graphql.types.timeline.mappers.SchemaVersionListMapper; +import com.linkedin.metadata.timeline.TimelineService; +import com.linkedin.metadata.timeline.data.ChangeCategory; +import com.linkedin.metadata.timeline.data.ChangeTransaction; +import graphql.schema.DataFetcher; +import graphql.schema.DataFetchingEnvironment; +import java.net.URISyntaxException; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.CompletableFuture; +import lombok.extern.slf4j.Slf4j; + +import static com.linkedin.datahub.graphql.resolvers.ResolverUtils.*; + + +/* +Returns the most recent changes made to each column in a dataset at each dataset version. + */ +@Slf4j +public class GetSchemaVersionListResolver implements DataFetcher> { + private final TimelineService _timelineService; + + public GetSchemaVersionListResolver(TimelineService timelineService) { + _timelineService = timelineService; + } + + @Override + public CompletableFuture get(final DataFetchingEnvironment environment) throws Exception { + final GetSchemaVersionListInput input = + bindArgument(environment.getArgument("input"), GetSchemaVersionListInput.class); + + final String datasetUrnString = input.getDatasetUrn(); + final long startTime = 0; + final long endTime = 0; + + return CompletableFuture.supplyAsync(() -> { + try { + final Set changeCategorySet = new HashSet<>(); + changeCategorySet.add(ChangeCategory.TECHNICAL_SCHEMA); + Urn datasetUrn = Urn.createFromString(datasetUrnString); + List changeTransactionList = + _timelineService.getTimeline(datasetUrn, changeCategorySet, startTime, endTime, null, null, false); + return SchemaVersionListMapper.map(changeTransactionList); + } catch (URISyntaxException u) { + log.error( + String.format("Failed to list schema blame data, likely due to the Urn %s being invalid", datasetUrnString), + u); + return null; + } catch (Exception e) { + log.error("Failed to list schema blame data", e); + return null; + } + }); + } +} \ No newline at end of file diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/timeline/mappers/SchemaFieldBlameMapper.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/timeline/mappers/SchemaBlameMapper.java similarity index 55% rename from datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/timeline/mappers/SchemaFieldBlameMapper.java rename to datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/timeline/mappers/SchemaBlameMapper.java index e7252b8a6369b4..9f51e0ae6fa7e8 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/timeline/mappers/SchemaFieldBlameMapper.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/timeline/mappers/SchemaBlameMapper.java @@ -6,13 +6,13 @@ import com.linkedin.datahub.graphql.generated.SchemaFieldBlame; import com.linkedin.datahub.graphql.generated.SchemaFieldChange; import com.linkedin.datahub.graphql.generated.SemanticVersionStruct; +import com.linkedin.datahub.graphql.types.timeline.utils.TimelineUtils; import com.linkedin.metadata.key.SchemaFieldKey; import com.linkedin.metadata.timeline.data.ChangeCategory; import com.linkedin.metadata.timeline.data.ChangeEvent; import com.linkedin.metadata.timeline.data.ChangeTransaction; import com.linkedin.metadata.utils.EntityKeyUtils; import com.linkedin.util.Pair; -import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -24,14 +24,17 @@ import lombok.extern.slf4j.Slf4j; import org.apache.parquet.SemanticVersion; +import static com.linkedin.datahub.graphql.types.timeline.utils.TimelineUtils.*; + // Class for converting ChangeTransactions received from the Timeline API to SchemaFieldBlame structs for every schema // at every semantic version. @Slf4j -public class SchemaFieldBlameMapper { +public class SchemaBlameMapper { public static GetSchemaBlameResult map(List changeTransactions, @Nullable String versionCutoff) { if (changeTransactions.isEmpty()) { + log.debug("Change transactions are empty"); return null; } @@ -40,10 +43,6 @@ public static GetSchemaBlameResult map(List changeTransaction String latestSemanticVersionString = truncateSemanticVersion(changeTransactions.get(changeTransactions.size() - 1).getSemVer()); - long latestSemanticVersionTimestamp = changeTransactions.get(changeTransactions.size() - 1).getTimestamp(); - String latestVersionStamp = changeTransactions.get(changeTransactions.size() - 1).getVersionStamp(); - result.setLatestVersion( - new SemanticVersionStruct(latestSemanticVersionString, latestSemanticVersionTimestamp, latestVersionStamp)); String semanticVersionFilterString = versionCutoff == null ? latestSemanticVersionString : versionCutoff; Optional semanticVersionFilterOptional = createSemanticVersion(semanticVersionFilterString); @@ -54,7 +53,7 @@ public static GetSchemaBlameResult map(List changeTransaction SemanticVersion semanticVersionFilter = semanticVersionFilterOptional.get(); List reversedChangeTransactions = changeTransactions.stream() - .map(SchemaFieldBlameMapper::semanticVersionChangeTransactionPair) + .map(TimelineUtils::semanticVersionChangeTransactionPair) .filter(Optional::isPresent) .map(Optional::get) .filter(semanticVersionChangeTransactionPair -> @@ -69,13 +68,7 @@ public static GetSchemaBlameResult map(List changeTransaction result.setVersion( new SemanticVersionStruct(selectedSemanticVersion, selectedSemanticVersionTimestamp, selectedVersionStamp)); - List semanticVersionStructList = new ArrayList<>(); for (ChangeTransaction changeTransaction : reversedChangeTransactions) { - SemanticVersionStruct semanticVersionStruct = - new SemanticVersionStruct(truncateSemanticVersion(changeTransaction.getSemVer()), - changeTransaction.getTimestamp(), changeTransaction.getVersionStamp()); - semanticVersionStructList.add(semanticVersionStruct); - for (ChangeEvent changeEvent : changeTransaction.getChangeEvents()) { if (changeEvent.getCategory() != ChangeCategory.TECHNICAL_SCHEMA) { continue; @@ -115,71 +108,9 @@ public static GetSchemaBlameResult map(List changeTransaction .getChangeType() .equals(ChangeOperationType.REMOVE)) .collect(Collectors.toList())); - result.setSemanticVersionList(semanticVersionStructList); return result; } - private static Optional> semanticVersionChangeTransactionPair( - ChangeTransaction changeTransaction) { - Optional semanticVersion = createSemanticVersion(changeTransaction.getSemVer()); - return semanticVersion.map(version -> Pair.of(version, changeTransaction)); - } - - private static Optional createSemanticVersion(String semanticVersionString) { - String truncatedSemanticVersion = truncateSemanticVersion(semanticVersionString); - try { - SemanticVersion semanticVersion = SemanticVersion.parse(truncatedSemanticVersion); - return Optional.of(semanticVersion); - } catch (SemanticVersion.SemanticVersionParseException e) { - return Optional.empty(); - } - } - - // The SemanticVersion is currently returned from the ChangeTransactions in the format "x.y.z-computed". This function - // removes the suffix "computed". - private static String truncateSemanticVersion(String semanticVersion) { - String suffix = "-computed"; - return semanticVersion.endsWith(suffix) ? semanticVersion.substring(0, semanticVersion.lastIndexOf(suffix)) - : semanticVersion; - } - - private static SchemaFieldChange getLastSchemaFieldChange(ChangeEvent changeEvent, long timestamp, - String semanticVersion, String versionStamp) { - SchemaFieldChange schemaFieldChange = new SchemaFieldChange(); - schemaFieldChange.setTimestampMillis(timestamp); - schemaFieldChange.setLastSemanticVersion(truncateSemanticVersion(semanticVersion)); - schemaFieldChange.setChangeType( - ChangeOperationType.valueOf(ChangeOperationType.class, changeEvent.getOperation().toString())); - schemaFieldChange.setVersionStamp(versionStamp); - - String translatedChangeOperationType; - switch (changeEvent.getOperation()) { - case ADD: - translatedChangeOperationType = "Added"; - break; - case MODIFY: - translatedChangeOperationType = "Modified"; - break; - case REMOVE: - translatedChangeOperationType = "Removed"; - break; - default: - translatedChangeOperationType = "Unknown change made"; - log.warn(translatedChangeOperationType); - break; - } - - String suffix = "-computed"; - String translatedSemanticVersion = - semanticVersion.endsWith(suffix) ? semanticVersion.substring(0, semanticVersion.lastIndexOf(suffix)) - : semanticVersion; - - String lastSchemaFieldChange = String.format("%s in v%s", translatedChangeOperationType, translatedSemanticVersion); - schemaFieldChange.setLastSchemaFieldChange(lastSchemaFieldChange); - - return schemaFieldChange; - } - - private SchemaFieldBlameMapper() { + private SchemaBlameMapper() { } } \ No newline at end of file diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/timeline/mappers/SchemaVersionListMapper.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/timeline/mappers/SchemaVersionListMapper.java new file mode 100644 index 00000000000000..249957b1a12621 --- /dev/null +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/timeline/mappers/SchemaVersionListMapper.java @@ -0,0 +1,56 @@ +package com.linkedin.datahub.graphql.types.timeline.mappers; + +import com.linkedin.datahub.graphql.generated.GetSchemaVersionListResult; +import com.linkedin.datahub.graphql.generated.SemanticVersionStruct; +import com.linkedin.datahub.graphql.types.timeline.utils.TimelineUtils; +import com.linkedin.metadata.timeline.data.ChangeTransaction; +import com.linkedin.util.Pair; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; +import lombok.extern.slf4j.Slf4j; + +import static com.linkedin.datahub.graphql.types.timeline.utils.TimelineUtils.*; + + +// Class for converting ChangeTransactions received from the Timeline API to list of schema versions. +@Slf4j +public class SchemaVersionListMapper { + + public static GetSchemaVersionListResult map(List changeTransactions) { + if (changeTransactions.isEmpty()) { + log.debug("Change transactions are empty"); + return null; + } + + GetSchemaVersionListResult result = new GetSchemaVersionListResult(); + + String latestSemanticVersionString = + truncateSemanticVersion(changeTransactions.get(changeTransactions.size() - 1).getSemVer()); + long latestSemanticVersionTimestamp = changeTransactions.get(changeTransactions.size() - 1).getTimestamp(); + String latestVersionStamp = changeTransactions.get(changeTransactions.size() - 1).getVersionStamp(); + result.setLatestVersion( + new SemanticVersionStruct(latestSemanticVersionString, latestSemanticVersionTimestamp, latestVersionStamp)); + + List reversedChangeTransactions = changeTransactions.stream() + .map(TimelineUtils::semanticVersionChangeTransactionPair) + .filter(Optional::isPresent) + .map(Optional::get) + .sorted(Collections.reverseOrder(Comparator.comparing(Pair::getFirst))) + .map(Pair::getSecond) + .collect(Collectors.toList()); + + List semanticVersionStructList = reversedChangeTransactions.stream() + .map(changeTransaction -> new SemanticVersionStruct(truncateSemanticVersion(changeTransaction.getSemVer()), + changeTransaction.getTimestamp(), changeTransaction.getVersionStamp())) + .collect(Collectors.toList()); + + result.setSemanticVersionList(semanticVersionStructList); + return result; + } + + private SchemaVersionListMapper() { + } +} \ No newline at end of file diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/timeline/utils/TimelineUtils.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/timeline/utils/TimelineUtils.java new file mode 100644 index 00000000000000..fcd77628045386 --- /dev/null +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/timeline/utils/TimelineUtils.java @@ -0,0 +1,79 @@ +package com.linkedin.datahub.graphql.types.timeline.utils; + +import com.linkedin.datahub.graphql.generated.ChangeOperationType; +import com.linkedin.datahub.graphql.generated.SchemaFieldChange; +import com.linkedin.metadata.timeline.data.ChangeEvent; +import com.linkedin.metadata.timeline.data.ChangeTransaction; +import com.linkedin.util.Pair; +import java.util.Optional; +import lombok.extern.slf4j.Slf4j; +import org.apache.parquet.SemanticVersion; + + +@Slf4j +public class TimelineUtils { + + public static Optional> semanticVersionChangeTransactionPair( + ChangeTransaction changeTransaction) { + Optional semanticVersion = createSemanticVersion(changeTransaction.getSemVer()); + return semanticVersion.map(version -> Pair.of(version, changeTransaction)); + } + + public static Optional createSemanticVersion(String semanticVersionString) { + String truncatedSemanticVersion = truncateSemanticVersion(semanticVersionString); + try { + SemanticVersion semanticVersion = SemanticVersion.parse(truncatedSemanticVersion); + return Optional.of(semanticVersion); + } catch (SemanticVersion.SemanticVersionParseException e) { + return Optional.empty(); + } + } + + // The SemanticVersion is currently returned from the ChangeTransactions in the format "x.y.z-computed". This function + // removes the suffix "computed". + public static String truncateSemanticVersion(String semanticVersion) { + String suffix = "-computed"; + return semanticVersion.endsWith(suffix) ? semanticVersion.substring(0, semanticVersion.lastIndexOf(suffix)) + : semanticVersion; + } + + public static SchemaFieldChange getLastSchemaFieldChange(ChangeEvent changeEvent, long timestamp, + String semanticVersion, String versionStamp) { + SchemaFieldChange schemaFieldChange = new SchemaFieldChange(); + schemaFieldChange.setTimestampMillis(timestamp); + schemaFieldChange.setLastSemanticVersion(truncateSemanticVersion(semanticVersion)); + schemaFieldChange.setChangeType( + ChangeOperationType.valueOf(ChangeOperationType.class, changeEvent.getOperation().toString())); + schemaFieldChange.setVersionStamp(versionStamp); + + String translatedChangeOperationType; + switch (changeEvent.getOperation()) { + case ADD: + translatedChangeOperationType = "Added"; + break; + case MODIFY: + translatedChangeOperationType = "Modified"; + break; + case REMOVE: + translatedChangeOperationType = "Removed"; + break; + default: + translatedChangeOperationType = "Unknown change made"; + log.warn(translatedChangeOperationType); + break; + } + + String suffix = "-computed"; + String translatedSemanticVersion = + semanticVersion.endsWith(suffix) ? semanticVersion.substring(0, semanticVersion.lastIndexOf(suffix)) + : semanticVersion; + + String lastSchemaFieldChange = String.format("%s in v%s", translatedChangeOperationType, translatedSemanticVersion); + schemaFieldChange.setLastSchemaFieldChange(lastSchemaFieldChange); + + return schemaFieldChange; + } + + private TimelineUtils() { + } +} diff --git a/datahub-graphql-core/src/main/resources/timeline.graphql b/datahub-graphql-core/src/main/resources/timeline.graphql index 7c60cb7a29bede..e1757c315a16db 100644 --- a/datahub-graphql-core/src/main/resources/timeline.graphql +++ b/datahub-graphql-core/src/main/resources/timeline.graphql @@ -3,6 +3,11 @@ extend type Query { Returns the most recent changes made to each column in a dataset at each dataset version. """ getSchemaBlame(input: GetSchemaBlameInput!): GetSchemaBlameResult + + """ + Returns the list of schema versions for a dataset. + """ + getSchemaVersionList(input: GetSchemaVersionListInput!): GetSchemaVersionListResult } """ @@ -50,27 +55,19 @@ enum ChangeCategoryType { } """ -Input for getting schema changes computed at a specific version. +Input for getting list of schema versions. """ -input GetSchemaBlameInput { +input GetSchemaVersionListInput { """ The dataset urn """ datasetUrn: String! - """ - Categories to filter on. By default, will just be TECHNICAL_SCHEMA. - """ - categories: [ChangeCategoryType!] - """ - Changes after this version are not shown. If not provided, this is the latestVersion. - """ - version: String } """ Schema changes computed at a specific version. """ -type GetSchemaBlameResult { +type GetSchemaVersionListResult { """ Latest and current semantic version """ @@ -83,6 +80,31 @@ type GetSchemaBlameResult { All semantic versions. Absent when there are no versions. """ semanticVersionList: [SemanticVersionStruct!] +} + + +""" +Input for getting schema changes computed at a specific version. +""" +input GetSchemaBlameInput { + """ + The dataset urn + """ + datasetUrn: String! + """ + Changes after this version are not shown. If not provided, this is the latestVersion. + """ + version: String +} + +""" +Schema changes computed at a specific version. +""" +type GetSchemaBlameResult { + """ + Selected semantic version + """ + version: SemanticVersionStruct """ List of schema blame. Absent when there are no fields to return history for. """ diff --git a/datahub-web-react/src/app/entity/dataset/profile/schema/components/SchemaHeader.tsx b/datahub-web-react/src/app/entity/dataset/profile/schema/components/SchemaHeader.tsx index fb992d0a0c3418..52e9dbac660f97 100644 --- a/datahub-web-react/src/app/entity/dataset/profile/schema/components/SchemaHeader.tsx +++ b/datahub-web-react/src/app/entity/dataset/profile/schema/components/SchemaHeader.tsx @@ -1,14 +1,19 @@ import React from 'react'; import { useHistory, useLocation } from 'react-router-dom'; -import { Button, Popover, Radio, Select, Typography } from 'antd'; -import { CaretDownOutlined, FileTextOutlined, InfoCircleOutlined, TableOutlined } from '@ant-design/icons'; +import { Button, Popover, Select, Tooltip, Typography } from 'antd'; +import { + AuditOutlined, + CaretDownOutlined, + FileTextOutlined, + QuestionCircleOutlined, + TableOutlined, +} from '@ant-design/icons'; import styled from 'styled-components'; import CustomPagination from './CustomPagination'; import TabToolbar from '../../../../shared/components/styled/TabToolbar'; import { SemanticVersionStruct } from '../../../../../../types.generated'; import { toRelativeTimeString } from '../../../../../shared/time/timeUtils'; -import { SchemaViewType } from '../utils/types'; -import { ANTD_GRAY } from '../../../../shared/constants'; +import { ANTD_GRAY, REDESIGN_COLORS } from '../../../../shared/constants'; import { navigateToVersionedDatasetUrl } from '../../../../shared/tabs/Dataset/Schema/utils/navigateToVersionedDatasetUrl'; import SchemaTimeStamps from './SchemaTimeStamps'; @@ -86,24 +91,16 @@ const SchemaBlameSelectorOption = styled(Select.Option)` } `; -const BlameRadio = styled(Radio.Group)` +const SchemaAuditButton = styled(Button)` &&& { margin-top: 6px; - margin-right: 10px; - min-width: 140px; - } -`; - -const BlameRadioButton = styled(Radio.Button)` - &&& { - min-width: 30px; } `; -const StyledInfoCircleOutlined = styled(InfoCircleOutlined)` +const StyledQuestionCircleOutlined = styled(QuestionCircleOutlined)` &&& { - margin-top: 12px; - font-size: 20px; + margin-top: 14px; + font-size: 16px; color: ${ANTD_GRAY[6]}; } `; @@ -129,8 +126,8 @@ type Props = { lastObserved?: number | null; selectedVersion: string; versionList: Array; - schemaView: SchemaViewType; - setSchemaView: any; + showSchemaAuditView: boolean; + setShowSchemaAuditView: any; }; export default function SchemaHeader({ @@ -148,8 +145,8 @@ export default function SchemaHeader({ lastObserved, selectedVersion, versionList, - schemaView, - setSchemaView, + showSchemaAuditView, + setShowSchemaAuditView, }: Props) { const history = useHistory(); const location = useLocation(); @@ -167,6 +164,7 @@ export default function SchemaHeader({ 'unknown'; return `${semanticVersion.semanticVersion} - ${semanticVersionTimestampString}`; }; + const numVersions = versionList.length; const renderOptions = () => { return versionList.map( @@ -182,10 +180,7 @@ export default function SchemaHeader({ ), ); }; - - const onSchemaViewToggle = (e) => { - setSchemaView(e.target.value); - }; + const schemaAuditToggleText = showSchemaAuditView ? 'Close column history' : 'View column history'; const docLink = 'https://datahubproject.io/docs/dev-guides/timeline/'; return ( @@ -227,45 +222,51 @@ export default function SchemaHeader({ - - - Normal - - - Blame - - - { - const datasetVersion: string = e as string; - navigateToVersionedDatasetUrl({ - location, - history, - datasetVersion, - }); - }} - data-testid="schema-version-selector-dropdown" - suffixIcon={} - > - {renderOptions()} - - - Semantic versions for this view were computed using Technical Schema. You can find more - info about how we compute versions - - {' '} - here.{' '} - - - } - > - - + + setShowSchemaAuditView(!showSchemaAuditView)} + style={{ color: showSchemaAuditView ? REDESIGN_COLORS.BLUE : ANTD_GRAY[7] }} + > + + + + {numVersions > 1 && ( + <> + { + const datasetVersion: string = e as string; + navigateToVersionedDatasetUrl({ + location, + history, + datasetVersion, + }); + }} + data-testid="schema-version-selector-dropdown" + suffixIcon={} + > + {renderOptions()} + + + Semantic versions for this view were computed using Technical Schema. You can + find more info about how DataHub computes versions + + {' '} + here.{' '} + + + } + > + + + + )} diff --git a/datahub-web-react/src/app/entity/shared/tabs/Dataset/Schema/SchemaTab.tsx b/datahub-web-react/src/app/entity/shared/tabs/Dataset/Schema/SchemaTab.tsx index a3d9b48a73c17a..fa2edd32f04d45 100644 --- a/datahub-web-react/src/app/entity/shared/tabs/Dataset/Schema/SchemaTab.tsx +++ b/datahub-web-react/src/app/entity/shared/tabs/Dataset/Schema/SchemaTab.tsx @@ -2,10 +2,7 @@ import { Empty } from 'antd'; import React, { useEffect, useMemo, useState } from 'react'; import styled from 'styled-components'; import { GetDatasetQuery } from '../../../../../../graphql/dataset.generated'; -import { - useGetSchemaBlameQuery, - useGetSchemaBlameVersionsQuery, -} from '../../../../../../graphql/schemaBlame.generated'; +import { useGetSchemaBlameQuery, useGetSchemaVersionListQuery } from '../../../../../../graphql/schemaBlame.generated'; import SchemaEditableContext from '../../../../../shared/SchemaEditableContext'; import SchemaHeader from '../../../../dataset/profile/schema/components/SchemaHeader'; import SchemaRawView from '../../../../dataset/profile/schema/components/SchemaRawView'; @@ -13,8 +10,7 @@ import { KEY_SCHEMA_PREFIX } from '../../../../dataset/profile/schema/utils/cons import { groupByFieldPath } from '../../../../dataset/profile/schema/utils/utils'; import { ANTD_GRAY } from '../../../constants'; import { useBaseEntity, useEntityData } from '../../../EntityContext'; -import { ChangeCategoryType, SchemaFieldBlame, SemanticVersionStruct } from '../../../../../../types.generated'; -import { SchemaViewType } from '../../../../dataset/profile/schema/utils/types'; +import { SchemaFieldBlame, SemanticVersionStruct } from '../../../../../../types.generated'; import SchemaTable from './SchemaTable'; import useGetSemanticVersionFromUrlParams from './utils/useGetSemanticVersionFromUrlParams'; import { useGetVersionedDatasetQuery } from '../../../../../../graphql/versionedDataset.generated'; @@ -54,22 +50,20 @@ export const SchemaTab = ({ properties }: { properties?: any }) => { ); const [showKeySchema, setShowKeySchema] = useState(false); - const [schemaViewMode, setSchemaViewMode] = useState(SchemaViewType.NORMAL); + const [showSchemaAuditView, setShowSchemaAuditView] = useState(false); - const { data: getSchemaBlameVersionsData } = useGetSchemaBlameVersionsQuery({ + const { data: getSchemaVersionListData } = useGetSchemaVersionListQuery({ skip: !datasetUrn, variables: { input: { datasetUrn, - categories: [ChangeCategoryType.TechnicalSchema], }, }, }); - const latestVersion: string = getSchemaBlameVersionsData?.getSchemaBlame?.latestVersion?.semanticVersion || ''; + const latestVersion: string = getSchemaVersionListData?.getSchemaVersionList?.latestVersion?.semanticVersion || ''; - const showSchemaBlame: boolean = schemaViewMode === SchemaViewType.BLAME; const versionList: Array = - getSchemaBlameVersionsData?.getSchemaBlame?.semanticVersionList || []; + getSchemaVersionListData?.getSchemaVersionList?.semanticVersionList || []; const version = useGetSemanticVersionFromUrlParams(); const selectedVersion = version || latestVersion; @@ -77,9 +71,10 @@ export const SchemaTab = ({ properties }: { properties?: any }) => { (semanticVersion) => semanticVersion.semanticVersion === selectedVersion, ); const selectedVersionStamp: string = selectedSemanticVersionStruct?.versionStamp || ''; + const isVersionLatest = selectedVersion === latestVersion; let editMode = true; - if (selectedVersion !== latestVersion) { + if (!isVersionLatest) { editMode = false; } else if (properties && properties.hasOwnProperty('editMode')) { editMode = properties.editMode; @@ -91,7 +86,6 @@ export const SchemaTab = ({ properties }: { properties?: any }) => { input: { datasetUrn, version: selectedVersion, - categories: [ChangeCategoryType.TechnicalSchema], }, }, }); @@ -139,8 +133,8 @@ export const SchemaTab = ({ properties }: { properties?: any }) => { lastUpdated={lastUpdated} selectedVersion={selectedVersion} versionList={versionList} - schemaView={schemaViewMode} - setSchemaView={setSchemaViewMode} + showSchemaAuditView={showSchemaAuditView} + setShowSchemaAuditView={setShowSchemaAuditView} /> {/* eslint-disable-next-line no-nested-ternary */} {showRaw ? ( @@ -159,7 +153,7 @@ export const SchemaTab = ({ properties }: { properties?: any }) => { editableSchemaMetadata={editableSchemaMetadata} usageStats={usageStats} schemaFieldBlameList={schemaFieldBlameList} - showSchemaBlame={showSchemaBlame} + showSchemaAuditView={showSchemaAuditView} /> diff --git a/datahub-web-react/src/app/entity/shared/tabs/Dataset/Schema/SchemaTable.tsx b/datahub-web-react/src/app/entity/shared/tabs/Dataset/Schema/SchemaTable.tsx index 4ddc35bca49db6..504675a64bbf13 100644 --- a/datahub-web-react/src/app/entity/shared/tabs/Dataset/Schema/SchemaTable.tsx +++ b/datahub-web-react/src/app/entity/shared/tabs/Dataset/Schema/SchemaTable.tsx @@ -44,7 +44,7 @@ export type Props = { editMode?: boolean; usageStats?: UsageQueryResult | null; schemaFieldBlameList?: Array | null; - showSchemaBlame: boolean; + showSchemaAuditView: boolean; }; export default function SchemaTable({ rows, @@ -53,7 +53,7 @@ export default function SchemaTable({ usageStats, editMode = true, schemaFieldBlameList, - showSchemaBlame, + showSchemaAuditView, }: Props): JSX.Element { const hasUsageStats = useMemo(() => (usageStats?.aggregations?.fields?.length || 0) > 0, [usageStats]); @@ -149,7 +149,7 @@ export default function SchemaTable({ allColumns = [...allColumns, usageColumn]; } - if (showSchemaBlame) { + if (showSchemaAuditView) { allColumns = [...allColumns, blameColumn]; } diff --git a/datahub-web-react/src/graphql/schemaBlame.graphql b/datahub-web-react/src/graphql/schemaBlame.graphql index f5ec131fec36d3..4fafaa4340c887 100644 --- a/datahub-web-react/src/graphql/schemaBlame.graphql +++ b/datahub-web-react/src/graphql/schemaBlame.graphql @@ -17,8 +17,8 @@ query getSchemaBlame($input: GetSchemaBlameInput!) { } } -query getSchemaBlameVersions($input: GetSchemaBlameInput!) { - getSchemaBlame(input: $input) { +query getSchemaVersionList($input: GetSchemaVersionListInput!) { + getSchemaVersionList(input: $input) { latestVersion { semanticVersion semanticVersionTimestamp