From dbcfe9e50e6f5793f8ee8f3c2dd86af63e11b1ef Mon Sep 17 00:00:00 2001 From: Aseem Bansal Date: Wed, 9 Feb 2022 22:26:27 +0530 Subject: [PATCH 1/9] docs(kafka): add example for using domains, change for clarity (#4100) --- metadata-ingestion/source_docs/kafka.md | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/metadata-ingestion/source_docs/kafka.md b/metadata-ingestion/source_docs/kafka.md index 1f6b790e3554de..f14ea4b23ee448 100644 --- a/metadata-ingestion/source_docs/kafka.md +++ b/metadata-ingestion/source_docs/kafka.md @@ -69,6 +69,23 @@ sink: # sink configs ``` +If you are trying to add domains to your topics you can use a configuration like below. + +```yml +source: + type: "kafka" + config: + # ...connection block + domain: + "urn:li:domain:13ae4d85-d955-49fc-8474-9004c663a810": + allow: + - ".*" + "urn:li:domain:d6ec9868-6736-4b1f-8aa6-fee4c5948f17": + deny: + - ".*" +``` + +Note that the `domain` in config above can be either an _urn_ or a domain _id_ (i.e. `urn:li:domain:13ae4d85-d955-49fc-8474-9004c663a810` or simply `13ae4d85-d955-49fc-8474-9004c663a810`). The Domain should exist in your DataHub instance before ingesting data into the Domain. To create a Domain on DataHub, check out the [Domains User Guide](https://datahubproject.io/docs/domains/). ## Config details @@ -84,9 +101,9 @@ Note that a `.` is used to denote nested fields in the YAML recipe. | `topic_patterns.allow` | | | List of regex patterns for topics to include in ingestion. | | `topic_patterns.deny` | | | List of regex patterns for topics to exclude from ingestion. | | `topic_patterns.ignoreCase` | | `True` | Whether to ignore case sensitivity during pattern matching. | -| `domain.domain_key.allow` | | | List of regex patterns for topics to set domain_key domain key. There can be multiple domain key specified. | -| `domain.domain_key.deny` | | | List of regex patterns for topics to not assign domain_key. There can be multiple domain key specified. | -| `domain.domain_key.ignoreCase` | | `True` | Whether to ignore case sensitivity during pattern matching.There can be multiple domain key specified. | +| `domain.domain_urn.allow` | | | List of regex patterns for topics to set domain_urn domain key. There can be multiple domain key specified. | +| `domain.domain_urn.deny` | | | List of regex patterns for topics to not assign domain_urn. There can be multiple domain key specified. | +| `domain.domain_urn.ignoreCase` | | `True` | Whether to ignore case sensitivity during pattern matching.There can be multiple domain key specified. | The options in the consumer config and schema registry config are passed to the Kafka DeserializingConsumer and SchemaRegistryClient respectively. From 0f56bc5d4875313009391f32c6d0446f52a45340 Mon Sep 17 00:00:00 2001 From: John Joyce Date: Wed, 9 Feb 2022 08:58:40 -0800 Subject: [PATCH 2/9] Adding extra properties to corp user editable info (#4097) --- .../graphql/types/corpuser/CorpUserType.java | 6 ++++ .../mappers/CorpUserEditableInfoMapper.java | 2 ++ .../mappers/CorpUserPropertiesMapper.java | 2 ++ .../src/main/resources/entity.graphql | 30 +++++++++++++++++++ .../identity/CorpUserEditableInfo.pdl | 10 +++++++ .../com.linkedin.entity.aspects.snapshot.json | 10 +++++++ ...com.linkedin.entity.entities.snapshot.json | 10 +++++++ .../com.linkedin.entity.runs.snapshot.json | 10 +++++++ 8 files changed, 80 insertions(+) diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/CorpUserType.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/CorpUserType.java index 993f2060459c22..ae4fec4aee5769 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/CorpUserType.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/CorpUserType.java @@ -138,6 +138,9 @@ public CorpUser update(@Nonnull String urn, @Nonnull CorpUserUpdateInput input, private RecordTemplate mapCorpUserEditableInfo(CorpUserUpdateInput input, Optional existing) { CorpUserEditableInfo result = existing.orElseGet(() -> new CorpUserEditableInfo()); + if (input.getDisplayName() != null) { + result.setDisplayName(input.getDisplayName()); + } if (input.getAboutMe() != null) { result.setAboutMe(input.getAboutMe()); } @@ -162,6 +165,9 @@ private RecordTemplate mapCorpUserEditableInfo(CorpUserUpdateInput input, Option if (input.getEmail() != null) { result.setEmail(input.getEmail()); } + if (input.getTitle() != null) { + result.setTitle(input.getTitle()); + } return result; } diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/mappers/CorpUserEditableInfoMapper.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/mappers/CorpUserEditableInfoMapper.java index 8e6ddfcbaab6bf..2a9f0efd69bcc8 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/mappers/CorpUserEditableInfoMapper.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/mappers/CorpUserEditableInfoMapper.java @@ -21,6 +21,8 @@ public static CorpUserEditableProperties map(@Nonnull final com.linkedin.identit @Override public CorpUserEditableProperties apply(@Nonnull final com.linkedin.identity.CorpUserEditableInfo info) { final CorpUserEditableProperties result = new CorpUserEditableProperties(); + result.setDisplayName(info.getDisplayName()); + result.setTitle(info.getTitle()); result.setAboutMe(info.getAboutMe()); result.setSkills(info.getSkills()); result.setTeams(info.getTeams()); diff --git a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/mappers/CorpUserPropertiesMapper.java b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/mappers/CorpUserPropertiesMapper.java index c64406a74733bc..1b42121962e46f 100644 --- a/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/mappers/CorpUserPropertiesMapper.java +++ b/datahub-graphql-core/src/main/java/com/linkedin/datahub/graphql/types/corpuser/mappers/CorpUserPropertiesMapper.java @@ -21,6 +21,8 @@ public static CorpUserProperties map(@Nonnull final com.linkedin.identity.CorpUs @Override public CorpUserProperties apply(@Nonnull final com.linkedin.identity.CorpUserInfo info) { final CorpUserProperties result = new CorpUserProperties(); + result.setDisplayName(info.getDisplayName()); + result.setTitle(info.getTitle()); result.setActive(info.isActive()); result.setCountryCode(info.getCountryCode()); result.setDepartmentId(info.getDepartmentId()); diff --git a/datahub-graphql-core/src/main/resources/entity.graphql b/datahub-graphql-core/src/main/resources/entity.graphql index 8b8ddc5c5516af..a75181c78c4133 100644 --- a/datahub-graphql-core/src/main/resources/entity.graphql +++ b/datahub-graphql-core/src/main/resources/entity.graphql @@ -2217,6 +2217,16 @@ Deprecated, use CorpUserEditableProperties instead Additional read write info about a user """ type CorpUserEditableInfo { + """ + Display name to show on DataHub + """ + displayName: String + + """ + Title to show on DataHub + """ + title: String + """ About me section of the user """ @@ -2242,6 +2252,16 @@ type CorpUserEditableInfo { Additional read write properties about a user """ type CorpUserEditableProperties { + """ + Display name to show on DataHub + """ + displayName: String + + """ + Title to show on DataHub + """ + title: String + """ About me section of the user """ @@ -2283,6 +2303,16 @@ type CorpUserEditableProperties { Arguments provided to update a CorpUser Entity """ input CorpUserUpdateInput { + """ + Display name to show on DataHub + """ + displayName: String + + """ + Title to show on DataHub + """ + title: String + """ About me section of the user """ diff --git a/metadata-models/src/main/pegasus/com/linkedin/identity/CorpUserEditableInfo.pdl b/metadata-models/src/main/pegasus/com/linkedin/identity/CorpUserEditableInfo.pdl index 782a2723075a56..c2a2f384c3e986 100644 --- a/metadata-models/src/main/pegasus/com/linkedin/identity/CorpUserEditableInfo.pdl +++ b/metadata-models/src/main/pegasus/com/linkedin/identity/CorpUserEditableInfo.pdl @@ -41,6 +41,16 @@ record CorpUserEditableInfo { */ pictureLink: Url = "https://raw.githubusercontent.com/linkedin/datahub/master/datahub-web-react/src/images/default_avatar.png" + /** + * DataHub-native display name + */ + displayName: optional string + + /** + * DataHub-native Title, e.g. 'Software Engineer' + */ + title: optional string + /** * Slack handle for the user */ diff --git a/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.aspects.snapshot.json b/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.aspects.snapshot.json index 2fcd7010882c6e..bb456a342ad140 100644 --- a/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.aspects.snapshot.json +++ b/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.aspects.snapshot.json @@ -1674,6 +1674,16 @@ "type" : "com.linkedin.common.Url", "doc" : "A URL which points to a picture which user wants to set as a profile photo", "default" : "https://raw.githubusercontent.com/linkedin/datahub/master/datahub-web-react/src/images/default_avatar.png" + }, { + "name" : "displayName", + "type" : "string", + "doc" : "DataHub-native display name", + "optional" : true + }, { + "name" : "title", + "type" : "string", + "doc" : "DataHub-native Title, e.g. 'Software Engineer'", + "optional" : true }, { "name" : "slack", "type" : "string", diff --git a/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.entities.snapshot.json b/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.entities.snapshot.json index fadc939f9efe7f..3cf63c064f0878 100644 --- a/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.entities.snapshot.json +++ b/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.entities.snapshot.json @@ -2098,6 +2098,16 @@ "type" : "com.linkedin.common.Url", "doc" : "A URL which points to a picture which user wants to set as a profile photo", "default" : "https://raw.githubusercontent.com/linkedin/datahub/master/datahub-web-react/src/images/default_avatar.png" + }, { + "name" : "displayName", + "type" : "string", + "doc" : "DataHub-native display name", + "optional" : true + }, { + "name" : "title", + "type" : "string", + "doc" : "DataHub-native Title, e.g. 'Software Engineer'", + "optional" : true }, { "name" : "slack", "type" : "string", diff --git a/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.runs.snapshot.json b/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.runs.snapshot.json index b8f7949451150b..5194206d2e2f21 100644 --- a/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.runs.snapshot.json +++ b/metadata-service/restli-api/src/main/snapshot/com.linkedin.entity.runs.snapshot.json @@ -1431,6 +1431,16 @@ "type" : "com.linkedin.common.Url", "doc" : "A URL which points to a picture which user wants to set as a profile photo", "default" : "https://raw.githubusercontent.com/linkedin/datahub/master/datahub-web-react/src/images/default_avatar.png" + }, { + "name" : "displayName", + "type" : "string", + "doc" : "DataHub-native display name", + "optional" : true + }, { + "name" : "title", + "type" : "string", + "doc" : "DataHub-native Title, e.g. 'Software Engineer'", + "optional" : true }, { "name" : "slack", "type" : "string", From 2d7452d64ae59fca9bb8043640162c37c9687341 Mon Sep 17 00:00:00 2001 From: Ravindra Lanka Date: Wed, 9 Feb 2022 13:34:52 -0800 Subject: [PATCH 3/9] feat(ingest): bigquery - enhance logging while processing audit logs (#4101) --- .../datahub/ingestion/source/sql/bigquery.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/metadata-ingestion/src/datahub/ingestion/source/sql/bigquery.py b/metadata-ingestion/src/datahub/ingestion/source/sql/bigquery.py index 1cae97f2ad2a9c..e1a6d204edb494 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/sql/bigquery.py +++ b/metadata-ingestion/src/datahub/ingestion/source/sql/bigquery.py @@ -317,6 +317,7 @@ def _compute_big_query_lineage(self) -> None: ) def _compute_bigquery_lineage_via_gcp_logging(self) -> None: + logger.info("Populating lineage info via GCP audit logs") try: _clients: List[GCPLoggingClient] = self._make_bigquery_client() log_entries: Iterable[AuditLogEntry] = self._get_bigquery_log_entries( @@ -333,6 +334,7 @@ def _compute_bigquery_lineage_via_gcp_logging(self) -> None: ) def _compute_bigquery_lineage_via_exported_bigquery_audit_metadata(self) -> None: + logger.info("Populating lineage info via exported GCP audit logs") try: _client: BigQueryClient = BigQueryClient(project=self.config.project_id) exported_bigquery_audit_metadata: Iterable[ @@ -441,11 +443,15 @@ def _get_exported_bigquery_audit_metadata( def _parse_bigquery_log_entries( self, entries: Iterable[AuditLogEntry] ) -> Iterable[QueryEvent]: + num_total_log_entries: int = 0 + num_parsed_log_entires: int = 0 for entry in entries: + num_total_log_entries += 1 event: Optional[QueryEvent] = None try: if QueryEvent.can_parse_entry(entry): event = QueryEvent.from_entry(entry) + num_parsed_log_entires += 1 else: raise RuntimeError("Unable to parse log entry as QueryEvent.") except Exception as e: @@ -456,6 +462,10 @@ def _parse_bigquery_log_entries( logger.error("Unable to parse GCP log entry.", e) if event is not None: yield event + logger.info( + f"Parsing BigQuery log entries: Number of log entries scanned={num_total_log_entries}, " + f"number of log entries successfully parsed={num_parsed_log_entires}" + ) def _parse_exported_bigquery_audit_metadata( self, audit_metadata_rows: Iterable[BigQueryAuditMetadata] @@ -482,18 +492,29 @@ def _parse_exported_bigquery_audit_metadata( def _create_lineage_map(self, entries: Iterable[QueryEvent]) -> Dict[str, Set[str]]: lineage_map: Dict[str, Set[str]] = collections.defaultdict(set) + num_entries: int = 0 + num_skipped_entries: int = 0 for e in entries: + num_entries += 1 if ( e.destinationTable is None or e.destinationTable.is_anonymous() or not e.referencedTables ): + num_skipped_entries += 1 continue + entry_consumed: bool = False for ref_table in e.referencedTables: destination_table_str = str(e.destinationTable.remove_extras()) ref_table_str = str(ref_table.remove_extras()) if ref_table_str != destination_table_str: lineage_map[destination_table_str].add(ref_table_str) + entry_consumed = True + if not entry_consumed: + num_skipped_entries += 1 + logger.info( + f"Creating lineage map: total number of entries={num_entries}, number skipped={num_skipped_entries}." + ) return lineage_map def get_latest_partition( From 582ba475dbe7109685c9e97460f58e4b3c7027b2 Mon Sep 17 00:00:00 2001 From: Gabe Lyons Date: Wed, 9 Feb 2022 20:24:11 -0800 Subject: [PATCH 4/9] fix(ui): glossary-terms - fixes add term flow (#4106) --- .../src/app/shared/tags/AddTagTermModal.tsx | 25 +++++++++++++------ datahub-web-react/src/graphql/search.graphql | 1 + .../integration/mutations/mutations.js | 4 +++ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/datahub-web-react/src/app/shared/tags/AddTagTermModal.tsx b/datahub-web-react/src/app/shared/tags/AddTagTermModal.tsx index 3c300f45cdcfb0..66f6d376e4c50d 100644 --- a/datahub-web-react/src/app/shared/tags/AddTagTermModal.tsx +++ b/datahub-web-react/src/app/shared/tags/AddTagTermModal.tsx @@ -3,7 +3,15 @@ import { message, Button, Modal, Select, Typography } from 'antd'; import styled from 'styled-components'; import { useGetSearchResultsLazyQuery } from '../../../graphql/search.generated'; -import { GlobalTags, EntityType, GlossaryTerms, SubResourceType, SearchResult } from '../../../types.generated'; +import { + GlobalTags, + EntityType, + GlossaryTerms, + SubResourceType, + SearchResult, + Tag, + GlossaryTerm, +} from '../../../types.generated'; import CreateTagModal from './CreateTagModal'; import { useEntityRegistry } from '../../useEntityRegistry'; import { IconStyleType } from '../../entity/Entity'; @@ -75,15 +83,15 @@ export default function AddTagTermModal({ const entityRegistry = useEntityRegistry(); const [addTagMutation] = useAddTagMutation(); const [addTermMutation] = useAddTermMutation(); - const [tagSearch, { data: tagSearchData }] = useGetSearchResultsLazyQuery(); - const tagSearchResults = tagSearchData?.search?.searchResults || []; + const [tagTermSearch, { data: tagTermSearchData }] = useGetSearchResultsLazyQuery(); + const tagSearchResults = tagTermSearchData?.search?.searchResults || []; const handleSearch = (text: string) => { if (text.length > 0) { - tagSearch({ + tagTermSearch({ variables: { input: { - type: EntityType.Tag, + type, query: text, start: 0, count: 10, @@ -94,7 +102,10 @@ export default function AddTagTermModal({ }; const renderSearchResult = (result: SearchResult) => { - const displayName = entityRegistry.getDisplayName(result.entity.type, result.entity); + const displayName = + result.entity.type === EntityType.Tag + ? (result.entity as Tag).name + : (result.entity as GlossaryTerm).hierarchicalName; const item = renderItem( displayName, entityRegistry.getIcon(result.entity.type, 14, IconStyleType.ACCENT), @@ -137,7 +148,7 @@ export default function AddTagTermModal({ } if (selectedType === EntityType.GlossaryTerm) { mutation = addTermMutation; - if (glossaryTerms?.terms?.some((term) => term.term.name === selectedName)) { + if (glossaryTerms?.terms?.some((term) => term.term.hierarchicalName === selectedName)) { onClose(); return; } diff --git a/datahub-web-react/src/graphql/search.graphql b/datahub-web-react/src/graphql/search.graphql index 5adb5b14f0fbb6..fd641b4cfaae22 100644 --- a/datahub-web-react/src/graphql/search.graphql +++ b/datahub-web-react/src/graphql/search.graphql @@ -218,6 +218,7 @@ fragment searchResults on SearchResults { } ... on GlossaryTerm { name + hierarchicalName glossaryTermInfo { definition termSource diff --git a/smoke-test/tests/cypress/cypress/integration/mutations/mutations.js b/smoke-test/tests/cypress/cypress/integration/mutations/mutations.js index a8ba4afc192d45..e2df2710c26c0b 100644 --- a/smoke-test/tests/cypress/cypress/integration/mutations/mutations.js +++ b/smoke-test/tests/cypress/cypress/integration/mutations/mutations.js @@ -15,6 +15,10 @@ describe('mutations', () => { cy.contains(/Create$/).click(); + // wait a breath for elasticsearch to index the tag being applied to the dataset- if we navigate too quick ES + // wont know and we'll see applied to 0 entities + cy.wait(2000); + // go to tag page cy.get('a[href="/tag/urn:li:tag:CypressTestAddTag"]').click(); From 3bc7c5adf405448672f78693ca177757d565a457 Mon Sep 17 00:00:00 2001 From: Maggie Hays Date: Wed, 9 Feb 2022 22:25:34 -0600 Subject: [PATCH 5/9] docs(logos): Add Zynga - adoption & Tableau -integration logos (#4109) --- README.md | 4 +++- docs-website/src/components/Logos.js | 9 +++++++++ .../static/img/logos/companies/zynga.png | Bin 0 -> 43203 bytes .../static/img/logos/platforms/tableau.png | Bin 0 -> 13865 bytes 4 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 docs-website/static/img/logos/companies/zynga.png create mode 100644 docs-website/static/img/logos/platforms/tableau.png diff --git a/README.md b/README.md index 1619fc3f79eb52..1b81324eea247e 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,7 @@ Here are the companies that have officially adopted DataHub. Please feel free to - [Experius](https://www.experius.nl) - [Geotab](https://www.geotab.com) - [Grofers](https://grofers.com) +- [Haibo Technology](https://www.botech.com.cn) - [hipages](https://hipages.com.au/) - [IOMED](https://iomed.health) - [Klarna](https://www.klarna.com) @@ -128,7 +129,8 @@ Here are the companies that have officially adopted DataHub. Please feel free to - [Uphold](https://uphold.com) - [Viasat](https://viasat.com) - [Wolt](https://wolt.com) -- [Haibo Technology](https://www.botech.com.cn) +- [Zynga](https://www.zynga.com) + ## Select Articles & Talks diff --git a/docs-website/src/components/Logos.js b/docs-website/src/components/Logos.js index 5cda6fad29c566..0edb0cfa1456c4 100644 --- a/docs-website/src/components/Logos.js +++ b/docs-website/src/components/Logos.js @@ -21,6 +21,11 @@ const companyLogos = [ imageUrl: "/img/logos/companies/saxobank.svg", size: "default", }, + { + name: "Zynga", + imageUrl: "/img/logos/companies/zynga.png", + size: "default", + }, { name: "Grofers", imageUrl: "/img/logos/companies/grofers.png", @@ -181,6 +186,10 @@ const platformLogos = [ name: "Superset", imageUrl: "/img/logos/platforms/superset.svg", }, + { + name: "Tableau", + imageUrl: "/img/logos/platforms/tableau.png", + }, { name: "Teradata", imageUrl: "/img/logos/platforms/teradata.svg", diff --git a/docs-website/static/img/logos/companies/zynga.png b/docs-website/static/img/logos/companies/zynga.png new file mode 100644 index 0000000000000000000000000000000000000000..aaee6f79ff61218976675f84b195b55799234bc2 GIT binary patch literal 43203 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 literal 0 HcmV?d00001 diff --git a/docs-website/static/img/logos/platforms/tableau.png b/docs-website/static/img/logos/platforms/tableau.png new file mode 100644 index 0000000000000000000000000000000000000000..d13ad82f8eb4303ebf403498a2432414988e2986 GIT binary patch literal 13865 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 DTNk#1 literal 0 HcmV?d00001 From 47db7b5e2c89a6fb5d47bdd74af4c56cabf504ed Mon Sep 17 00:00:00 2001 From: Dexter Lee Date: Wed, 9 Feb 2022 23:12:41 -0800 Subject: [PATCH 6/9] fix(ingest): dependencies - fix for redshift-usage, mode, superset and others (#4103) --- metadata-ingestion/setup.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/metadata-ingestion/setup.py b/metadata-ingestion/setup.py index bccbaef454bb29..5b8958b34fb2b9 100644 --- a/metadata-ingestion/setup.py +++ b/metadata-ingestion/setup.py @@ -127,8 +127,8 @@ def get_long_description(): "lookml": looker_common | {"lkml>=1.1.2", "sql-metadata==2.2.2", "sqllineage==1.3.3"}, "metabase": {"requests", "sqllineage==1.3.3"}, - "mode": {"requests", "sqllineage==1.3.3"}, - "mongodb": {"pymongo>=3.11"}, + "mode": {"requests", "sqllineage==1.3.3", "tenacity>=8.0.1"}, + "mongodb": {"pymongo>=3.11", "packaging"}, "mssql": sql_common | {"sqlalchemy-pytds>=0.3"}, "mssql-odbc": sql_common | {"pyodbc"}, "mysql": sql_common | {"pymysql>=1.0.2"}, @@ -141,16 +141,16 @@ def get_long_description(): "redshift": sql_common | {"sqlalchemy-redshift", "psycopg2-binary", "GeoAlchemy2", "sqllineage==1.3.3"}, "redshift-usage": sql_common - | {"sqlalchemy-redshift", "psycopg2-binary", "GeoAlchemy2"}, + | {"sqlalchemy-redshift", "psycopg2-binary", "GeoAlchemy2", "sqllineage==1.3.3"}, "sagemaker": aws_common, "snowflake": snowflake_common, "snowflake-usage": snowflake_common | {"more-itertools>=8.12.0"}, "sqlalchemy": sql_common, - "superset": {"requests"}, + "superset": {"requests", "sqlalchemy", "great_expectations"}, "tableau": {"tableauserverclient>=0.17.0"}, "trino": sql_common | {"trino"}, "starburst-trino-usage": sql_common | {"trino"}, - "nifi": {"requests"}, + "nifi": {"requests", "packaging"}, } all_exclude_plugins: Set[str] = { From 54eb155f3ec16b3914f5a6ad002f6524a7c22de0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ebu=20=28=E3=81=88=E3=81=B6=29?= Date: Thu, 10 Feb 2022 16:16:26 +0900 Subject: [PATCH 7/9] feat(ui): Add svg datahub satellite loading logo (#4067) --- .../datahub-logo-color-loading_pendulum.svg | 85 +++++++++++-------- .../datahub-logo-color-loading_satellite.svg | 69 +++++++++++++++ 2 files changed, 119 insertions(+), 35 deletions(-) create mode 100644 datahub-web-react/src/images/datahub-logo-color-loading_satellite.svg diff --git a/datahub-web-react/src/images/datahub-logo-color-loading_pendulum.svg b/datahub-web-react/src/images/datahub-logo-color-loading_pendulum.svg index 06fe3b28ac70d8..97908866015a3e 100644 --- a/datahub-web-react/src/images/datahub-logo-color-loading_pendulum.svg +++ b/datahub-web-react/src/images/datahub-logo-color-loading_pendulum.svg @@ -1,48 +1,63 @@ - - - - + + + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + - - - + + + + - - - - - + + + + \ No newline at end of file diff --git a/datahub-web-react/src/images/datahub-logo-color-loading_satellite.svg b/datahub-web-react/src/images/datahub-logo-color-loading_satellite.svg new file mode 100644 index 00000000000000..dc14931c5dfba7 --- /dev/null +++ b/datahub-web-react/src/images/datahub-logo-color-loading_satellite.svg @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 076848ff553a333c2dcb314082eb68f26187a670 Mon Sep 17 00:00:00 2001 From: Harshal Sheth Date: Thu, 10 Feb 2022 02:18:19 -0500 Subject: [PATCH 8/9] fix(ingest): oracle - support large view definitions (#4027) --- .../datahub/ingestion/source/sql/oracle.py | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/metadata-ingestion/src/datahub/ingestion/source/sql/oracle.py b/metadata-ingestion/src/datahub/ingestion/source/sql/oracle.py index 0a2d18fbe3b4b9..296a46d4c4be8f 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/sql/oracle.py +++ b/metadata-ingestion/src/datahub/ingestion/source/sql/oracle.py @@ -1,10 +1,12 @@ -from typing import Optional +from typing import Iterable, Optional from unittest.mock import patch # This import verifies that the dependencies are available. import cx_Oracle # noqa: F401 import pydantic +from sqlalchemy import event from sqlalchemy.dialects.oracle.base import OracleDialect +from sqlalchemy.engine.reflection import Inspector from datahub.ingestion.source.sql.sql_common import ( BasicSQLAlchemyConfig, @@ -21,6 +23,19 @@ assert OracleDialect.ischema_names +def output_type_handler(cursor, name, defaultType, size, precision, scale): + """Add CLOB and BLOB support to Oracle connection.""" + + if defaultType == cx_Oracle.CLOB: + return cursor.var(cx_Oracle.LONG_STRING, arraysize=cursor.arraysize) + elif defaultType == cx_Oracle.BLOB: + return cursor.var(cx_Oracle.LONG_BINARY, arraysize=cursor.arraysize) + + +def before_cursor_execute(conn, cursor, statement, parameters, context, executemany): + cursor.outputtypehandler = output_type_handler + + class OracleConfig(BasicSQLAlchemyConfig): # defaults scheme = "oracle+cx_oracle" @@ -52,6 +67,13 @@ def create(cls, config_dict, ctx): config = OracleConfig.parse_obj(config_dict) return cls(config, ctx) + def get_inspectors(self) -> Iterable[Inspector]: + for inspector in super().get_inspectors(): + event.listen( + inspector.engine, "before_cursor_execute", before_cursor_execute + ) + yield inspector + def get_workunits(self): with patch.dict( "sqlalchemy.dialects.oracle.base.OracleDialect.ischema_names", From 9bdc9af7b90c6a97194eceb898543360b4eb105c Mon Sep 17 00:00:00 2001 From: Kevin Hu <6051736+kevinhu@users.noreply.github.com> Date: Thu, 10 Feb 2022 02:20:25 -0500 Subject: [PATCH 9/9] fix(ingest): postgres - ignore information_schema tables by default (#4069) --- metadata-ingestion/source_docs/postgres.md | 12 ++++++------ .../src/datahub/ingestion/source/sql/postgres.py | 2 ++ 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/metadata-ingestion/source_docs/postgres.md b/metadata-ingestion/source_docs/postgres.md index 32555cfb835649..b67f8da8ff3b29 100644 --- a/metadata-ingestion/source_docs/postgres.md +++ b/metadata-ingestion/source_docs/postgres.md @@ -16,10 +16,10 @@ This plugin extracts the following: - database_alias (optional) can be used to change the name of database to be ingested - Table, row, and column statistics via optional [SQL profiling](./sql_profiles.md) -| Capability | Status | Details | -|-------------------|--------|------------------------------------------| -| Data Containers | ✔️ | | -| Data Domains | ✔️ | [link](../../docs/domains.md) | +| Capability | Status | Details | +| --------------- | ------ | ----------------------------- | +| Data Containers | ✔️ | | +| Data Domains | ✔️ | [link](../../docs/domains.md) | ## Quickstart recipe @@ -53,10 +53,10 @@ Note that a `.` is used to denote nested fields in the YAML recipe. As a SQL-based service, the Athena integration is also supported by our SQL profiler. See [here](./sql_profiles.md) for more details on configuration. | Field | Required | Default | Description | -|--------------------------------|----------|----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ------------------------------ | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `username` | | | PostgreSQL username. | | `password` | | | PostgreSQL password. | -| `host_port` | ✅ | | PostgreSQL host URL. | +| `host_port` | ✅ | | PostgreSQL host URL. | | `database` | | | PostgreSQL database. | | `database_alias` | | | Alias to apply to database when ingesting. | | `env` | | `"PROD"` | Environment to use in namespace when constructing URNs. | diff --git a/metadata-ingestion/src/datahub/ingestion/source/sql/postgres.py b/metadata-ingestion/src/datahub/ingestion/source/sql/postgres.py index dedbb95a859e48..d18a4ce3f7382b 100644 --- a/metadata-ingestion/src/datahub/ingestion/source/sql/postgres.py +++ b/metadata-ingestion/src/datahub/ingestion/source/sql/postgres.py @@ -9,6 +9,7 @@ # https://geoalchemy-2.readthedocs.io/en/latest/core_tutorial.html#reflecting-tables. from geoalchemy2 import Geometry # noqa: F401 +from datahub.configuration.common import AllowDenyPattern from datahub.ingestion.source.sql.sql_common import ( BasicSQLAlchemyConfig, SQLAlchemySource, @@ -29,6 +30,7 @@ class PostgresConfig(BasicSQLAlchemyConfig): # defaults scheme = "postgresql+psycopg2" + schema_pattern = AllowDenyPattern(deny=["information_schema"]) def get_identifier(self: BasicSQLAlchemyConfig, schema: str, table: str) -> str: regular = f"{schema}.{table}"