google-pso-data-validator 7.6.0__tar.gz → 7.7.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {google_pso_data_validator-7.6.0/google_pso_data_validator.egg-info → google_pso_data_validator-7.7.0}/PKG-INFO +10 -3
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/README.md +9 -2
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/cli_tools.py +9 -2
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/clients.py +27 -7
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/combiner.py +12 -12
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/config_manager.py +19 -13
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/consts.py +18 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/query_builder/query_builder.py +9 -1
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/result_handlers/bigquery.py +1 -1
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/validation_builder.py +63 -11
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0/google_pso_data_validator.egg-info}/PKG-INFO +10 -3
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/google_pso_data_validator.egg-info/SOURCES.txt +2 -2
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/setup.py +1 -1
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_addon/operations.py +44 -63
- google_pso_data_validator-7.7.0/third_party/ibis/ibis_bigquery/__init__.py +142 -0
- google_pso_data_validator-7.7.0/third_party/ibis/ibis_bigquery/api.py +35 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_postgres/client.py +7 -0
- google_pso_data_validator-7.7.0/third_party/ibis/ibis_postgres/registry.py +86 -0
- google_pso_data_validator-7.6.0/third_party/ibis/ibis_biquery/api.py +0 -15
- google_pso_data_validator-7.6.0/third_party/ibis/ibis_postgres/registry.py +0 -34
- google_pso_data_validator-7.6.0/third_party/ibis/ibis_snowflake/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/LICENSE +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/__main__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/app.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/client_info.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/data_validation.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/exceptions.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/find_tables.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/gcs_helper.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/jellyfish_distance.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/metadata.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/partition_builder.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/query_builder/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/query_builder/partition_row_builder.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/query_builder/random_row_builder.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/raw_query.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/result_handlers/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/result_handlers/base_backend.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/result_handlers/factory.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/result_handlers/postgres.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/result_handlers/text.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/schema_validation.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/secret_manager.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/state_manager.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/util.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/google_pso_data_validator.egg-info/dependency_links.txt +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/google_pso_data_validator.egg-info/entry_points.txt +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/google_pso_data_validator.egg-info/requires.txt +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/google_pso_data_validator.egg-info/top_level.txt +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/setup.cfg +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_addon/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_addon/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_cloud_spanner/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_cloud_spanner/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_cloud_spanner/client.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_cloud_spanner/compiler.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_cloud_spanner/datatypes.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_cloud_spanner/registry.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_cloud_spanner/tests/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_cloud_spanner/tests/conftest.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_cloud_spanner/to_pandas.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_db2/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_db2/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_db2/compiler.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_db2/datatypes.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_db2/registry.py +0 -0
- {google_pso_data_validator-7.6.0/third_party/ibis/ibis_biquery → google_pso_data_validator-7.7.0/third_party/ibis/ibis_impala}/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_impala/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_mssql/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_mssql/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_mssql/datatypes.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_mssql/registry.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_mysql/__init__.py +0 -0
- {google_pso_data_validator-7.6.0/third_party/ibis/ibis_impala → google_pso_data_validator-7.7.0/third_party/ibis/ibis_mysql/base_sql_compiler}/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_mysql/base_sql_compiler/select_builder.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_mysql/compiler.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_oracle/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_oracle/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_oracle/compiler.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_oracle/datatypes.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_oracle/registry.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_postgres/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_postgres/datatypes.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_redshift/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_redshift/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_redshift/compiler.py +0 -0
- {google_pso_data_validator-7.6.0/third_party/ibis/ibis_mysql/base_sql_compiler → google_pso_data_validator-7.7.0/third_party/ibis/ibis_snowflake}/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_snowflake/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_snowflake/datatypes.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_teradata/__init__.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_teradata/api.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_teradata/compiler.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_teradata/datatypes.py +0 -0
- {google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/third_party/ibis/ibis_teradata/registry.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: google-pso-data-validator
|
|
3
|
-
Version: 7.
|
|
3
|
+
Version: 7.7.0
|
|
4
4
|
Summary: A package to enable easy data validation
|
|
5
5
|
Author: PSO DVT Engineering team
|
|
6
6
|
Author-email: data-validator-eng@google.com
|
|
@@ -125,8 +125,10 @@ please review the [Connections](https://github.com/GoogleCloudPlatform/professio
|
|
|
125
125
|
### Running Validations
|
|
126
126
|
|
|
127
127
|
The CLI is the main interface to use this tool and it has several different
|
|
128
|
-
commands which can be used to create and run validations.
|
|
129
|
-
|
|
128
|
+
commands which can be used to create and run validations. DVT is designed to run in
|
|
129
|
+
an environment connected to GCP services, specifically, BigQuery, GCS and Secret manager.
|
|
130
|
+
If DVT is being run on-premises or in an environment with restricted access to GCP services, see
|
|
131
|
+
[running DVT at on-prem](#running-dvt-at-on-prem). Below are the command syntax and options for running validations.
|
|
130
132
|
|
|
131
133
|
Alternatives to running DVT in the CLI include deploying DVT to Cloud Run, Cloud Functions, or Airflow
|
|
132
134
|
([Examples Here](https://github.com/GoogleCloudPlatform/professional-services-data-validator/tree/develop/samples)). See the [Validation Logic](https://github.com/GoogleCloudPlatform/professional-services-data-validator#validation-logic) section
|
|
@@ -584,6 +586,11 @@ For example, this flag can be used as follows:
|
|
|
584
586
|
"target_query": "SELECT `hash__all`, `station_id`\nFROM ..."
|
|
585
587
|
}
|
|
586
588
|
```
|
|
589
|
+
#### Running DVT at on-prem
|
|
590
|
+
On-premises environments can have limited access to GCP services. DVT supports using BigQuery for storing validation results, GCS for storage and
|
|
591
|
+
the Secret Manager for secrets. You may also use BigQuery and Spanner as a source or target for validation. Service
|
|
592
|
+
APIs (i.e. bigquery.googleapis.com) may not always be accessible due to firewall restrictions. Work with your network
|
|
593
|
+
adminstrator to identify the way to access these services. They may set up a [Private Service Connect Endpoint](https://cloud.google.com/vpc/docs/about-accessing-google-apis-endpoints). DVT supports accessing source and target tables in Spanner and BigQuery via endpoints set up in your network. Connection Parameters for [Spanner](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#google-spanner) and [BigQuery](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#google-bigquery) outline regarding how to specify endpoints.
|
|
587
594
|
|
|
588
595
|
### Running DVT with YAML Configuration Files
|
|
589
596
|
|
|
@@ -66,8 +66,10 @@ please review the [Connections](https://github.com/GoogleCloudPlatform/professio
|
|
|
66
66
|
### Running Validations
|
|
67
67
|
|
|
68
68
|
The CLI is the main interface to use this tool and it has several different
|
|
69
|
-
commands which can be used to create and run validations.
|
|
70
|
-
|
|
69
|
+
commands which can be used to create and run validations. DVT is designed to run in
|
|
70
|
+
an environment connected to GCP services, specifically, BigQuery, GCS and Secret manager.
|
|
71
|
+
If DVT is being run on-premises or in an environment with restricted access to GCP services, see
|
|
72
|
+
[running DVT at on-prem](#running-dvt-at-on-prem). Below are the command syntax and options for running validations.
|
|
71
73
|
|
|
72
74
|
Alternatives to running DVT in the CLI include deploying DVT to Cloud Run, Cloud Functions, or Airflow
|
|
73
75
|
([Examples Here](https://github.com/GoogleCloudPlatform/professional-services-data-validator/tree/develop/samples)). See the [Validation Logic](https://github.com/GoogleCloudPlatform/professional-services-data-validator#validation-logic) section
|
|
@@ -525,6 +527,11 @@ For example, this flag can be used as follows:
|
|
|
525
527
|
"target_query": "SELECT `hash__all`, `station_id`\nFROM ..."
|
|
526
528
|
}
|
|
527
529
|
```
|
|
530
|
+
#### Running DVT at on-prem
|
|
531
|
+
On-premises environments can have limited access to GCP services. DVT supports using BigQuery for storing validation results, GCS for storage and
|
|
532
|
+
the Secret Manager for secrets. You may also use BigQuery and Spanner as a source or target for validation. Service
|
|
533
|
+
APIs (i.e. bigquery.googleapis.com) may not always be accessible due to firewall restrictions. Work with your network
|
|
534
|
+
adminstrator to identify the way to access these services. They may set up a [Private Service Connect Endpoint](https://cloud.google.com/vpc/docs/about-accessing-google-apis-endpoints). DVT supports accessing source and target tables in Spanner and BigQuery via endpoints set up in your network. Connection Parameters for [Spanner](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#google-spanner) and [BigQuery](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#google-bigquery) outline regarding how to specify endpoints.
|
|
528
535
|
|
|
529
536
|
### Running DVT with YAML Configuration Files
|
|
530
537
|
|
{google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/cli_tools.py
RENAMED
|
@@ -76,7 +76,11 @@ CONNECTION_SOURCE_FIELDS = {
|
|
|
76
76
|
["google_service_account_key_path", "(Optional) GCP SA Key Path"],
|
|
77
77
|
[
|
|
78
78
|
"api_endpoint",
|
|
79
|
-
'(Optional) GCP BigQuery API endpoint (e.g. "https://
|
|
79
|
+
'(Optional) GCP BigQuery API endpoint (e.g. "https://bigquery-mypsc.p.googleapis.com")',
|
|
80
|
+
],
|
|
81
|
+
[
|
|
82
|
+
"storage_api_endpoint",
|
|
83
|
+
'(Optional) GCP BigQuery Storage API endpoint (e.g. "https://bigquerystorage-mypsc.p.googleapis.com")',
|
|
80
84
|
],
|
|
81
85
|
],
|
|
82
86
|
consts.SOURCE_TYPE_TERADATA: [
|
|
@@ -140,7 +144,7 @@ CONNECTION_SOURCE_FIELDS = {
|
|
|
140
144
|
["google_service_account_key_path", "(Optional) GCP SA Key Path"],
|
|
141
145
|
[
|
|
142
146
|
"api_endpoint",
|
|
143
|
-
'(Optional) GCP Spanner API endpoint (e.g. "https://
|
|
147
|
+
'(Optional) GCP Spanner API endpoint (e.g. "https://spanner-mypsc.p.googleapis.com")',
|
|
144
148
|
],
|
|
145
149
|
],
|
|
146
150
|
consts.SOURCE_TYPE_FILESYSTEM: [
|
|
@@ -1284,6 +1288,9 @@ def _get_result_handler(rc_value: str, sa_file=None) -> dict:
|
|
|
1284
1288
|
consts.PROJECT_ID: conn_from_file["project_id"],
|
|
1285
1289
|
consts.TABLE_ID: config[1],
|
|
1286
1290
|
consts.API_ENDPOINT: conn_from_file.get("api_endpoint", None),
|
|
1291
|
+
consts.STORAGE_API_ENDPOINT: conn_from_file.get(
|
|
1292
|
+
"storage_api_endpoint", None
|
|
1293
|
+
),
|
|
1287
1294
|
}
|
|
1288
1295
|
elif conn_from_file[consts.SOURCE_TYPE] == consts.SOURCE_TYPE_POSTGRES:
|
|
1289
1296
|
result_handler = {
|
{google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/clients.py
RENAMED
|
@@ -27,6 +27,8 @@ import pandas
|
|
|
27
27
|
|
|
28
28
|
from data_validation import client_info, consts, exceptions
|
|
29
29
|
from data_validation.secret_manager import SecretManagerBuilder
|
|
30
|
+
|
|
31
|
+
from third_party.ibis.ibis_bigquery.api import bigquery_connect
|
|
30
32
|
from third_party.ibis.ibis_cloud_spanner.api import spanner_connect
|
|
31
33
|
from third_party.ibis.ibis_impala.api import impala_connect
|
|
32
34
|
from third_party.ibis.ibis_mssql.api import mssql_connect
|
|
@@ -111,24 +113,42 @@ def get_google_bigquery_client(
|
|
|
111
113
|
)
|
|
112
114
|
|
|
113
115
|
|
|
116
|
+
def _get_google_bqstorage_client(credentials=None, api_endpoint: str = None):
|
|
117
|
+
options = None
|
|
118
|
+
if api_endpoint:
|
|
119
|
+
options = client_options.ClientOptions(api_endpoint=api_endpoint)
|
|
120
|
+
from google.cloud import bigquery_storage_v1 as bigquery_storage
|
|
121
|
+
|
|
122
|
+
return bigquery_storage.BigQueryReadClient(
|
|
123
|
+
credentials=credentials,
|
|
124
|
+
client_options=options,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
114
128
|
def get_bigquery_client(
|
|
115
|
-
project_id: str,
|
|
129
|
+
project_id: str,
|
|
130
|
+
dataset_id: str = "",
|
|
131
|
+
credentials=None,
|
|
132
|
+
api_endpoint: str = None,
|
|
133
|
+
storage_api_endpoint: str = None,
|
|
116
134
|
):
|
|
117
135
|
google_client = get_google_bigquery_client(
|
|
118
136
|
project_id, credentials=credentials, api_endpoint=api_endpoint
|
|
119
137
|
)
|
|
138
|
+
bqstorage_client = None
|
|
139
|
+
if storage_api_endpoint:
|
|
140
|
+
bqstorage_client = _get_google_bqstorage_client(
|
|
141
|
+
credentials=credentials, api_endpoint=storage_api_endpoint
|
|
142
|
+
)
|
|
120
143
|
|
|
121
|
-
|
|
144
|
+
return bigquery_connect(
|
|
122
145
|
project_id=project_id,
|
|
123
146
|
dataset_id=dataset_id,
|
|
124
147
|
credentials=credentials,
|
|
148
|
+
bigquery_client=google_client,
|
|
149
|
+
bqstorage_client=bqstorage_client,
|
|
125
150
|
)
|
|
126
151
|
|
|
127
|
-
# Override the BigQuery client object to ensure the correct user agent is
|
|
128
|
-
# included and any api_endpoint is used.
|
|
129
|
-
ibis_client.client = google_client
|
|
130
|
-
return ibis_client
|
|
131
|
-
|
|
132
152
|
|
|
133
153
|
def get_pandas_client(table_name, file_path, file_type):
|
|
134
154
|
"""Return pandas client and env with file loaded into DataFrame
|
{google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/combiner.py
RENAMED
|
@@ -406,18 +406,18 @@ def _pivot_result(
|
|
|
406
406
|
consts.AGGREGATION_TYPE
|
|
407
407
|
),
|
|
408
408
|
ibis.literal(validation.get_table_name(result_type)).name(
|
|
409
|
-
|
|
409
|
+
consts.COMBINER_TABLE_NAME
|
|
410
410
|
),
|
|
411
411
|
# Cast to string to ensure types match, even when column
|
|
412
412
|
# name is NULL (such as for count aggregations).
|
|
413
413
|
ibis.literal(validation.get_column_name(result_type))
|
|
414
414
|
.cast("string")
|
|
415
|
-
.name(
|
|
415
|
+
.name(consts.COMBINER_COLUMN_NAME),
|
|
416
416
|
primary_keys,
|
|
417
417
|
ibis.literal(validation.num_random_rows).name(
|
|
418
418
|
consts.NUM_RANDOM_ROWS
|
|
419
419
|
),
|
|
420
|
-
result[field].cast("string").name(
|
|
420
|
+
result[field].cast("string").name(consts.COMBINER_AGG_VALUE),
|
|
421
421
|
)
|
|
422
422
|
+ join_on_fields
|
|
423
423
|
)
|
|
@@ -469,11 +469,11 @@ def _join_pivots(
|
|
|
469
469
|
+ [
|
|
470
470
|
source[consts.VALIDATION_TYPE],
|
|
471
471
|
source[consts.AGGREGATION_TYPE],
|
|
472
|
-
source[consts.
|
|
473
|
-
source[
|
|
472
|
+
source[consts.COMBINER_TABLE_NAME],
|
|
473
|
+
source[consts.COMBINER_COLUMN_NAME],
|
|
474
474
|
source[consts.CONFIG_PRIMARY_KEYS],
|
|
475
475
|
source[consts.NUM_RANDOM_ROWS],
|
|
476
|
-
source[
|
|
476
|
+
source[consts.COMBINER_AGG_VALUE],
|
|
477
477
|
differences[consts.VALIDATION_DIFFERENCE],
|
|
478
478
|
differences[consts.VALIDATION_PCT_DIFFERENCE],
|
|
479
479
|
differences[consts.VALIDATION_PCT_THRESHOLD],
|
|
@@ -488,12 +488,12 @@ def _join_pivots(
|
|
|
488
488
|
source_difference[consts.AGGREGATION_TYPE]
|
|
489
489
|
.fillna(target[consts.AGGREGATION_TYPE])
|
|
490
490
|
.name(consts.AGGREGATION_TYPE),
|
|
491
|
-
source_difference[
|
|
492
|
-
source_difference[
|
|
493
|
-
source_difference[
|
|
494
|
-
target[
|
|
495
|
-
target[
|
|
496
|
-
target[
|
|
491
|
+
source_difference[consts.COMBINER_TABLE_NAME].name(consts.SOURCE_TABLE_NAME),
|
|
492
|
+
source_difference[consts.COMBINER_COLUMN_NAME].name(consts.SOURCE_COLUMN_NAME),
|
|
493
|
+
source_difference[consts.COMBINER_AGG_VALUE].name(consts.SOURCE_AGG_VALUE),
|
|
494
|
+
target[consts.COMBINER_TABLE_NAME].name(consts.TARGET_TABLE_NAME),
|
|
495
|
+
target[consts.COMBINER_COLUMN_NAME].name(consts.TARGET_COLUMN_NAME),
|
|
496
|
+
target[consts.COMBINER_AGG_VALUE].name(consts.TARGET_AGG_VALUE),
|
|
497
497
|
group_by_columns,
|
|
498
498
|
source_difference[consts.CONFIG_PRIMARY_KEYS],
|
|
499
499
|
source_difference[consts.NUM_RANDOM_ROWS],
|
|
@@ -611,7 +611,7 @@ class ConfigManager(object):
|
|
|
611
611
|
self.build_config_calculated_fields(
|
|
612
612
|
[casefold_source_columns[field.casefold()]],
|
|
613
613
|
[casefold_target_columns[field.casefold()]],
|
|
614
|
-
|
|
614
|
+
consts.CALC_FIELD_RSTRIP,
|
|
615
615
|
alias,
|
|
616
616
|
0,
|
|
617
617
|
)
|
|
@@ -812,16 +812,16 @@ class ConfigManager(object):
|
|
|
812
812
|
consts.CONFIG_FIELD_ALIAS
|
|
813
813
|
]
|
|
814
814
|
depth = 1
|
|
815
|
-
calc_func =
|
|
815
|
+
calc_func = consts.CALC_FIELD_LENGTH
|
|
816
816
|
elif column_type in ["string", "!string"]:
|
|
817
|
-
calc_func =
|
|
817
|
+
calc_func = consts.CALC_FIELD_LENGTH
|
|
818
818
|
|
|
819
819
|
elif self._is_uuid(column_type, target_column_type):
|
|
820
820
|
calc_func = consts.CONFIG_CAST
|
|
821
821
|
cast_type = consts.CONFIG_CAST_UUID_STRING
|
|
822
822
|
|
|
823
823
|
elif column_type in ["binary", "!binary"]:
|
|
824
|
-
calc_func =
|
|
824
|
+
calc_func = consts.CALC_FIELD_BYTE_LENGTH
|
|
825
825
|
|
|
826
826
|
elif column_type in ["timestamp", "!timestamp", "date", "!date"]:
|
|
827
827
|
if (
|
|
@@ -841,7 +841,7 @@ class ConfigManager(object):
|
|
|
841
841
|
]
|
|
842
842
|
depth = 1
|
|
843
843
|
|
|
844
|
-
calc_func =
|
|
844
|
+
calc_func = consts.CALC_FIELD_EPOCH_SECONDS
|
|
845
845
|
if agg_type == consts.CONFIG_TYPE_SUM:
|
|
846
846
|
# It is possible to exceed int64 when summing epoch_seconds therefore cast to string.
|
|
847
847
|
# See issue 1391 for details.
|
|
@@ -1138,7 +1138,7 @@ class ConfigManager(object):
|
|
|
1138
1138
|
target_table: "ibis.expr.types.Table",
|
|
1139
1139
|
) -> dict:
|
|
1140
1140
|
"""Mutates col_config to contain any overrides. Also returns col_config for convenience."""
|
|
1141
|
-
if col_config["calc_type"] !=
|
|
1141
|
+
if col_config["calc_type"] != consts.CALC_FIELD_CAST:
|
|
1142
1142
|
return col_config
|
|
1143
1143
|
|
|
1144
1144
|
source_table_schema = {k: v for k, v in source_table.schema().items()}
|
|
@@ -1186,13 +1186,19 @@ class ConfigManager(object):
|
|
|
1186
1186
|
|
|
1187
1187
|
def _get_order_of_operations(self, calc_type: str) -> List[str]:
|
|
1188
1188
|
"""Return order of operations for row validation."""
|
|
1189
|
-
order_of_operations = [
|
|
1189
|
+
order_of_operations = [
|
|
1190
|
+
consts.CALC_FIELD_CAST,
|
|
1191
|
+
consts.CALC_FIELD_IFNULL,
|
|
1192
|
+
consts.CALC_FIELD_RSTRIP,
|
|
1193
|
+
]
|
|
1190
1194
|
if self.case_insensitive_match():
|
|
1191
|
-
order_of_operations.append(
|
|
1192
|
-
if calc_type ==
|
|
1193
|
-
order_of_operations.extend(
|
|
1194
|
-
|
|
1195
|
-
|
|
1195
|
+
order_of_operations.append(consts.CALC_FIELD_UPPER)
|
|
1196
|
+
if calc_type == consts.CALC_FIELD_HASH:
|
|
1197
|
+
order_of_operations.extend(
|
|
1198
|
+
[consts.CALC_FIELD_CONCAT, consts.CALC_FIELD_HASH]
|
|
1199
|
+
)
|
|
1200
|
+
elif calc_type == consts.CALC_FIELD_CONCAT:
|
|
1201
|
+
order_of_operations.append(consts.CALC_FIELD_CONCAT)
|
|
1196
1202
|
|
|
1197
1203
|
return order_of_operations
|
|
1198
1204
|
|
|
@@ -1241,7 +1247,7 @@ class ConfigManager(object):
|
|
|
1241
1247
|
previous_level = [x for x in casefold_source_columns.keys()]
|
|
1242
1248
|
else:
|
|
1243
1249
|
previous_level = [k for k, v in column_aliases.items() if v == i - 1]
|
|
1244
|
-
if calc in [
|
|
1250
|
+
if calc in [consts.CALC_FIELD_CONCAT, consts.CALC_FIELD_HASH]:
|
|
1245
1251
|
col = {}
|
|
1246
1252
|
col["source_reference"] = previous_level
|
|
1247
1253
|
col["target_reference"] = previous_level
|
{google_pso_data_validator-7.6.0 → google_pso_data_validator-7.7.0}/data_validation/consts.py
RENAMED
|
@@ -143,6 +143,7 @@ PROJECT_ID = "project_id"
|
|
|
143
143
|
TABLE_ID = "table_id"
|
|
144
144
|
GOOGLE_SERVICE_ACCOUNT_KEY_PATH = "google_service_account_key_path"
|
|
145
145
|
API_ENDPOINT = "api_endpoint"
|
|
146
|
+
STORAGE_API_ENDPOINT = "storage_api_endpoint"
|
|
146
147
|
|
|
147
148
|
# Result Handler Output Table Fields
|
|
148
149
|
VALIDATION_TYPE = "validation_type"
|
|
@@ -181,6 +182,11 @@ FAILED_SOURCE_NOT_IN_TARGET = "failed_rows_present_in_source_not_in_target"
|
|
|
181
182
|
FAILED_TARGET_NOT_IN_SOURCE = "failed_rows_present_in_target_not_in_source"
|
|
182
183
|
FAILED_PRESENT_IN_BOTH_TABLES = "failed_rows_present_in_both_source_and_target"
|
|
183
184
|
|
|
185
|
+
# Combiner only constants
|
|
186
|
+
COMBINER_TABLE_NAME = "dvt_table_name"
|
|
187
|
+
COMBINER_COLUMN_NAME = "dvt_column_name"
|
|
188
|
+
COMBINER_AGG_VALUE = "dvt_agg_value"
|
|
189
|
+
|
|
184
190
|
# SQL Template Formatting
|
|
185
191
|
# TODO: should this be managed in query_builder if that is the only place its used?
|
|
186
192
|
COUNT_STAR = "{count_star}"
|
|
@@ -239,3 +245,15 @@ MAX_CONCAT_COLUMNS_DEFAULTS = {
|
|
|
239
245
|
# Minimizing risk of: [Error 3556] Too many columns defined for this table.
|
|
240
246
|
"teradata": 500,
|
|
241
247
|
}
|
|
248
|
+
|
|
249
|
+
# CalculatedField expression constants.
|
|
250
|
+
CALC_FIELD_CAST = "cast"
|
|
251
|
+
CALC_FIELD_CONCAT = "concat"
|
|
252
|
+
CALC_FIELD_BYTE_LENGTH = "byte_length"
|
|
253
|
+
CALC_FIELD_EPOCH_SECONDS = "epoch_seconds"
|
|
254
|
+
CALC_FIELD_HASH = "hash"
|
|
255
|
+
CALC_FIELD_IFNULL = "ifnull"
|
|
256
|
+
CALC_FIELD_LENGTH = "length"
|
|
257
|
+
CALC_FIELD_PADDED_CHAR_LENGTH = "padded_char_length"
|
|
258
|
+
CALC_FIELD_RSTRIP = "rstrip"
|
|
259
|
+
CALC_FIELD_UPPER = "upper"
|
|
@@ -358,6 +358,14 @@ class CalculatedField(object):
|
|
|
358
358
|
fields,
|
|
359
359
|
)
|
|
360
360
|
|
|
361
|
+
@staticmethod
|
|
362
|
+
def padded_char_length(config, fields):
|
|
363
|
+
return CalculatedField(
|
|
364
|
+
ibis.expr.types.StringValue.padded_char_length,
|
|
365
|
+
config,
|
|
366
|
+
fields,
|
|
367
|
+
)
|
|
368
|
+
|
|
361
369
|
@staticmethod
|
|
362
370
|
def byte_length(config, fields):
|
|
363
371
|
return CalculatedField(
|
|
@@ -403,7 +411,7 @@ class CalculatedField(object):
|
|
|
403
411
|
@staticmethod
|
|
404
412
|
def custom(config, fields):
|
|
405
413
|
"""Returns a CalculatedField instance built for any custom ibis expression
|
|
406
|
-
|
|
414
|
+
e.g. 'ibis.expr.api.StringValue.replace'. For a list of supported functions,
|
|
407
415
|
see https://github.com/ibis-project/ibis/blob/1.4.0/ibis/expr/api.py
|
|
408
416
|
Args:
|
|
409
417
|
expr (Str): A custom ibis expression to be used as a calc field
|
|
@@ -65,7 +65,7 @@ class BigQueryResultHandler(BaseBackendResultHandler):
|
|
|
65
65
|
Explicit credentials to use in case default credentials
|
|
66
66
|
aren't working properly.
|
|
67
67
|
status_list (list): provided status to filter the results with
|
|
68
|
-
api_endpoint (str): BigQuery API endpoint (e.g. https://
|
|
68
|
+
api_endpoint (str): BigQuery API endpoint (e.g. https://bigquery-mypsc.p.googleapis.com)
|
|
69
69
|
text_format (str, optional):
|
|
70
70
|
This allows the user to influence the text results written via logger.debug.
|
|
71
71
|
See: https://github.com/GoogleCloudPlatform/professional-services-data-validator/issues/871
|
|
@@ -11,8 +11,9 @@
|
|
|
11
11
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
12
|
# See the License for the specific language governing permissions and
|
|
13
13
|
# limitations under the License.
|
|
14
|
-
import logging
|
|
15
14
|
from copy import deepcopy
|
|
15
|
+
import logging
|
|
16
|
+
from typing import TYPE_CHECKING
|
|
16
17
|
|
|
17
18
|
from data_validation import consts, metadata
|
|
18
19
|
from data_validation.clients import get_max_in_list_size
|
|
@@ -25,6 +26,9 @@ from data_validation.query_builder.query_builder import (
|
|
|
25
26
|
QueryBuilder,
|
|
26
27
|
)
|
|
27
28
|
|
|
29
|
+
if TYPE_CHECKING:
|
|
30
|
+
import ibis
|
|
31
|
+
|
|
28
32
|
|
|
29
33
|
def list_to_sublists(id_list: list, max_size: int) -> list:
|
|
30
34
|
"""Return a list of items as a list of lists based on a max list length of max_size."""
|
|
@@ -87,6 +91,23 @@ class ValidationBuilder(object):
|
|
|
87
91
|
else:
|
|
88
92
|
return FilterField.isin(column_name, in_list)
|
|
89
93
|
|
|
94
|
+
def _is_padded_char(
|
|
95
|
+
self,
|
|
96
|
+
client: "ibis.backends.base.BaseBackend",
|
|
97
|
+
raw_column_metadata: dict,
|
|
98
|
+
column_name: str,
|
|
99
|
+
) -> bool:
|
|
100
|
+
# Clients only need to implement _is_char_type_padded method if they support padded strings.
|
|
101
|
+
return (
|
|
102
|
+
(
|
|
103
|
+
client.is_char_type_padded(raw_column_metadata[column_name])
|
|
104
|
+
if raw_column_metadata.get(column_name)
|
|
105
|
+
else False
|
|
106
|
+
)
|
|
107
|
+
if hasattr(client, "is_char_type_padded") and raw_column_metadata
|
|
108
|
+
else False
|
|
109
|
+
)
|
|
110
|
+
|
|
90
111
|
def clone(self):
|
|
91
112
|
cloned_builder = ValidationBuilder(self.config_manager)
|
|
92
113
|
|
|
@@ -347,32 +368,63 @@ class ValidationBuilder(object):
|
|
|
347
368
|
threshold=self.config_manager.threshold,
|
|
348
369
|
)
|
|
349
370
|
|
|
350
|
-
def
|
|
371
|
+
def _get_calc_type(
|
|
372
|
+
self,
|
|
373
|
+
calc_field: dict,
|
|
374
|
+
column_name: str,
|
|
375
|
+
client: "ibis.backends.base.BaseBackend",
|
|
376
|
+
raw_data_types: dict,
|
|
377
|
+
) -> str:
|
|
378
|
+
if calc_field[
|
|
379
|
+
consts.CONFIG_TYPE
|
|
380
|
+
] == consts.CALC_FIELD_LENGTH and self._is_padded_char(
|
|
381
|
+
client,
|
|
382
|
+
raw_data_types,
|
|
383
|
+
column_name,
|
|
384
|
+
):
|
|
385
|
+
calc_type = consts.CALC_FIELD_PADDED_CHAR_LENGTH
|
|
386
|
+
else:
|
|
387
|
+
calc_type = calc_field[consts.CONFIG_TYPE]
|
|
388
|
+
# Check if valid calc field and return correct object.
|
|
389
|
+
if not hasattr(CalculatedField, calc_type):
|
|
390
|
+
raise Exception("Unknown Calculation Type: {}".format(calc_type))
|
|
391
|
+
return calc_type
|
|
392
|
+
|
|
393
|
+
def add_calc(self, calc_field: dict):
|
|
351
394
|
"""Add CalculatedField to Queries
|
|
352
395
|
|
|
353
396
|
Args:
|
|
354
397
|
calc_field (Dict): An object with source, target, and cast info
|
|
355
398
|
"""
|
|
356
|
-
#
|
|
399
|
+
# Prepare source and target payloads
|
|
357
400
|
source_config = deepcopy(calc_field)
|
|
358
401
|
source_fields = calc_field[consts.CONFIG_CALCULATED_SOURCE_COLUMNS]
|
|
359
402
|
target_config = deepcopy(calc_field)
|
|
360
403
|
target_fields = calc_field[consts.CONFIG_CALCULATED_TARGET_COLUMNS]
|
|
361
|
-
#
|
|
404
|
+
# Grab calc field metadata
|
|
362
405
|
alias = calc_field[consts.CONFIG_FIELD_ALIAS]
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
406
|
+
source_calc_type = self._get_calc_type(
|
|
407
|
+
calc_field,
|
|
408
|
+
source_fields[0],
|
|
409
|
+
self.source_client,
|
|
410
|
+
self.config_manager.get_source_raw_data_types(),
|
|
411
|
+
)
|
|
412
|
+
target_calc_type = self._get_calc_type(
|
|
413
|
+
calc_field,
|
|
414
|
+
target_fields[0],
|
|
415
|
+
self.target_client,
|
|
416
|
+
self.config_manager.get_target_raw_data_types(),
|
|
417
|
+
)
|
|
418
|
+
|
|
419
|
+
source_field = getattr(CalculatedField, source_calc_type)(
|
|
368
420
|
config=source_config, fields=source_fields
|
|
369
421
|
)
|
|
370
|
-
target_field = getattr(CalculatedField,
|
|
422
|
+
target_field = getattr(CalculatedField, target_calc_type)(
|
|
371
423
|
config=target_config, fields=target_fields
|
|
372
424
|
)
|
|
373
425
|
self.source_builder.add_calculated_field(source_field)
|
|
374
426
|
self.target_builder.add_calculated_field(target_field)
|
|
375
|
-
#
|
|
427
|
+
# Register calc field under alias
|
|
376
428
|
self.calculated_aliases[alias] = calc_field
|
|
377
429
|
|
|
378
430
|
def get_source_query(self):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: google-pso-data-validator
|
|
3
|
-
Version: 7.
|
|
3
|
+
Version: 7.7.0
|
|
4
4
|
Summary: A package to enable easy data validation
|
|
5
5
|
Author: PSO DVT Engineering team
|
|
6
6
|
Author-email: data-validator-eng@google.com
|
|
@@ -125,8 +125,10 @@ please review the [Connections](https://github.com/GoogleCloudPlatform/professio
|
|
|
125
125
|
### Running Validations
|
|
126
126
|
|
|
127
127
|
The CLI is the main interface to use this tool and it has several different
|
|
128
|
-
commands which can be used to create and run validations.
|
|
129
|
-
|
|
128
|
+
commands which can be used to create and run validations. DVT is designed to run in
|
|
129
|
+
an environment connected to GCP services, specifically, BigQuery, GCS and Secret manager.
|
|
130
|
+
If DVT is being run on-premises or in an environment with restricted access to GCP services, see
|
|
131
|
+
[running DVT at on-prem](#running-dvt-at-on-prem). Below are the command syntax and options for running validations.
|
|
130
132
|
|
|
131
133
|
Alternatives to running DVT in the CLI include deploying DVT to Cloud Run, Cloud Functions, or Airflow
|
|
132
134
|
([Examples Here](https://github.com/GoogleCloudPlatform/professional-services-data-validator/tree/develop/samples)). See the [Validation Logic](https://github.com/GoogleCloudPlatform/professional-services-data-validator#validation-logic) section
|
|
@@ -584,6 +586,11 @@ For example, this flag can be used as follows:
|
|
|
584
586
|
"target_query": "SELECT `hash__all`, `station_id`\nFROM ..."
|
|
585
587
|
}
|
|
586
588
|
```
|
|
589
|
+
#### Running DVT at on-prem
|
|
590
|
+
On-premises environments can have limited access to GCP services. DVT supports using BigQuery for storing validation results, GCS for storage and
|
|
591
|
+
the Secret Manager for secrets. You may also use BigQuery and Spanner as a source or target for validation. Service
|
|
592
|
+
APIs (i.e. bigquery.googleapis.com) may not always be accessible due to firewall restrictions. Work with your network
|
|
593
|
+
adminstrator to identify the way to access these services. They may set up a [Private Service Connect Endpoint](https://cloud.google.com/vpc/docs/about-accessing-google-apis-endpoints). DVT supports accessing source and target tables in Spanner and BigQuery via endpoints set up in your network. Connection Parameters for [Spanner](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#google-spanner) and [BigQuery](https://github.com/GoogleCloudPlatform/professional-services-data-validator/blob/develop/docs/connections.md#google-bigquery) outline regarding how to specify endpoints.
|
|
587
594
|
|
|
588
595
|
### Running DVT with YAML Configuration Files
|
|
589
596
|
|
|
@@ -43,8 +43,8 @@ google_pso_data_validator.egg-info/top_level.txt
|
|
|
43
43
|
third_party/ibis/ibis_addon/__init__.py
|
|
44
44
|
third_party/ibis/ibis_addon/api.py
|
|
45
45
|
third_party/ibis/ibis_addon/operations.py
|
|
46
|
-
third_party/ibis/
|
|
47
|
-
third_party/ibis/
|
|
46
|
+
third_party/ibis/ibis_bigquery/__init__.py
|
|
47
|
+
third_party/ibis/ibis_bigquery/api.py
|
|
48
48
|
third_party/ibis/ibis_cloud_spanner/__init__.py
|
|
49
49
|
third_party/ibis/ibis_cloud_spanner/api.py
|
|
50
50
|
third_party/ibis/ibis_cloud_spanner/client.py
|