dataops-testgen 2.2.0__py3-none-any.whl
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.
- dataops_testgen-2.2.0.dist-info/LICENSE +203 -0
- dataops_testgen-2.2.0.dist-info/METADATA +287 -0
- dataops_testgen-2.2.0.dist-info/NOTICE +5 -0
- dataops_testgen-2.2.0.dist-info/RECORD +270 -0
- dataops_testgen-2.2.0.dist-info/WHEEL +5 -0
- dataops_testgen-2.2.0.dist-info/entry_points.txt +2 -0
- dataops_testgen-2.2.0.dist-info/top_level.txt +1 -0
- testgen/__init__.py +0 -0
- testgen/__main__.py +770 -0
- testgen/commands/__init__.py +0 -0
- testgen/commands/queries/__init__.py +0 -0
- testgen/commands/queries/execute_cat_tests_query.py +95 -0
- testgen/commands/queries/execute_tests_query.py +160 -0
- testgen/commands/queries/generate_tests_query.py +94 -0
- testgen/commands/queries/profiling_query.py +366 -0
- testgen/commands/queries/test_parameter_validation_query.py +88 -0
- testgen/commands/run_execute_cat_tests.py +162 -0
- testgen/commands/run_execute_tests.py +168 -0
- testgen/commands/run_generate_tests.py +107 -0
- testgen/commands/run_get_entities.py +122 -0
- testgen/commands/run_launch_db_config.py +84 -0
- testgen/commands/run_observability_exporter.py +330 -0
- testgen/commands/run_profiling_bridge.py +495 -0
- testgen/commands/run_quick_start.py +168 -0
- testgen/commands/run_setup_profiling_tools.py +96 -0
- testgen/commands/run_test_definition.py +146 -0
- testgen/commands/run_test_parameter_validation.py +135 -0
- testgen/commands/run_upgrade_db_config.py +156 -0
- testgen/common/__init__.py +8 -0
- testgen/common/clean_sql.py +53 -0
- testgen/common/credentials.py +25 -0
- testgen/common/database/__init__.py +0 -0
- testgen/common/database/database_service.py +629 -0
- testgen/common/database/flavor/__init__.py +0 -0
- testgen/common/database/flavor/flavor_service.py +75 -0
- testgen/common/database/flavor/mssql_flavor_service.py +34 -0
- testgen/common/database/flavor/postgresql_flavor_service.py +5 -0
- testgen/common/database/flavor/redshift_flavor_service.py +22 -0
- testgen/common/database/flavor/snowflake_flavor_service.py +69 -0
- testgen/common/database/flavor/trino_flavor_service.py +21 -0
- testgen/common/date_service.py +68 -0
- testgen/common/display_service.py +85 -0
- testgen/common/docker_service.py +76 -0
- testgen/common/encrypt.py +55 -0
- testgen/common/get_pipeline_parms.py +57 -0
- testgen/common/logs.py +79 -0
- testgen/common/process_service.py +62 -0
- testgen/common/read_file.py +69 -0
- testgen/settings.py +440 -0
- testgen/template/dbsetup/010_create_base_schema.sql +2 -0
- testgen/template/dbsetup/020_create_standard_functions_sprocs.sql +179 -0
- testgen/template/dbsetup/030_initialize_new_schema_structure.sql +735 -0
- testgen/template/dbsetup/040_populate_new_schema_project.sql +59 -0
- testgen/template/dbsetup/050_populate_new_schema_metadata.sql +1517 -0
- testgen/template/dbsetup/060_create_standard_views.sql +248 -0
- testgen/template/dbsetup/070_create_default_users.sql +17 -0
- testgen/template/dbsetup/075_grant_role_rights.sql +43 -0
- testgen/template/dbsetup/080_set_current_revision.sql +5 -0
- testgen/template/dbupgrade/0100_incremental_upgrade.sql +5 -0
- testgen/template/dbupgrade/0101_incremental_upgrade.sql +15 -0
- testgen/template/dbupgrade/0102_incremental_upgrade.sql +4 -0
- testgen/template/dbupgrade/0103_incremental_upgrade.sql +22 -0
- testgen/template/dbupgrade/0104_incremental_upgrade.sql +44 -0
- testgen/template/dbupgrade/0105_incremental_upgrade.sql +1 -0
- testgen/template/dbupgrade/0106_incremental_upgrade.sql +5 -0
- testgen/template/dbupgrade/0107_incremental_upgrade.sql +3 -0
- testgen/template/dbupgrade_helpers/get_tg_revision.sql +2 -0
- testgen/template/exec_cat_tests/ex_cat_build_agg_table_tests.sql +116 -0
- testgen/template/exec_cat_tests/ex_cat_get_distinct_tables.sql +11 -0
- testgen/template/exec_cat_tests/ex_cat_results_parse.sql +69 -0
- testgen/template/exec_cat_tests/ex_cat_retrieve_agg_test_parms.sql +6 -0
- testgen/template/exec_cat_tests/ex_cat_test_query.sql +8 -0
- testgen/template/execution/ex_finalize_test_run_results.sql +37 -0
- testgen/template/execution/ex_get_tests_non_cat.sql +47 -0
- testgen/template/execution/ex_update_test_record_in_testrun_table.sql +27 -0
- testgen/template/execution/ex_write_test_record_to_testrun_table.sql +6 -0
- testgen/template/flavors/generic/exec_query_tests/ex_aggregate_match_no_drops_generic.sql +48 -0
- testgen/template/flavors/generic/exec_query_tests/ex_aggregate_match_num_incr_generic.sql +34 -0
- testgen/template/flavors/generic/exec_query_tests/ex_aggregate_match_percent_above_generic.sql +49 -0
- testgen/template/flavors/generic/exec_query_tests/ex_aggregate_match_percent_within_generic.sql +49 -0
- testgen/template/flavors/generic/exec_query_tests/ex_aggregate_match_same_generic.sql +49 -0
- testgen/template/flavors/generic/exec_query_tests/ex_custom_query_generic.sql +39 -0
- testgen/template/flavors/generic/exec_query_tests/ex_data_match_2way_generic.sql +58 -0
- testgen/template/flavors/generic/exec_query_tests/ex_data_match_generic.sql +44 -0
- testgen/template/flavors/generic/exec_query_tests/ex_prior_match_generic.sql +37 -0
- testgen/template/flavors/generic/exec_query_tests/ex_relative_entropy_generic.sql +53 -0
- testgen/template/flavors/generic/exec_query_tests/ex_window_match_no_drops_generic.sql +46 -0
- testgen/template/flavors/generic/exec_query_tests/ex_window_match_same_generic.sql +59 -0
- testgen/template/flavors/generic/profiling/contingency_counts.sql +3 -0
- testgen/template/flavors/generic/validate_tests/ex_get_project_column_list_generic.sql +3 -0
- testgen/template/flavors/mssql/exec_query_tests/ex_relative_entropy_mssql.sql +53 -0
- testgen/template/flavors/mssql/profiling/project_ddf_query_mssql.sql +35 -0
- testgen/template/flavors/mssql/profiling/project_profiling_query_mssql.yaml +246 -0
- testgen/template/flavors/mssql/profiling/project_secondary_profiling_query_mssql.sql +36 -0
- testgen/template/flavors/mssql/setup_profiling_tools/00_drop_existing_functions_mssql.sql +8 -0
- testgen/template/flavors/mssql/setup_profiling_tools/01_create_functions_mssql.sql +12 -0
- testgen/template/flavors/mssql/setup_profiling_tools/02_create_functions_mssql.sql +54 -0
- testgen/template/flavors/mssql/setup_profiling_tools/create_qc_schema_mssql.sql +4 -0
- testgen/template/flavors/mssql/setup_profiling_tools/grant_execute_privileges_mssql.sql +1 -0
- testgen/template/flavors/postgresql/exec_query_tests/ex_window_match_no_drops_postgresql.sql +46 -0
- testgen/template/flavors/postgresql/exec_query_tests/ex_window_match_same_postgresql.sql +59 -0
- testgen/template/flavors/postgresql/profiling/project_ddf_query_postgresql.sql +42 -0
- testgen/template/flavors/postgresql/profiling/project_profiling_query_postgresql.yaml +225 -0
- testgen/template/flavors/postgresql/profiling/project_secondary_profiling_query_postgresql.sql +28 -0
- testgen/template/flavors/postgresql/setup_profiling_tools/create_functions_postgresql.sql +157 -0
- testgen/template/flavors/postgresql/setup_profiling_tools/create_qc_schema_postgresql.sql +1 -0
- testgen/template/flavors/postgresql/setup_profiling_tools/grant_execute_privileges_postgresql.sql +2 -0
- testgen/template/flavors/redshift/profiling/project_ddf_query_redshift.sql +38 -0
- testgen/template/flavors/redshift/profiling/project_profiling_query_redshift.yaml +221 -0
- testgen/template/flavors/redshift/profiling/project_secondary_profiling_query_redshift.sql +29 -0
- testgen/template/flavors/redshift/setup_profiling_tools/create_functions_redshift.sql +115 -0
- testgen/template/flavors/redshift/setup_profiling_tools/create_qc_schema_redshift.sql +1 -0
- testgen/template/flavors/redshift/setup_profiling_tools/grant_execute_privileges_redshift.sql +2 -0
- testgen/template/flavors/snowflake/profiling/project_ddf_query_snowflake.sql +38 -0
- testgen/template/flavors/snowflake/profiling/project_profiling_query_snowflake.yaml +220 -0
- testgen/template/flavors/snowflake/profiling/project_secondary_profiling_query_snowflake.sql +29 -0
- testgen/template/flavors/snowflake/setup_profiling_tools/create_functions_snowflake.sql +69 -0
- testgen/template/flavors/snowflake/setup_profiling_tools/create_qc_schema_snowflake.sql +1 -0
- testgen/template/flavors/snowflake/setup_profiling_tools/grant_execute_privileges_snowflake.sql +6 -0
- testgen/template/flavors/trino/profiling/project_profiling_query_trino.yaml +219 -0
- testgen/template/flavors/trino/setup_profiling_tools/create_functions_trino.sql +92 -0
- testgen/template/flavors/trino/setup_profiling_tools/create_qc_schema_trino.sql +1 -0
- testgen/template/gen_funny_cat_tests/gen_test_constant.sql +104 -0
- testgen/template/gen_funny_cat_tests/gen_test_distinct_value_ct.sql +98 -0
- testgen/template/gen_funny_cat_tests/gen_test_row_ct.sql +57 -0
- testgen/template/gen_funny_cat_tests/gen_test_row_ct_pct.sql +59 -0
- testgen/template/generation/gen_delete_old_tests.sql +5 -0
- testgen/template/generation/gen_insert_test_suite.sql +5 -0
- testgen/template/generation/gen_retrieve_or_insert_test_suite.sql +58 -0
- testgen/template/generation/gen_standard_test_type_list.sql +13 -0
- testgen/template/generation/gen_standard_tests.sql +48 -0
- testgen/template/get_entities/get_connection.sql +21 -0
- testgen/template/get_entities/get_connections_list.sql +9 -0
- testgen/template/get_entities/get_latest.sql +4 -0
- testgen/template/get_entities/get_profile.sql +12 -0
- testgen/template/get_entities/get_profile_info.sql +17 -0
- testgen/template/get_entities/get_profile_list.sql +17 -0
- testgen/template/get_entities/get_profile_screen.sql +275 -0
- testgen/template/get_entities/get_project_list.sql +6 -0
- testgen/template/get_entities/get_table_group_list.sql +10 -0
- testgen/template/get_entities/get_test_generation_list.sql +18 -0
- testgen/template/get_entities/get_test_info.sql +41 -0
- testgen/template/get_entities/get_test_results_for_run_cli.sql +16 -0
- testgen/template/get_entities/get_test_run_list.sql +24 -0
- testgen/template/get_entities/get_test_suite.sql +13 -0
- testgen/template/get_entities/get_test_suite_list.sql +18 -0
- testgen/template/get_entities/list_test_types.sql +4 -0
- testgen/template/observability/get_event_data.sql +23 -0
- testgen/template/observability/get_test_results.sql +41 -0
- testgen/template/observability/update_test_results_exported_to_observability.sql +12 -0
- testgen/template/parms/parms_profiling.sql +34 -0
- testgen/template/parms/parms_test_execution.sql +13 -0
- testgen/template/parms/parms_test_gen.sql +23 -0
- testgen/template/profiling/contingency_columns.sql +7 -0
- testgen/template/profiling/datatype_suggestions.sql +56 -0
- testgen/template/profiling/functional_datatype.sql +523 -0
- testgen/template/profiling/functional_tabletype_stage.sql +48 -0
- testgen/template/profiling/functional_tabletype_update.sql +8 -0
- testgen/template/profiling/pii_flag.sql +133 -0
- testgen/template/profiling/profile_anomalies_screen_column.sql +22 -0
- testgen/template/profiling/profile_anomalies_screen_multi_column.sql +58 -0
- testgen/template/profiling/profile_anomalies_screen_table.sql +22 -0
- testgen/template/profiling/profile_anomalies_screen_table_dates.sql +30 -0
- testgen/template/profiling/profile_anomalies_screen_variants.sql +40 -0
- testgen/template/profiling/profile_anomaly_types_get.sql +3 -0
- testgen/template/profiling/project_get_table_sample_count.sql +22 -0
- testgen/template/profiling/project_profile_run_record_insert.sql +8 -0
- testgen/template/profiling/project_profile_run_record_update.sql +5 -0
- testgen/template/profiling/project_profile_run_record_update_status.sql +5 -0
- testgen/template/profiling/project_update_profile_results_to_estimates.sql +32 -0
- testgen/template/profiling/refresh_anomalies.sql +33 -0
- testgen/template/profiling/refresh_data_chars_from_profiling.sql +156 -0
- testgen/template/profiling/secondary_profiling_columns.sql +12 -0
- testgen/template/profiling/secondary_profiling_delete.sql +4 -0
- testgen/template/profiling/secondary_profiling_update.sql +18 -0
- testgen/template/quick_start/populate_target_data.sql +1077 -0
- testgen/template/quick_start/recreate_target_data_schema.sql +167 -0
- testgen/template/quick_start/update_target_data.sql +100 -0
- testgen/template/updates/create_tmp_test_definition.sql +19 -0
- testgen/template/updates/get_test_def_parms.sql +38 -0
- testgen/template/updates/populate_stg_test_definitions.sql +184 -0
- testgen/template/validate_tests/ex_disable_tests_test_definitions.sql +5 -0
- testgen/template/validate_tests/ex_flag_tests_test_definitions.sql +64 -0
- testgen/template/validate_tests/ex_get_project_column_list_generic.sql +3 -0
- testgen/template/validate_tests/ex_get_test_column_list_tg.sql +65 -0
- testgen/template/validate_tests/ex_write_test_val_errors.sql +22 -0
- testgen/ui/__init__.py +0 -0
- testgen/ui/app.py +98 -0
- testgen/ui/assets/dk_logo.svg +46 -0
- testgen/ui/assets/question_mark.png +0 -0
- testgen/ui/assets/scripts.js +68 -0
- testgen/ui/assets/style.css +140 -0
- testgen/ui/bootstrap.py +109 -0
- testgen/ui/components/__init__.py +0 -0
- testgen/ui/components/frontend/css/KFOlCnqEu92Fr1MmEU9fBBc4.woff2 +0 -0
- testgen/ui/components/frontend/css/KFOlCnqEu92Fr1MmEU9fChc4EsA.woff2 +0 -0
- testgen/ui/components/frontend/css/KFOmCnqEu92Fr1Mu4mxK.woff2 +0 -0
- testgen/ui/components/frontend/css/KFOmCnqEu92Fr1Mu7GxKOzY.woff2 +0 -0
- testgen/ui/components/frontend/css/material-symbols-rounded.css +24 -0
- testgen/ui/components/frontend/css/material-symbols-rounded.woff2 +0 -0
- testgen/ui/components/frontend/css/roboto-font-faces.css +35 -0
- testgen/ui/components/frontend/css/shared.css +36 -0
- testgen/ui/components/frontend/img/dk_logo.svg +46 -0
- testgen/ui/components/frontend/index.html +17 -0
- testgen/ui/components/frontend/js/components/breadcrumbs.js +86 -0
- testgen/ui/components/frontend/js/components/button.js +66 -0
- testgen/ui/components/frontend/js/components/location.js +62 -0
- testgen/ui/components/frontend/js/components/select.js +75 -0
- testgen/ui/components/frontend/js/components/sidebar.js +358 -0
- testgen/ui/components/frontend/js/main.js +99 -0
- testgen/ui/components/frontend/js/streamlit.js +19 -0
- testgen/ui/components/frontend/js/van.min.js +1 -0
- testgen/ui/components/utils/__init__.py +0 -0
- testgen/ui/components/utils/callbacks.py +51 -0
- testgen/ui/components/utils/component.py +13 -0
- testgen/ui/components/widgets/__init__.py +6 -0
- testgen/ui/components/widgets/breadcrumbs.py +32 -0
- testgen/ui/components/widgets/location.py +65 -0
- testgen/ui/components/widgets/modal.py +97 -0
- testgen/ui/components/widgets/sidebar.py +69 -0
- testgen/ui/navigation/__init__.py +0 -0
- testgen/ui/navigation/menu.py +42 -0
- testgen/ui/navigation/page.py +20 -0
- testgen/ui/navigation/router.py +63 -0
- testgen/ui/queries/__init__.py +0 -0
- testgen/ui/queries/authentication_queries.py +47 -0
- testgen/ui/queries/connection_queries.py +121 -0
- testgen/ui/queries/profiling_queries.py +148 -0
- testgen/ui/queries/project_queries.py +9 -0
- testgen/ui/queries/table_group_queries.py +186 -0
- testgen/ui/queries/test_definition_queries.py +270 -0
- testgen/ui/queries/test_run_queries.py +32 -0
- testgen/ui/queries/test_suite_queries.py +145 -0
- testgen/ui/scripts/__init__.py +0 -0
- testgen/ui/scripts/patch_streamlit.py +111 -0
- testgen/ui/services/__init__.py +0 -0
- testgen/ui/services/authentication_service.py +119 -0
- testgen/ui/services/connection_service.py +220 -0
- testgen/ui/services/database_service.py +282 -0
- testgen/ui/services/form_service.py +1008 -0
- testgen/ui/services/javascript_service.py +44 -0
- testgen/ui/services/query_service.py +316 -0
- testgen/ui/services/string_service.py +12 -0
- testgen/ui/services/table_group_service.py +130 -0
- testgen/ui/services/test_definition_service.py +117 -0
- testgen/ui/services/test_run_service.py +13 -0
- testgen/ui/services/test_suite_service.py +76 -0
- testgen/ui/services/toolbar_service.py +77 -0
- testgen/ui/session.py +46 -0
- testgen/ui/views/__init__.py +0 -0
- testgen/ui/views/app_log_modal.py +92 -0
- testgen/ui/views/connections.py +72 -0
- testgen/ui/views/connections_base.py +367 -0
- testgen/ui/views/login.py +40 -0
- testgen/ui/views/not_found.py +16 -0
- testgen/ui/views/overview.py +34 -0
- testgen/ui/views/profiling_anomalies.py +501 -0
- testgen/ui/views/profiling_details.py +335 -0
- testgen/ui/views/profiling_modal.py +40 -0
- testgen/ui/views/profiling_results.py +206 -0
- testgen/ui/views/profiling_summary.py +177 -0
- testgen/ui/views/project_settings.py +74 -0
- testgen/ui/views/table_groups.py +530 -0
- testgen/ui/views/test_definitions.py +1020 -0
- testgen/ui/views/test_results.py +908 -0
- testgen/ui/views/test_runs.py +195 -0
- testgen/ui/views/test_suites.py +545 -0
- testgen/utils/__init__.py +0 -0
- testgen/utils/plugins.py +17 -0
- testgen/utils/singleton.py +14 -0
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import time
|
|
3
|
+
|
|
4
|
+
import streamlit as st
|
|
5
|
+
|
|
6
|
+
import testgen.ui.services.database_service as db
|
|
7
|
+
import testgen.ui.services.form_service as fm
|
|
8
|
+
from testgen.commands.run_setup_profiling_tools import get_setup_profiling_tools_queries
|
|
9
|
+
from testgen.common.database.database_service import empty_cache
|
|
10
|
+
from testgen.ui.services import authentication_service, connection_service
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def show_create_qc_schema_modal(modal, selected):
|
|
14
|
+
with modal.container():
|
|
15
|
+
fm.render_modal_header("Configure QC Utility Schema", None)
|
|
16
|
+
selected_connection = selected
|
|
17
|
+
connection_id = selected_connection["connection_id"]
|
|
18
|
+
project_qc_schema = selected_connection["project_qc_schema"]
|
|
19
|
+
sql_flavor = selected_connection["sql_flavor"]
|
|
20
|
+
user = selected_connection["project_user"]
|
|
21
|
+
|
|
22
|
+
create_qc_schema = st.toggle("Create QC Utility Schema", value=True)
|
|
23
|
+
grant_privileges = st.toggle("Grant access privileges to TestGen user", value=True)
|
|
24
|
+
|
|
25
|
+
user_role = None
|
|
26
|
+
|
|
27
|
+
# TODO ALEX: This textbox may be needed if we want to grant permissions to user role
|
|
28
|
+
# if sql_flavor == "snowflake":
|
|
29
|
+
# user_role_textbox_label = f"Primary role for database user {user}"
|
|
30
|
+
# user_role = st.text_input(label=user_role_textbox_label, max_chars=100)
|
|
31
|
+
|
|
32
|
+
admin_credentials_expander = st.expander("Admin credential options", expanded=True)
|
|
33
|
+
with admin_credentials_expander:
|
|
34
|
+
admin_connection_option_index = 0
|
|
35
|
+
admin_connection_options = ["Do not use admin credentials", "Use admin credentials with Password"]
|
|
36
|
+
if sql_flavor == "snowflake":
|
|
37
|
+
admin_connection_options.append("Use admin credentials with Key-Pair")
|
|
38
|
+
|
|
39
|
+
admin_connection_option = st.radio(
|
|
40
|
+
"Admin credential options",
|
|
41
|
+
label_visibility="hidden",
|
|
42
|
+
options=admin_connection_options,
|
|
43
|
+
index=admin_connection_option_index,
|
|
44
|
+
horizontal=True,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
st.markdown("</p> </br>", unsafe_allow_html=True)
|
|
48
|
+
|
|
49
|
+
db_user = None
|
|
50
|
+
db_password = None
|
|
51
|
+
admin_private_key_passphrase = None
|
|
52
|
+
admin_private_key = None
|
|
53
|
+
if admin_connection_option == admin_connection_options[0]:
|
|
54
|
+
st.markdown(":orange[User created in the connection dialog will be used.]")
|
|
55
|
+
else:
|
|
56
|
+
db_user = st.text_input(label="Admin db user", max_chars=40)
|
|
57
|
+
if admin_connection_option == admin_connection_options[1]:
|
|
58
|
+
db_password = st.text_input(
|
|
59
|
+
label="Admin db password", max_chars=40, type="password"
|
|
60
|
+
)
|
|
61
|
+
st.markdown(":orange[Note: Admin credentials are not stored, are only used for this operation.]")
|
|
62
|
+
|
|
63
|
+
if len(admin_connection_options) > 2 and admin_connection_option == admin_connection_options[2]:
|
|
64
|
+
admin_private_key_passphrase = st.text_input(
|
|
65
|
+
label="Private Key Passphrase",
|
|
66
|
+
key="create-qc-schema-private-key-password",
|
|
67
|
+
type="password",
|
|
68
|
+
max_chars=200,
|
|
69
|
+
help="Passphrase used while creating the private Key (leave empty if not applicable)",
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
admin_uploaded_file = st.file_uploader("Upload private key (rsa_key.p8)", key="admin-uploaded-file")
|
|
73
|
+
if admin_uploaded_file:
|
|
74
|
+
admin_private_key = admin_uploaded_file.getvalue().decode("utf-8")
|
|
75
|
+
|
|
76
|
+
st.markdown(":orange[Note: Admin credentials are not stored, are only used for this operation.]")
|
|
77
|
+
|
|
78
|
+
submit = st.button("Update Configuration")
|
|
79
|
+
|
|
80
|
+
if submit:
|
|
81
|
+
empty_cache()
|
|
82
|
+
script_expander = st.expander("Script Details")
|
|
83
|
+
|
|
84
|
+
operation_status = st.empty()
|
|
85
|
+
operation_status.info(f"Configuring QC Utility Schema '{project_qc_schema}'...")
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
skip_granting_privileges = not grant_privileges
|
|
89
|
+
queries = get_setup_profiling_tools_queries(sql_flavor, create_qc_schema, skip_granting_privileges, project_qc_schema, user, user_role)
|
|
90
|
+
with script_expander:
|
|
91
|
+
st.code(
|
|
92
|
+
os.linesep.join(queries),
|
|
93
|
+
language="sql",
|
|
94
|
+
line_numbers=True)
|
|
95
|
+
|
|
96
|
+
connection_service.create_qc_schema(
|
|
97
|
+
connection_id,
|
|
98
|
+
create_qc_schema,
|
|
99
|
+
db_user if db_user else None,
|
|
100
|
+
db_password if db_password else None,
|
|
101
|
+
skip_granting_privileges,
|
|
102
|
+
admin_private_key_passphrase=admin_private_key_passphrase,
|
|
103
|
+
admin_private_key=admin_private_key,
|
|
104
|
+
user_role=user_role,
|
|
105
|
+
)
|
|
106
|
+
operation_status.empty()
|
|
107
|
+
operation_status.success("Operation has finished successfully.")
|
|
108
|
+
|
|
109
|
+
except Exception as e:
|
|
110
|
+
operation_status.empty()
|
|
111
|
+
operation_status.error("Error configuring QC Utility Schema.")
|
|
112
|
+
error_message = e.args[0]
|
|
113
|
+
st.text_area("Error Details", value=error_message)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def show_connection(connection_modal, selected_connection, mode, project_code, show_header=True):
|
|
117
|
+
if show_header:
|
|
118
|
+
fm.render_modal_header("Add Connection" if mode == "add" else "Edit Connection", None)
|
|
119
|
+
flavor_options = ["redshift", "snowflake", "mssql", "postgresql"]
|
|
120
|
+
connection_options = ["Connect by Password", "Connect by Key-Pair"]
|
|
121
|
+
|
|
122
|
+
left_column, right_column = st.columns([0.75, 0.25])
|
|
123
|
+
mid_column = st.columns(1)[0]
|
|
124
|
+
toggle_left_column, toggle_right_column = st.columns([0.25, 0.75])
|
|
125
|
+
bottom_left_column, bottom_right_column = st.columns([0.25, 0.75])
|
|
126
|
+
button_left_column, button_right_column, button_remaining_column = st.columns([0.20, 0.20, 0.60])
|
|
127
|
+
|
|
128
|
+
connection_id = selected_connection["connection_id"] if mode == "edit" else None
|
|
129
|
+
connection_name = selected_connection["connection_name"] if mode == "edit" else ""
|
|
130
|
+
sql_flavor_index = flavor_options.index(selected_connection["sql_flavor"]) if mode == "edit" else 0
|
|
131
|
+
project_port = selected_connection["project_port"] if mode == "edit" else ""
|
|
132
|
+
project_host = selected_connection["project_host"] if mode == "edit" else ""
|
|
133
|
+
project_db = selected_connection["project_db"] if mode == "edit" else ""
|
|
134
|
+
project_user = selected_connection["project_user"] if mode == "edit" else ""
|
|
135
|
+
url = selected_connection["url"] if mode == "edit" else ""
|
|
136
|
+
project_qc_schema = selected_connection["project_qc_schema"] if mode == "edit" else "qc"
|
|
137
|
+
password = selected_connection["password"] if mode == "edit" else ""
|
|
138
|
+
max_threads = selected_connection["max_threads"] if mode == "edit" else 4
|
|
139
|
+
max_query_chars = selected_connection["max_query_chars"] if mode == "edit" else 10000
|
|
140
|
+
connect_by_url = selected_connection["connect_by_url"] if mode == "edit" else False
|
|
141
|
+
connect_by_key = selected_connection["connect_by_key"] if mode == "edit" else False
|
|
142
|
+
connection_option_index = 1 if connect_by_key else 0
|
|
143
|
+
private_key = selected_connection["private_key"] if mode == "edit" else None
|
|
144
|
+
private_key_passphrase = selected_connection["private_key_passphrase"] if mode == "edit" else ""
|
|
145
|
+
|
|
146
|
+
new_connection = {
|
|
147
|
+
"connection_id": connection_id,
|
|
148
|
+
"project_code": project_code,
|
|
149
|
+
"private_key": private_key,
|
|
150
|
+
"private_key_passphrase": private_key_passphrase,
|
|
151
|
+
"password": password,
|
|
152
|
+
"url": url,
|
|
153
|
+
"max_threads": right_column.number_input(
|
|
154
|
+
label="Max Threads (Advanced Tuning)",
|
|
155
|
+
min_value=1,
|
|
156
|
+
max_value=8,
|
|
157
|
+
value=max_threads,
|
|
158
|
+
help="Maximum number of concurrent threads that run tests. Default values should be retained unless test queries are failing.",
|
|
159
|
+
),
|
|
160
|
+
"max_query_chars": right_column.number_input(
|
|
161
|
+
label="Max Expression Length (Advanced Tuning)",
|
|
162
|
+
min_value=500,
|
|
163
|
+
max_value=14000,
|
|
164
|
+
value=max_query_chars,
|
|
165
|
+
help="Some tests are consolidated into queries for maximum performance. Default values should be retained unless test queries are failing.",
|
|
166
|
+
),
|
|
167
|
+
"connection_name": left_column.text_input(
|
|
168
|
+
label="Connection Name",
|
|
169
|
+
max_chars=40,
|
|
170
|
+
value=connection_name,
|
|
171
|
+
help="Your name for this connection. Can be any text.",
|
|
172
|
+
),
|
|
173
|
+
"sql_flavor": left_column.selectbox(
|
|
174
|
+
label="SQL Flavor",
|
|
175
|
+
options=flavor_options,
|
|
176
|
+
index=sql_flavor_index,
|
|
177
|
+
help="The type of database server that you will connect to. This determines TestGen's drivers and SQL dialect.",
|
|
178
|
+
)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if "disable_url_widgets" not in st.session_state:
|
|
182
|
+
st.session_state.disable_url_widgets = connect_by_url
|
|
183
|
+
|
|
184
|
+
new_connection["project_port"] = right_column.text_input(label="Port", max_chars=5, value=project_port, disabled=st.session_state.disable_url_widgets)
|
|
185
|
+
new_connection["project_host"] = left_column.text_input(label="Host", max_chars=250, value=project_host, disabled=st.session_state.disable_url_widgets)
|
|
186
|
+
new_connection["project_db"] = left_column.text_input(
|
|
187
|
+
label="Database",
|
|
188
|
+
max_chars=100,
|
|
189
|
+
value=project_db,
|
|
190
|
+
help="The name of the database defined on your host where your schemas and tables is present.",
|
|
191
|
+
disabled=st.session_state.disable_url_widgets,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
new_connection["project_user"] = left_column.text_input(
|
|
195
|
+
label="User",
|
|
196
|
+
max_chars=50,
|
|
197
|
+
value=project_user,
|
|
198
|
+
help="Username to connect to your database.",
|
|
199
|
+
)
|
|
200
|
+
|
|
201
|
+
new_connection["project_qc_schema"] = right_column.text_input(
|
|
202
|
+
label="QC Utility Schema",
|
|
203
|
+
max_chars=50,
|
|
204
|
+
value=project_qc_schema,
|
|
205
|
+
help="The name of the schema on your database that will contain TestGen's profiling functions.",
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
if new_connection["sql_flavor"] == "snowflake":
|
|
209
|
+
mid_column.divider()
|
|
210
|
+
|
|
211
|
+
connection_option = mid_column.radio(
|
|
212
|
+
"Connection options",
|
|
213
|
+
options=connection_options,
|
|
214
|
+
index=connection_option_index,
|
|
215
|
+
horizontal=True,
|
|
216
|
+
help="Connection strategy",
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
new_connection["connect_by_key"] = connection_option == "Connect by Key-Pair"
|
|
220
|
+
password_column = mid_column
|
|
221
|
+
else:
|
|
222
|
+
new_connection["connect_by_key"] = False
|
|
223
|
+
password_column = left_column
|
|
224
|
+
|
|
225
|
+
uploaded_file = None
|
|
226
|
+
|
|
227
|
+
if new_connection["connect_by_key"]:
|
|
228
|
+
new_connection["private_key_passphrase"] = mid_column.text_input(
|
|
229
|
+
label="Private Key Passphrase",
|
|
230
|
+
type="password",
|
|
231
|
+
max_chars=200,
|
|
232
|
+
value=private_key_passphrase,
|
|
233
|
+
help="Passphrase used while creating the private Key (leave empty if not applicable)",
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
uploaded_file = mid_column.file_uploader("Upload private key (rsa_key.p8)")
|
|
237
|
+
else:
|
|
238
|
+
new_connection["password"] = password_column.text_input(
|
|
239
|
+
label="Password",
|
|
240
|
+
max_chars=50,
|
|
241
|
+
type="password",
|
|
242
|
+
value=password,
|
|
243
|
+
help="Password to connect to your database.",
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
mid_column.divider()
|
|
247
|
+
|
|
248
|
+
url_override_help_text = "If this switch is set to on, the connection string will be driven by the field below. "
|
|
249
|
+
if new_connection["connect_by_key"]:
|
|
250
|
+
url_override_help_text += "Only user name will be passed per the relevant fields above."
|
|
251
|
+
else:
|
|
252
|
+
url_override_help_text += "Only user name and password will be passed per the relevant fields above."
|
|
253
|
+
|
|
254
|
+
def on_connect_by_url_change():
|
|
255
|
+
value = st.session_state.connect_by_url_toggle
|
|
256
|
+
st.session_state.disable_url_widgets = value
|
|
257
|
+
|
|
258
|
+
new_connection["connect_by_url"] = toggle_left_column.toggle(
|
|
259
|
+
"URL override",
|
|
260
|
+
value=connect_by_url,
|
|
261
|
+
key="connect_by_url_toggle",
|
|
262
|
+
help=url_override_help_text,
|
|
263
|
+
on_change=on_connect_by_url_change
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
if new_connection["connect_by_url"]:
|
|
267
|
+
connection_string = connection_service.form_overwritten_connection_url(new_connection)
|
|
268
|
+
connection_string_beginning, connection_string_end = connection_string.split("@", 1)
|
|
269
|
+
connection_string_header = connection_string_beginning + "@"
|
|
270
|
+
connection_string_header = connection_string_header.replace("%3E", ">")
|
|
271
|
+
connection_string_header = connection_string_header.replace("%3C", "<")
|
|
272
|
+
|
|
273
|
+
if not url:
|
|
274
|
+
url = connection_string_end
|
|
275
|
+
|
|
276
|
+
new_connection["url"] = bottom_right_column.text_input(
|
|
277
|
+
label="URL Suffix",
|
|
278
|
+
max_chars=200,
|
|
279
|
+
value=url,
|
|
280
|
+
help="Provide a connection string directly. This will override connection parameters if the 'Connect by URL' switch is set.",
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
bottom_left_column.text_input(label="URL Prefix", value=connection_string_header, disabled=True)
|
|
284
|
+
|
|
285
|
+
bottom_left_column.markdown("</p> </br>", unsafe_allow_html=True)
|
|
286
|
+
|
|
287
|
+
submit_button_text = "Save" if mode == "edit" else "Add Connection"
|
|
288
|
+
submit = button_left_column.button(
|
|
289
|
+
submit_button_text, disabled=authentication_service.current_user_has_read_role()
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
if submit:
|
|
293
|
+
if not new_connection["password"] and not new_connection["connect_by_key"]:
|
|
294
|
+
st.error("Enter a valid password.")
|
|
295
|
+
else:
|
|
296
|
+
if uploaded_file:
|
|
297
|
+
new_connection["private_key"] = uploaded_file.getvalue().decode("utf-8")
|
|
298
|
+
|
|
299
|
+
if mode == "edit":
|
|
300
|
+
connection_service.edit_connection(new_connection)
|
|
301
|
+
else:
|
|
302
|
+
connection_service.add_connection(new_connection)
|
|
303
|
+
success_message = (
|
|
304
|
+
"Changes have been saved successfully. "
|
|
305
|
+
if mode == "edit"
|
|
306
|
+
else "New connection added successfully. "
|
|
307
|
+
)
|
|
308
|
+
st.success(success_message)
|
|
309
|
+
time.sleep(1)
|
|
310
|
+
if connection_modal:
|
|
311
|
+
connection_modal.close()
|
|
312
|
+
st.experimental_rerun()
|
|
313
|
+
|
|
314
|
+
test_left_column, test_mid_column, test_right_column = st.columns([0.15, 0.15, 0.70])
|
|
315
|
+
test_connection = button_right_column.button("Test Connection")
|
|
316
|
+
|
|
317
|
+
connection_status = test_right_column.empty()
|
|
318
|
+
|
|
319
|
+
if test_connection:
|
|
320
|
+
if mode == "add" and new_connection["connect_by_key"]:
|
|
321
|
+
connection_status.empty()
|
|
322
|
+
connection_status.error(
|
|
323
|
+
"Please add the connection before testing it (so that we can get your private key file).")
|
|
324
|
+
else:
|
|
325
|
+
empty_cache()
|
|
326
|
+
connection_status.empty()
|
|
327
|
+
connection_status.info("Testing the connection...")
|
|
328
|
+
try:
|
|
329
|
+
sql_query = "select 1;"
|
|
330
|
+
results = db.retrieve_target_db_data(
|
|
331
|
+
new_connection["sql_flavor"],
|
|
332
|
+
new_connection["project_host"],
|
|
333
|
+
new_connection["project_port"],
|
|
334
|
+
new_connection["project_db"],
|
|
335
|
+
new_connection["project_user"],
|
|
336
|
+
new_connection["password"],
|
|
337
|
+
new_connection["url"],
|
|
338
|
+
new_connection["connect_by_url"],
|
|
339
|
+
new_connection["connect_by_key"],
|
|
340
|
+
new_connection["private_key"],
|
|
341
|
+
new_connection["private_key_passphrase"],
|
|
342
|
+
sql_query,
|
|
343
|
+
)
|
|
344
|
+
if len(results) == 1 and results[0][0] == 1:
|
|
345
|
+
qc_error_message = "The connection was successful, but there is an issue with the QC Utility Schema"
|
|
346
|
+
try:
|
|
347
|
+
qc_results = connection_service.test_qc_connection(project_code, new_connection)
|
|
348
|
+
if not all(qc_results):
|
|
349
|
+
error_message = f"QC Utility Schema confirmation failed. details: {qc_results}"
|
|
350
|
+
connection_status.empty()
|
|
351
|
+
connection_status.error(qc_error_message)
|
|
352
|
+
st.text_area("Connection Error Details", value=error_message)
|
|
353
|
+
else:
|
|
354
|
+
connection_status.empty()
|
|
355
|
+
connection_status.success("The connection was successful.")
|
|
356
|
+
except Exception as e:
|
|
357
|
+
connection_status.empty()
|
|
358
|
+
connection_status.error(qc_error_message)
|
|
359
|
+
error_message = e.args[0]
|
|
360
|
+
st.text_area("Connection Error Details", value=error_message)
|
|
361
|
+
else:
|
|
362
|
+
test_right_column.error("Error completing a query to the database server.")
|
|
363
|
+
except Exception as e:
|
|
364
|
+
connection_status.empty()
|
|
365
|
+
connection_status.error("Error attempting the Connection.")
|
|
366
|
+
error_message = e.args[0]
|
|
367
|
+
st.text_area("Connection Error Details", value=error_message)
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
import streamlit as st
|
|
4
|
+
import streamlit_authenticator as stauth
|
|
5
|
+
|
|
6
|
+
from testgen.ui.navigation.page import Page
|
|
7
|
+
from testgen.ui.services import authentication_service, javascript_service
|
|
8
|
+
from testgen.ui.session import session
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class LoginPage(Page):
|
|
12
|
+
path = "login"
|
|
13
|
+
can_activate: typing.ClassVar = [
|
|
14
|
+
lambda: not session.authentication_status or "overview",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
def render(self) -> None:
|
|
18
|
+
auth_data = authentication_service.get_auth_data()
|
|
19
|
+
|
|
20
|
+
authenticator = stauth.Authenticate(
|
|
21
|
+
auth_data["credentials"],
|
|
22
|
+
auth_data["cookie"]["name"],
|
|
23
|
+
auth_data["cookie"]["key"],
|
|
24
|
+
auth_data["cookie"]["expiry_days"],
|
|
25
|
+
auth_data["preauthorized"],
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
name, authentication_status, username = authenticator.login("Login", "main")
|
|
29
|
+
|
|
30
|
+
if authentication_status is False:
|
|
31
|
+
st.error("Username or password is incorrect.")
|
|
32
|
+
|
|
33
|
+
if authentication_status is None:
|
|
34
|
+
st.warning("Please enter your username and password.")
|
|
35
|
+
javascript_service.clear_component_states()
|
|
36
|
+
|
|
37
|
+
session.authentication_status = authentication_status
|
|
38
|
+
|
|
39
|
+
if authentication_status:
|
|
40
|
+
authentication_service.start_user_session(name, username)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import typing
|
|
2
|
+
|
|
3
|
+
import streamlit as st
|
|
4
|
+
|
|
5
|
+
from testgen.ui.navigation.page import Page
|
|
6
|
+
from testgen.ui.session import session
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class NotFoundPage(Page):
|
|
10
|
+
path = "404"
|
|
11
|
+
can_activate: typing.ClassVar = [
|
|
12
|
+
lambda: session.authentication_status or "login",
|
|
13
|
+
]
|
|
14
|
+
|
|
15
|
+
def render(self, **_) -> None:
|
|
16
|
+
st.write("Page not found")
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
import typing
|
|
3
|
+
|
|
4
|
+
import streamlit as st
|
|
5
|
+
|
|
6
|
+
from testgen.ui.navigation.menu import MenuItem
|
|
7
|
+
from testgen.ui.navigation.page import Page
|
|
8
|
+
from testgen.ui.services import form_service
|
|
9
|
+
from testgen.ui.session import session
|
|
10
|
+
|
|
11
|
+
LOG = logging.getLogger("testgen")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class OverviewPage(Page):
|
|
15
|
+
path = "overview"
|
|
16
|
+
can_activate: typing.ClassVar = [
|
|
17
|
+
lambda: session.authentication_status or "login",
|
|
18
|
+
]
|
|
19
|
+
menu_item = MenuItem(icon="home", label="Overview", order=0)
|
|
20
|
+
|
|
21
|
+
def render(self):
|
|
22
|
+
form_service.render_page_header(
|
|
23
|
+
"Welcome to DataOps TestGen",
|
|
24
|
+
"https://docs.datakitchen.io/article/dataops-testgen-help/introduction-to-dataops-testgen",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
st.session_state["app_title"] = "TestGen Dashboard"
|
|
28
|
+
|
|
29
|
+
st.markdown(
|
|
30
|
+
"###### The easiest way possible to institute comprehensive, agile data quality testing.\n\n"
|
|
31
|
+
" - Start measuring immediately. \n"
|
|
32
|
+
" - Derive actionable information quickly. \n"
|
|
33
|
+
" - Then iterate, using tests and results to refine as you go."
|
|
34
|
+
)
|