ethyca-fides 2.66.2rc0__py2.py3-none-any.whl → 2.67.0__py2.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.
Potentially problematic release.
This version of ethyca-fides might be problematic. Click here for more details.
- {ethyca_fides-2.66.2rc0.dist-info → ethyca_fides-2.67.0.dist-info}/METADATA +1 -1
- {ethyca_fides-2.66.2rc0.dist-info → ethyca_fides-2.67.0.dist-info}/RECORD +297 -282
- fides/_version.py +3 -3
- fides/api/alembic/migrations/versions/7e9a2b52f498_adding_masking_secrets.py +60 -0
- fides/api/alembic/migrations/versions/a7065df4dcf1_add_finalized_fields_to_privacy_request.py +65 -0
- fides/api/alembic/migrations/versions/d0031087eacb_create_manualtaskconditionaldependency_.py +106 -0
- fides/api/api/v1/endpoints/dataset_config_endpoints.py +13 -5
- fides/api/api/v1/endpoints/drp_endpoints.py +7 -1
- fides/api/api/v1/endpoints/privacy_request_endpoints.py +44 -1
- fides/api/api/v1/endpoints/user_endpoints.py +83 -7
- fides/api/app_setup.py +3 -2
- fides/api/common_exceptions.py +8 -0
- fides/api/db/base.py +1 -0
- fides/api/db/database.py +1 -1
- fides/api/graph/execution.py +46 -0
- fides/api/graph/graph.py +13 -2
- fides/api/graph/traversal.py +126 -39
- fides/api/models/manual_task/__init__.py +2 -0
- fides/api/models/manual_task/conditional_dependency.py +144 -0
- fides/api/models/{manual_task.py → manual_task/manual_task.py} +10 -0
- fides/api/models/masking_secret.py +72 -0
- fides/api/models/policy.py +23 -0
- fides/api/models/privacy_request/execution_log.py +1 -0
- fides/api/models/privacy_request/privacy_request.py +64 -26
- fides/api/oauth/roles.py +2 -0
- fides/api/schemas/application_config.py +12 -1
- fides/api/schemas/connection_configuration/connection_secrets_datahub.py +10 -1
- fides/api/schemas/masking/masking_secrets.py +1 -1
- fides/api/schemas/policy.py +1 -0
- fides/api/schemas/privacy_request.py +5 -0
- fides/api/service/connectors/base_connector.py +15 -0
- fides/api/service/connectors/bigquery_connector.py +72 -19
- fides/api/service/connectors/dynamodb_connector.py +2 -1
- fides/api/service/connectors/fides_connector.py +1 -0
- fides/api/service/connectors/http_connector.py +1 -0
- fides/api/service/connectors/manual_task_connector.py +1 -0
- fides/api/service/connectors/manual_webhook_connector.py +2 -1
- fides/api/service/connectors/mongodb_connector.py +1 -0
- fides/api/service/connectors/okta_connector.py +1 -0
- fides/api/service/connectors/query_configs/bigquery_query_config.py +95 -36
- fides/api/service/connectors/query_configs/snowflake_query_config.py +3 -3
- fides/api/service/connectors/rds_mysql_connector.py +1 -0
- fides/api/service/connectors/rds_postgres_connector.py +1 -0
- fides/api/service/connectors/s3_connector.py +1 -0
- fides/api/service/connectors/saas_connector.py +1 -0
- fides/api/service/connectors/scylla_connector.py +1 -0
- fides/api/service/connectors/snowflake_connector.py +55 -2
- fides/api/service/connectors/sql_connector.py +159 -13
- fides/api/service/connectors/website_connector.py +1 -0
- fides/api/service/privacy_request/dsr_package/templates/welcome.html +2 -2
- fides/api/service/privacy_request/request_runner_service.py +145 -55
- fides/api/service/privacy_request/request_service.py +172 -52
- fides/api/task/conditional_dependencies/__init__.py +0 -0
- fides/api/task/conditional_dependencies/evaluator.py +109 -0
- fides/api/task/conditional_dependencies/schemas.py +54 -0
- fides/api/task/create_request_tasks.py +1 -1
- fides/api/task/deprecated_graph_task.py +24 -6
- fides/api/task/execute_request_tasks.py +93 -12
- fides/api/task/filter_results.py +1 -1
- fides/api/task/graph_task.py +86 -5
- fides/api/task/manual/manual_task_address.py +46 -0
- fides/api/task/manual/manual_task_graph_task.py +118 -126
- fides/api/task/manual/manual_task_utils.py +52 -105
- fides/api/util/aws_util.py +5 -1
- fides/api/util/cache.py +61 -0
- fides/api/util/encryption/secrets_util.py +48 -18
- fides/api/util/memory_watchdog.py +286 -0
- fides/common/api/scope_registry.py +3 -0
- fides/common/api/v1/urn_registry.py +1 -1
- fides/config/execution_settings.py +12 -0
- fides/config/utils.py +2 -0
- fides/service/privacy_request/privacy_request_service.py +6 -1
- fides/ui-build/static/admin/404.html +1 -1
- fides/ui-build/static/admin/_next/static/MNlh9olWjgbqAHKyQY3LF/_buildManifest.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/1316-2606e19807c08aa5.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/1467-8808ec8836e033f9.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/1817-b78b58ae3b75d75a.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/1975.7d4634a0e823a02b.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{203-5a663f465ba26bb4.js → 203-cd78ea279cecba60.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/2150-930ffaf2c4718edc.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/255-1bc0cbef7a59cdc6.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{3450-0ba194991d0cca88.js → 3450-69f4e16978971bb8.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/346-aa3b88efb85f2e28.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/3550-d04125c828d591a1.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{3855-e172870d3e21b0dd.js → 3855-509ca7ac99b5eada.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{3872-46cebf7ec1b31a2b.js → 3872-0a0f0032ca39a93f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/409-ea70638a59296659.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{4121-2bc09fc4ddbfe5cb.js → 4121-877e19d3fa078c7b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{4230-60100f7ef3ddcde1.js → 4230-114e31621c19ea69.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/4259.d1507e0db19cbed7.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/431-1db919f6569a4021.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{4608-bbb7bf511a05c3c2.js → 4608-f16f281f2d05d963.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/5163-e682273cd76a7d07.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/5309-d9a488457898263b.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{905-ffdbd0b14167e8bd.js → 5596-4378b2927dae65b2.js} +3 -3
- fides/ui-build/static/admin/_next/static/chunks/5619-9b50cec521203989.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{6084-7178ff6ea6822475.js → 6084-ddbad3149364725d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/6148-59a59d5c5925344f.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/6419-d0c00d661b01f8fa.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{6662-507be5d46e5b719b.js → 6662-a9e54ead3dc53644.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/6780-b42a27e72707936d.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{6853-2ad3e08fe6f9f5f2.js → 6853-d0190d2cca9dbde2.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{6882-6af16fef26c21e06.js → 6882-59ea739e3616ce83.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/6954-ba98e778a5b45ebf.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{7476-281ee9a8286556f3.js → 7476-cc0d9a94ed7aad53.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/7725-539d3a906f627531.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{787-fb41002f797eb2df.js → 787-c57185ad89c4e288.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{79-7e87aff851423d4a.js → 79-2aab56be75e16187.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{796-329a5f823ec258a5.js → 796-3bdda2a7868464af.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/8735-40caf91800a3610c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/8765-f622a35b40a7ec63.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/9046-7085a401297c5520.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/9187-7438242f0d380bb0.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{9226-746771d47dff6266.js → 9226-2dcac54ab3fb94be.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/9278-08cc704317fe535e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{9951-9b753ad7c3f51bdf.js → 9951-7c6639e5d062779e.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{_app-39ccb07327c2c5d5.js → _app-750d6bd16c971bb9.js} +56 -56
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-98777246bec9dc2a.js → manual-2a655ff3a97f2492.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{add-systems-a71c0aff4e0e6535.js → add-systems-0902f0bb4080643e.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{[id]-1edf582ba3cd3bbb.js → [id]-f22ddd9b48a5c418.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{new-06bb3b0bf097fcdb.js → new-e74cb5ea87f15b40.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-experience-685771e5f7196d87.js → privacy-experience-21f997c69fc3b4c2.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-6ccedc70dc447089.js → [id]-da4124b7600a2a1d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-944bca1cc57985b5.js → new-a57d251c88ce68ae.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-notices-84f4bd14ce8673bc.js → privacy-notices-ad105181bc91209b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-b0c4235fe6d0b0c8.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/{[resourceUrn]-11d52f1570759c4d.js → [resourceUrn]-aad6047a4604b945.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{projects-32eac8bbd217615a.js → projects-29784a11fe0fbd0a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/{[resourceUrn]-b83afa5565d0c84e.js → [resourceUrn]-b6b98cea25dd94fa.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{data-catalog-6f630d42ac9fb6b4.js → data-catalog-7770a8dc34bd0fc0.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-3e5725cd06d7fe6c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-92b0bd97d8e79340.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-ee3c0a103346fc06.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{activity-9aa744d56cdacb0d.js → activity-ad6a84a6276f914c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-14bd7500362ff224.js → [resourceUrn]-f98dd251babb7e28.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-9e7dfd5a6acc2e8f.js → discovery-56eb4c014f0d96a3.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datamap-d23b3ae139f0428b.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/{[...subfieldNames]-c0d2bfd465df20e0.js → [...subfieldNames]-15301bd6bf7cf718.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/{[collectionName]-28280a8a39a6e37c.js → [collectionName]-0fa72873e464f581.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{new-82fb246d87e58ebd.js → new-0d50084fbdf9b84c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{dataset-20165c31ab1bc7cf.js → dataset-f3348d0a92543bab.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-b4a6bcc87d126840.js → [id]-7d6027570d05c57f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{new-f95d7b0bbfc58f5a.js → new-c6614583b14dc9f2.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{fides-js-docs-5d8fd1af75f19e2f.js → fides-js-docs-1f4335dca5c09860.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{index-1919aab9e5834b51.js → index-fbf9b845bb901238.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-8e346fb36e8034d2.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{integrations-e2d5d7e2a5265e68.js → integrations-7f15cd8538cdc24d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/messaging-1bae386d8c190348.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/{table-migration-69ad86b7a8a9a115.js → table-migration-05616e2ae20ff4f8.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-79f1576b1126975c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-f1d818242d8550f8.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-d40a26bddb126c5c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-96a08c4431b5462c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-9d1840f8309b706e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/{[purpose_id]-fc201657f4a782c7.js → [purpose_id]-e891d01ece59669e.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{consent-c2d39cba8396ef3a.js → consent-f61b87e79367865b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{custom-fields-d992103cc55901ae.js → custom-fields-f8eea5d508c60c64.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-023e1895552817de.js → locations-ed6a140b362c5baa.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-ac403c0886b20e20.js → organization-ff9a34264d48c35f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-7a3396ac819c7904.js → test-datasets-a4b6d41ca679298b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-8314a819837f5b2a.js → [id]-c8f5fbaa83dd9945.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{systems-21f423a7c417aa9d.js → systems-c05b49ddec1a1b4f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-df0d88716578e295.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-da68efc31998dc66.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-173ac3a1ed2b05a6.js → user-management-e98dfc7d4f2a4e16.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/webpack-90e8ec1fc5c6455b.js +1 -0
- fides/ui-build/static/admin/_next/static/css/{5bfb2473e5701527.css → 23cf870196941c9a.css} +1 -1
- fides/ui-build/static/admin/_next/static/css/{94965f224bc991e9.css → 8bc1833f1fa53ff0.css} +1 -1
- fides/ui-build/static/admin/_next/static/css/d9924caa849931b3.css +1 -0
- fides/ui-build/static/admin/_next/static/css/dbcf63488933a4d5.css +29 -0
- fides/ui-build/static/admin/add-systems/manual.html +1 -1
- fides/ui-build/static/admin/add-systems/multiple.html +1 -1
- fides/ui-build/static/admin/add-systems.html +1 -1
- fides/ui-build/static/admin/consent/configure/add-vendors.html +1 -1
- fides/ui-build/static/admin/consent/configure.html +1 -1
- fides/ui-build/static/admin/consent/privacy-experience/[id].html +1 -1
- fides/ui-build/static/admin/consent/privacy-experience/new.html +1 -1
- fides/ui-build/static/admin/consent/privacy-experience.html +1 -1
- fides/ui-build/static/admin/consent/privacy-notices/[id].html +1 -1
- fides/ui-build/static/admin/consent/privacy-notices/new.html +1 -1
- fides/ui-build/static/admin/consent/privacy-notices.html +1 -1
- fides/ui-build/static/admin/consent/properties.html +1 -1
- fides/ui-build/static/admin/consent/reporting.html +1 -1
- fides/ui-build/static/admin/consent.html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn].html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/projects.html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/resources/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/resources.html +1 -1
- fides/ui-build/static/admin/data-catalog.html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center/[monitorId]/[systemId].html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center/[monitorId].html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
- fides/ui-build/static/admin/data-discovery/activity.html +1 -1
- fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-discovery/detection.html +1 -1
- fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
- fides/ui-build/static/admin/datamap.html +1 -1
- fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
- fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
- fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
- fides/ui-build/static/admin/dataset/new.html +1 -1
- fides/ui-build/static/admin/dataset.html +1 -1
- fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
- fides/ui-build/static/admin/datastore-connection/new.html +1 -1
- fides/ui-build/static/admin/datastore-connection.html +1 -1
- fides/ui-build/static/admin/index.html +1 -1
- fides/ui-build/static/admin/integrations/[id].html +1 -1
- fides/ui-build/static/admin/integrations.html +1 -1
- fides/ui-build/static/admin/lib/fides-ext-gpp.js +1 -1
- fides/ui-build/static/admin/lib/fides-headless.js +1 -1
- fides/ui-build/static/admin/lib/fides-preview.js +1 -1
- fides/ui-build/static/admin/lib/fides-tcf.js +4 -4
- fides/ui-build/static/admin/lib/fides.js +4 -4
- fides/ui-build/static/admin/login/[provider].html +1 -1
- fides/ui-build/static/admin/login.html +1 -1
- fides/ui-build/static/admin/messaging/[id].html +1 -1
- fides/ui-build/static/admin/messaging/add-template.html +1 -1
- fides/ui-build/static/admin/messaging.html +1 -1
- fides/ui-build/static/admin/poc/ant-components.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/AntForm.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikAntFormItem.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikControlled.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikField.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikSpreadField.html +1 -1
- fides/ui-build/static/admin/poc/forms.html +1 -1
- fides/ui-build/static/admin/poc/table-migration.html +1 -1
- fides/ui-build/static/admin/privacy-requests/[id].html +1 -1
- fides/ui-build/static/admin/privacy-requests/configure/messaging.html +1 -1
- fides/ui-build/static/admin/privacy-requests/configure/storage.html +1 -1
- fides/ui-build/static/admin/privacy-requests/configure.html +1 -1
- fides/ui-build/static/admin/privacy-requests.html +1 -1
- fides/ui-build/static/admin/properties/[id].html +1 -1
- fides/ui-build/static/admin/properties/add-property.html +1 -1
- fides/ui-build/static/admin/properties.html +1 -1
- fides/ui-build/static/admin/reporting/datamap.html +1 -1
- fides/ui-build/static/admin/settings/about/alpha.html +1 -1
- fides/ui-build/static/admin/settings/about.html +1 -1
- fides/ui-build/static/admin/settings/consent/[configuration_id]/[purpose_id].html +1 -1
- fides/ui-build/static/admin/settings/consent.html +1 -1
- fides/ui-build/static/admin/settings/custom-fields.html +1 -1
- fides/ui-build/static/admin/settings/domain-records.html +1 -1
- fides/ui-build/static/admin/settings/domains.html +1 -1
- fides/ui-build/static/admin/settings/email-templates.html +1 -1
- fides/ui-build/static/admin/settings/locations.html +1 -1
- fides/ui-build/static/admin/settings/organization.html +1 -1
- fides/ui-build/static/admin/settings/regulations.html +1 -1
- fides/ui-build/static/admin/systems/configure/[id]/test-datasets.html +1 -1
- fides/ui-build/static/admin/systems/configure/[id].html +1 -1
- fides/ui-build/static/admin/systems.html +1 -1
- fides/ui-build/static/admin/taxonomy.html +1 -1
- fides/ui-build/static/admin/user-management/new.html +1 -1
- fides/ui-build/static/admin/user-management/profile/[id].html +1 -1
- fides/ui-build/static/admin/user-management.html +1 -1
- fides/ui-build/static/admin/_next/static/8108ANFxs99VY7KZ_Xev2/_buildManifest.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/1316-6cc72a45ebf7ff81.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/1807-3beab149351d5ded.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/1817-e601e737e3cc7a0e.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/255-7db55b0e3a0f9dea.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/2599-6c4d22e75028d8b6.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/2858-0b44609b6be7850b.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/2866-a73888c17a195cbe.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/3615-5e2d062d684b8fa1.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/409-a257e14acebcd73b.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/431-34f0b91c26f8d9ab.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/5309-b2c4803370634ff8.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/570-c99f07161bd339cd.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/6780-e3d40aa17a4bf2e9.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/6954-bb875d9ac89f6030.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7062.fda15dcb7df85675.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7c79804f.7a7112aece470725.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9014-eeae6f581158e645.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9046-5c4c22c375de25b1.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9278-9b1b5970f0702668.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9392.f4520f66206d347d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/ea076c0a.84423f606aef37cd.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-afdbd4665657cfa1.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-2265ecb899d45fbc.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-5d522637871ac6c8.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-9ddb52ebb7ac4c71.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datamap-7674b97d655c193b.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-0a58aee2d1e7fa01.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/messaging-5094ffea13f32ed9.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-32600543eb7b584f.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-10ce53ea356f8bad.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-5501bbb129fee9c4.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-cbe4c8f9096b6543.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-e130c0197362e8f3.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-6387fcc8cce872eb.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-ff5738706da07801.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/webpack-ff0cd6bff75588da.js +0 -1
- fides/ui-build/static/admin/_next/static/css/2cadb5f62dcd7c2b.css +0 -1
- {ethyca_fides-2.66.2rc0.dist-info → ethyca_fides-2.67.0.dist-info}/WHEEL +0 -0
- {ethyca_fides-2.66.2rc0.dist-info → ethyca_fides-2.67.0.dist-info}/entry_points.txt +0 -0
- {ethyca_fides-2.66.2rc0.dist-info → ethyca_fides-2.67.0.dist-info}/licenses/LICENSE +0 -0
- {ethyca_fides-2.66.2rc0.dist-info → ethyca_fides-2.67.0.dist-info}/top_level.txt +0 -0
- /fides/ui-build/static/admin/_next/static/{8108ANFxs99VY7KZ_Xev2 → MNlh9olWjgbqAHKyQY3LF}/_ssgManifest.js +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{2921-455e6357b74d2f76.js → 2921-86f1547ac40a5cdf.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{3923-6cc911dafccc5f63.js → 3923-13a6b4da2d51bf8f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{401-1b529d5800aa1f3a.js → 401-3cc1fee61494e3bd.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{5574-b13021775a15bfd2.js → 5574-9312f97b637d9ee2.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{7630-9aac73191ed5ed13.js → 7630-b1c93688013ef013.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{9826-111aaee8bd8dbd09.js → 9826-3c578665c6d3b21d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{404-aece2c920ea14514.js → 404-2d803dab6a00f353.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{multiple-dc75dc6e37e52f05.js → multiple-8ff7f37913ad736a.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/{add-vendors-24d226b5a8de5c74.js → add-vendors-d00c9034cdeb0236.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{configure-6a8ef51138ac926a.js → configure-0e1ca0f4c8e7f4da.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{properties-6f86ab63a08a6528.js → properties-057cad65e7414a44.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{consent-73d3cbf68f7c3a31.js → consent-e17c56eec8d91371.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/{[projectUrn]-6ba9e160dae64695.js → [projectUrn]-80a6cc8e8573514a.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{resources-7648bbd4f6711e4d.js → resources-6c3714ee97a718c1.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-393e20924c83373e.js → [resourceUrn]-31e6c54794a9883e.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-8733807dad4bc96e.js → detection-2822a423a7ad0550.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{[datasetId]-006b695e5af5ef24.js → [datasetId]-a8e8b5f4ee7af86c.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{datastore-connection-c391c6fad56eec48.js → datastore-connection-3bd77864da523d41.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{[id]-53fecfb9dd6a1e0c.js → [id]-5627d0d0668077f9.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{add-template-76b01cec5fde10a9.js → add-template-feca66ad5c5fe54a.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{ant-components-5c08e8447c45ce44.js → ant-components-64a322d01aae5ca7.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{AntForm-06ad5f34585480aa.js → AntForm-8bca16a7726e7eb2.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikAntFormItem-6f071c2bc9446cb0.js → FormikAntFormItem-b0f246fc3b67ebf7.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikControlled-efcc38c58991ac9e.js → FormikControlled-1a0852b090bfc392.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikField-430ba5c979abfb7c.js → FormikField-11f3de1b45e36583.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{forms-5c561880bf131afb.js → forms-1b73a1c2b6c6285f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{configure-d888a69a3bbe040e.js → configure-e551a860ec727802.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{[id]-d3d8e3d7583ec635.js → [id]-dd99183f93763ae4.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{add-property-1af10ed303815d46.js → add-property-0bdbc1fcbf553b8f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{properties-cebc0dc186be499a.js → properties-e959378bb32b6b73.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/{alpha-5e1322de868d615e.js → alpha-1066f0c202ef744c.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{about-241f95e372b65d0f.js → about-37ba24a72a06862e.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-41242f805599feda.js → domain-records-51333dbd21cb37c8.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domains-2e885f74c92f669c.js → domains-bde86e5f6c09da5a.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{email-templates-ff112655ad5f41e5.js → email-templates-4f9a5cc8bea7725b.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{regulations-86062a18e081a52a.js → regulations-102efd9199e87124.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/user-management/{new-a2524414e968f862.js → new-de8cb3739ab99c09.js} +0 -0
|
@@ -14,6 +14,7 @@ from fides.api.common_exceptions import (
|
|
|
14
14
|
ClientUnsuccessfulException,
|
|
15
15
|
IdentityNotFoundException,
|
|
16
16
|
ManualWebhookFieldsUnset,
|
|
17
|
+
MaskingSecretsExpired,
|
|
17
18
|
MessageDispatchException,
|
|
18
19
|
NoCachedManualWebhookEntry,
|
|
19
20
|
PrivacyRequestExit,
|
|
@@ -75,9 +76,11 @@ from fides.api.task.graph_task import (
|
|
|
75
76
|
from fides.api.task.manual.manual_task_utils import create_manual_task_artificial_graphs
|
|
76
77
|
from fides.api.tasks import DatabaseTask, celery_app
|
|
77
78
|
from fides.api.tasks.scheduled.scheduler import scheduler
|
|
79
|
+
from fides.api.util.cache import get_all_masking_secret_keys
|
|
78
80
|
from fides.api.util.collection_util import Row
|
|
79
81
|
from fides.api.util.logger import Pii, _log_exception, _log_warning
|
|
80
82
|
from fides.api.util.logger_context_utils import LoggerContextKeys, log_context
|
|
83
|
+
from fides.api.util.memory_watchdog import memory_limiter
|
|
81
84
|
from fides.common.api.v1.urn_registry import (
|
|
82
85
|
PRIVACY_REQUEST_TRANSFER_TO_PARENT,
|
|
83
86
|
V1_URL_PREFIX,
|
|
@@ -356,8 +359,8 @@ def upload_and_save_access_results( # pylint: disable=R0912
|
|
|
356
359
|
|
|
357
360
|
|
|
358
361
|
@celery_app.task(base=DatabaseTask, bind=True)
|
|
359
|
-
|
|
360
|
-
|
|
362
|
+
@memory_limiter
|
|
363
|
+
@log_context(capture_args={"privacy_request_id": LoggerContextKeys.privacy_request_id})
|
|
361
364
|
def run_privacy_request(
|
|
362
365
|
self: DatabaseTask,
|
|
363
366
|
privacy_request_id: str,
|
|
@@ -478,12 +481,21 @@ def run_privacy_request(
|
|
|
478
481
|
connection_configs
|
|
479
482
|
)
|
|
480
483
|
|
|
484
|
+
# If the privacy request requires manual finalization and has not yet been finalized, we exit here
|
|
485
|
+
if (
|
|
486
|
+
privacy_request.status
|
|
487
|
+
== PrivacyRequestStatus.requires_manual_finalization
|
|
488
|
+
and privacy_request.finalized_at is None
|
|
489
|
+
):
|
|
490
|
+
return
|
|
491
|
+
|
|
481
492
|
# Access CHECKPOINT
|
|
482
493
|
if (
|
|
483
494
|
policy.get_rules_for_action(action_type=ActionType.access)
|
|
484
495
|
or policy.get_rules_for_action(action_type=ActionType.erasure)
|
|
485
496
|
) and can_run_checkpoint(
|
|
486
|
-
request_checkpoint=CurrentStep.access,
|
|
497
|
+
request_checkpoint=CurrentStep.access,
|
|
498
|
+
from_checkpoint=resume_step,
|
|
487
499
|
):
|
|
488
500
|
privacy_request.cache_failed_checkpoint_details(CurrentStep.access)
|
|
489
501
|
access_runner(
|
|
@@ -528,9 +540,12 @@ def run_privacy_request(
|
|
|
528
540
|
if policy.get_rules_for_action(
|
|
529
541
|
action_type=ActionType.erasure
|
|
530
542
|
) and can_run_checkpoint(
|
|
531
|
-
request_checkpoint=CurrentStep.erasure,
|
|
543
|
+
request_checkpoint=CurrentStep.erasure,
|
|
544
|
+
from_checkpoint=resume_step,
|
|
532
545
|
):
|
|
533
546
|
privacy_request.cache_failed_checkpoint_details(CurrentStep.erasure)
|
|
547
|
+
_verify_masking_secrets(policy, privacy_request, resume_step)
|
|
548
|
+
|
|
534
549
|
# We only need to run the erasure once until masking strategies are handled
|
|
535
550
|
erasure_runner(
|
|
536
551
|
privacy_request=privacy_request,
|
|
@@ -652,58 +667,111 @@ def run_privacy_request(
|
|
|
652
667
|
if not proceed:
|
|
653
668
|
return
|
|
654
669
|
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
else MessagingActionType.PRIVACY_REQUEST_COMPLETE_DELETION
|
|
663
|
-
)
|
|
664
|
-
|
|
665
|
-
if message_send_enabled(
|
|
666
|
-
session,
|
|
667
|
-
privacy_request.property_id,
|
|
668
|
-
action_type,
|
|
669
|
-
legacy_request_completion_enabled,
|
|
670
|
-
) and not policy.get_rules_for_action(action_type=ActionType.consent):
|
|
671
|
-
try:
|
|
672
|
-
if not access_result_urls:
|
|
673
|
-
# For DSR 3.0, if the request had both access and erasure rules, this needs to be fetched
|
|
674
|
-
# from the database because the Privacy Request would have exited
|
|
675
|
-
# processing and lost access to the access_result_urls in memory
|
|
676
|
-
access_result_urls = (
|
|
677
|
-
privacy_request.access_result_urls or {}
|
|
678
|
-
).get("access_result_urls", [])
|
|
679
|
-
initiate_privacy_request_completion_email(
|
|
680
|
-
session,
|
|
681
|
-
policy,
|
|
682
|
-
access_result_urls,
|
|
683
|
-
identity_data,
|
|
684
|
-
privacy_request.property_id,
|
|
685
|
-
)
|
|
686
|
-
except (IdentityNotFoundException, MessageDispatchException) as e:
|
|
687
|
-
privacy_request.error_processing(db=session)
|
|
688
|
-
# If dev mode, log traceback
|
|
689
|
-
_log_exception(e, CONFIG.dev_mode)
|
|
690
|
-
return
|
|
691
|
-
|
|
692
|
-
# Only mark as complete if not in error state
|
|
693
|
-
if privacy_request.status != PrivacyRequestStatus.error:
|
|
694
|
-
privacy_request.finished_processing_at = datetime.utcnow()
|
|
695
|
-
AuditLog.create(
|
|
696
|
-
db=session,
|
|
697
|
-
data={
|
|
698
|
-
"user_id": "system",
|
|
699
|
-
"privacy_request_id": privacy_request.id,
|
|
700
|
-
"action": AuditLogAction.finished,
|
|
701
|
-
"message": "",
|
|
702
|
-
},
|
|
670
|
+
# Request finalization CHECKPOINT
|
|
671
|
+
if can_run_checkpoint(
|
|
672
|
+
request_checkpoint=CurrentStep.finalization,
|
|
673
|
+
from_checkpoint=resume_step,
|
|
674
|
+
):
|
|
675
|
+
privacy_request.cache_failed_checkpoint_details(
|
|
676
|
+
CurrentStep.finalization,
|
|
703
677
|
)
|
|
704
|
-
privacy_request.status
|
|
705
|
-
|
|
706
|
-
|
|
678
|
+
if privacy_request.status != PrivacyRequestStatus.error:
|
|
679
|
+
erasure_rules = policy.get_rules_for_action(
|
|
680
|
+
action_type=ActionType.erasure
|
|
681
|
+
)
|
|
682
|
+
if (
|
|
683
|
+
privacy_request.finalized_at is None
|
|
684
|
+
and erasure_rules
|
|
685
|
+
and CONFIG.execution.erasure_request_finalization_required
|
|
686
|
+
):
|
|
687
|
+
logger.info(
|
|
688
|
+
"Marking privacy request '{}' as requires manual finalization.",
|
|
689
|
+
privacy_request.id,
|
|
690
|
+
)
|
|
691
|
+
privacy_request.status = (
|
|
692
|
+
PrivacyRequestStatus.requires_manual_finalization
|
|
693
|
+
)
|
|
694
|
+
privacy_request.save(db=session)
|
|
695
|
+
return
|
|
696
|
+
|
|
697
|
+
# Finally, mark the request as complete
|
|
698
|
+
if privacy_request.finalized_at:
|
|
699
|
+
logger.info(
|
|
700
|
+
"Marking privacy request '{}' as finalized.",
|
|
701
|
+
privacy_request.id,
|
|
702
|
+
)
|
|
703
|
+
privacy_request.add_success_execution_log(
|
|
704
|
+
session,
|
|
705
|
+
connection_key=None,
|
|
706
|
+
dataset_name="Request finalized",
|
|
707
|
+
collection_name=None,
|
|
708
|
+
message=f"Request finalized for privacy request: {privacy_request.id}",
|
|
709
|
+
action_type=privacy_request.policy.get_action_type(), # type: ignore
|
|
710
|
+
)
|
|
711
|
+
|
|
712
|
+
logger.info(
|
|
713
|
+
"Marking privacy request '{}' as complete.",
|
|
714
|
+
privacy_request.id,
|
|
715
|
+
)
|
|
716
|
+
AuditLog.create(
|
|
717
|
+
db=session,
|
|
718
|
+
data={
|
|
719
|
+
"user_id": "system",
|
|
720
|
+
"privacy_request_id": privacy_request.id,
|
|
721
|
+
"action": AuditLogAction.finished,
|
|
722
|
+
"message": "",
|
|
723
|
+
},
|
|
724
|
+
)
|
|
725
|
+
privacy_request.status = PrivacyRequestStatus.complete
|
|
726
|
+
privacy_request.finished_processing_at = datetime.utcnow()
|
|
727
|
+
privacy_request.save(db=session)
|
|
728
|
+
|
|
729
|
+
# Send a final email to the user confirming request completion
|
|
730
|
+
if privacy_request.status == PrivacyRequestStatus.complete:
|
|
731
|
+
legacy_request_completion_enabled = ConfigProxy(
|
|
732
|
+
session
|
|
733
|
+
).notifications.send_request_completion_notification
|
|
734
|
+
|
|
735
|
+
action_type = (
|
|
736
|
+
MessagingActionType.PRIVACY_REQUEST_COMPLETE_ACCESS
|
|
737
|
+
if policy.get_rules_for_action(
|
|
738
|
+
action_type=ActionType.access
|
|
739
|
+
)
|
|
740
|
+
else MessagingActionType.PRIVACY_REQUEST_COMPLETE_DELETION
|
|
741
|
+
)
|
|
742
|
+
|
|
743
|
+
if message_send_enabled(
|
|
744
|
+
session,
|
|
745
|
+
privacy_request.property_id,
|
|
746
|
+
action_type,
|
|
747
|
+
legacy_request_completion_enabled,
|
|
748
|
+
) and not policy.get_rules_for_action(
|
|
749
|
+
action_type=ActionType.consent
|
|
750
|
+
):
|
|
751
|
+
if not access_result_urls:
|
|
752
|
+
# For DSR 3.0, if the request had both access and erasure rules, this needs to be fetched
|
|
753
|
+
# from the database because the Privacy Request would have exited
|
|
754
|
+
# processing and lost access to the access_result_urls in memory
|
|
755
|
+
access_result_urls = (
|
|
756
|
+
privacy_request.access_result_urls or {}
|
|
757
|
+
).get("access_result_urls", [])
|
|
758
|
+
|
|
759
|
+
try:
|
|
760
|
+
initiate_privacy_request_completion_email(
|
|
761
|
+
session,
|
|
762
|
+
policy,
|
|
763
|
+
access_result_urls,
|
|
764
|
+
identity_data,
|
|
765
|
+
privacy_request.property_id,
|
|
766
|
+
)
|
|
767
|
+
except (
|
|
768
|
+
IdentityNotFoundException,
|
|
769
|
+
MessageDispatchException,
|
|
770
|
+
) as e:
|
|
771
|
+
privacy_request.error_processing(db=session)
|
|
772
|
+
# If dev mode, log traceback
|
|
773
|
+
_log_exception(e, CONFIG.dev_mode)
|
|
774
|
+
return
|
|
707
775
|
|
|
708
776
|
|
|
709
777
|
def initiate_privacy_request_completion_email(
|
|
@@ -1000,3 +1068,25 @@ def run_webhooks_and_report_status(
|
|
|
1000
1068
|
return False
|
|
1001
1069
|
|
|
1002
1070
|
return True
|
|
1071
|
+
|
|
1072
|
+
|
|
1073
|
+
def _verify_masking_secrets(
|
|
1074
|
+
policy: Policy, privacy_request: PrivacyRequest, resume_step: Optional[CurrentStep]
|
|
1075
|
+
) -> None:
|
|
1076
|
+
"""
|
|
1077
|
+
Checks that the required masking secrets are still cached for the given request.
|
|
1078
|
+
Raises an exception if masking secrets are needed for the given policy but they don't exist.
|
|
1079
|
+
"""
|
|
1080
|
+
|
|
1081
|
+
if resume_step is None:
|
|
1082
|
+
return
|
|
1083
|
+
|
|
1084
|
+
# if masking can be performed without any masking secrets, we skip the cache check
|
|
1085
|
+
if (
|
|
1086
|
+
policy.generate_masking_secrets()
|
|
1087
|
+
and not get_all_masking_secret_keys(privacy_request.id)
|
|
1088
|
+
and not privacy_request.masking_secrets
|
|
1089
|
+
):
|
|
1090
|
+
raise MaskingSecretsExpired(
|
|
1091
|
+
f"The masking secrets for privacy request ID '{privacy_request.id}' have expired. Please submit a new erasure request."
|
|
1092
|
+
)
|
|
@@ -8,10 +8,10 @@ from typing import Any, Dict, List, Optional, Set
|
|
|
8
8
|
from httpx import AsyncClient
|
|
9
9
|
from loguru import logger
|
|
10
10
|
from sqlalchemy import text
|
|
11
|
+
from sqlalchemy.orm import Session
|
|
11
12
|
from sqlalchemy.sql.elements import TextClause
|
|
12
13
|
|
|
13
14
|
from fides.api.common_exceptions import PrivacyRequestNotFound
|
|
14
|
-
from fides.api.models.policy import Policy
|
|
15
15
|
from fides.api.models.privacy_request import (
|
|
16
16
|
EXITED_EXECUTION_LOG_STATUSES,
|
|
17
17
|
PrivacyRequest,
|
|
@@ -19,14 +19,12 @@ from fides.api.models.privacy_request import (
|
|
|
19
19
|
)
|
|
20
20
|
from fides.api.models.worker_task import ExecutionLogStatus
|
|
21
21
|
from fides.api.schemas.drp_privacy_request import DrpPrivacyRequestCreate
|
|
22
|
-
from fides.api.schemas.masking.masking_secrets import MaskingSecretCache
|
|
23
22
|
from fides.api.schemas.policy import ActionType
|
|
24
23
|
from fides.api.schemas.privacy_request import (
|
|
25
24
|
PrivacyRequestResponse,
|
|
26
25
|
PrivacyRequestStatus,
|
|
27
26
|
)
|
|
28
27
|
from fides.api.schemas.redis_cache import Identity
|
|
29
|
-
from fides.api.service.masking.strategy.masking_strategy import MaskingStrategy
|
|
30
28
|
from fides.api.tasks import DSR_QUEUE_NAME, DatabaseTask, celery_app
|
|
31
29
|
from fides.api.tasks.scheduled.scheduler import scheduler
|
|
32
30
|
from fides.api.util.cache import (
|
|
@@ -34,6 +32,9 @@ from fides.api.util.cache import (
|
|
|
34
32
|
celery_tasks_in_flight,
|
|
35
33
|
get_async_task_tracking_cache_key,
|
|
36
34
|
get_cache,
|
|
35
|
+
get_privacy_request_retry_count,
|
|
36
|
+
increment_privacy_request_retry_count,
|
|
37
|
+
reset_privacy_request_retry_count,
|
|
37
38
|
)
|
|
38
39
|
from fides.api.util.lock import redis_lock
|
|
39
40
|
from fides.common.api.v1.urn_registry import PRIVACY_REQUESTS, V1_URL_PREFIX
|
|
@@ -69,7 +70,6 @@ def build_required_privacy_request_kwargs(
|
|
|
69
70
|
|
|
70
71
|
def cache_data(
|
|
71
72
|
privacy_request: PrivacyRequest,
|
|
72
|
-
policy: Policy,
|
|
73
73
|
identity: Identity,
|
|
74
74
|
encryption_key: Optional[str],
|
|
75
75
|
drp_request_body: Optional[DrpPrivacyRequestCreate],
|
|
@@ -82,23 +82,6 @@ def cache_data(
|
|
|
82
82
|
privacy_request.cache_custom_privacy_request_fields(custom_privacy_request_fields)
|
|
83
83
|
privacy_request.cache_encryption(encryption_key) # handles None already
|
|
84
84
|
|
|
85
|
-
# Store masking secrets in the cache
|
|
86
|
-
logger.info("Caching masking secrets for privacy request {}", privacy_request.id)
|
|
87
|
-
erasure_rules = policy.get_rules_for_action(action_type=ActionType.erasure)
|
|
88
|
-
unique_masking_strategies_by_name: Set[str] = set()
|
|
89
|
-
for rule in erasure_rules:
|
|
90
|
-
strategy_name: str = rule.masking_strategy["strategy"] # type: ignore
|
|
91
|
-
configuration = rule.masking_strategy["configuration"] # type: ignore
|
|
92
|
-
if strategy_name in unique_masking_strategies_by_name:
|
|
93
|
-
continue
|
|
94
|
-
unique_masking_strategies_by_name.add(strategy_name)
|
|
95
|
-
masking_strategy = MaskingStrategy.get_strategy(strategy_name, configuration)
|
|
96
|
-
if masking_strategy.secrets_required():
|
|
97
|
-
masking_secrets: List[MaskingSecretCache] = (
|
|
98
|
-
masking_strategy.generate_secrets_for_cache()
|
|
99
|
-
)
|
|
100
|
-
for masking_secret in masking_secrets:
|
|
101
|
-
privacy_request.cache_masking_secret(masking_secret)
|
|
102
85
|
if drp_request_body:
|
|
103
86
|
privacy_request.cache_drp_request_body(drp_request_body)
|
|
104
87
|
|
|
@@ -371,10 +354,17 @@ def initiate_interrupted_task_requeue_poll() -> None:
|
|
|
371
354
|
|
|
372
355
|
|
|
373
356
|
def get_cached_task_id(entity_id: str) -> Optional[str]:
|
|
374
|
-
"""Gets the cached task ID for a privacy request or request task by ID.
|
|
357
|
+
"""Gets the cached task ID for a privacy request or request task by ID.
|
|
358
|
+
|
|
359
|
+
Raises Exception if cache operations fail, allowing callers to handle cache failures appropriately.
|
|
360
|
+
"""
|
|
375
361
|
cache: FidesopsRedis = get_cache()
|
|
376
|
-
|
|
377
|
-
|
|
362
|
+
try:
|
|
363
|
+
task_id = cache.get(get_async_task_tracking_cache_key(entity_id))
|
|
364
|
+
return task_id
|
|
365
|
+
except Exception as exc:
|
|
366
|
+
logger.error(f"Failed to get cached task ID for entity {entity_id}: {exc}")
|
|
367
|
+
raise
|
|
378
368
|
|
|
379
369
|
|
|
380
370
|
REQUEUE_INTERRUPTED_TASKS_LOCK = "requeue_interrupted_tasks_lock"
|
|
@@ -414,6 +404,115 @@ def _get_task_ids_from_dsr_queue(
|
|
|
414
404
|
return queued_tasks_ids
|
|
415
405
|
|
|
416
406
|
|
|
407
|
+
def _cancel_interrupted_tasks_and_error_privacy_request(
|
|
408
|
+
db: Session, privacy_request: PrivacyRequest, error_message: Optional[str] = None
|
|
409
|
+
) -> None:
|
|
410
|
+
"""
|
|
411
|
+
Cancel all tasks associated with an interrupted privacy request and set the privacy request to error state.
|
|
412
|
+
|
|
413
|
+
This function:
|
|
414
|
+
1. Logs the error message (either provided or default)
|
|
415
|
+
2. Revokes the main privacy request task and all associated request tasks
|
|
416
|
+
3. Sets the privacy request status to error
|
|
417
|
+
4. Creates an error log entry
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
db: Database session
|
|
421
|
+
privacy_request: The privacy request to cancel and error
|
|
422
|
+
error_message: Optional error message to log. If not provided, uses default message.
|
|
423
|
+
"""
|
|
424
|
+
if error_message:
|
|
425
|
+
logger.error(error_message)
|
|
426
|
+
else:
|
|
427
|
+
logger.error(
|
|
428
|
+
f"Canceling interrupted tasks and marking privacy request {privacy_request.id} as error"
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
# Cancel all associated Celery tasks
|
|
432
|
+
privacy_request.cancel_celery_tasks()
|
|
433
|
+
|
|
434
|
+
# Set privacy request to error state using the existing method
|
|
435
|
+
try:
|
|
436
|
+
privacy_request.error_processing(db)
|
|
437
|
+
logger.info(
|
|
438
|
+
f"Privacy request {privacy_request.id} marked as error due to task interruption"
|
|
439
|
+
)
|
|
440
|
+
except Exception as exc:
|
|
441
|
+
logger.error(
|
|
442
|
+
f"Failed to mark privacy request {privacy_request.id} as error: {exc}"
|
|
443
|
+
)
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def _handle_privacy_request_requeue(
|
|
447
|
+
db: Session, privacy_request: PrivacyRequest
|
|
448
|
+
) -> None:
|
|
449
|
+
"""Handle retry logic for a privacy request - either requeue or cancel based on retry count."""
|
|
450
|
+
try:
|
|
451
|
+
# Check retry count and either requeue or cancel based on limit
|
|
452
|
+
current_retry_count = get_privacy_request_retry_count(privacy_request.id)
|
|
453
|
+
max_retries = CONFIG.execution.privacy_request_requeue_retry_count
|
|
454
|
+
|
|
455
|
+
if current_retry_count < max_retries:
|
|
456
|
+
# Increment retry count and attempt requeue
|
|
457
|
+
new_retry_count = increment_privacy_request_retry_count(privacy_request.id)
|
|
458
|
+
logger.info(
|
|
459
|
+
f"Requeuing privacy request {privacy_request.id} "
|
|
460
|
+
f"(attempt {new_retry_count}/{max_retries})"
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
from fides.service.privacy_request.privacy_request_service import ( # pylint: disable=cyclic-import
|
|
464
|
+
PrivacyRequestError,
|
|
465
|
+
_requeue_privacy_request,
|
|
466
|
+
)
|
|
467
|
+
|
|
468
|
+
try:
|
|
469
|
+
_requeue_privacy_request(db, privacy_request)
|
|
470
|
+
except PrivacyRequestError as exc:
|
|
471
|
+
# If requeue fails, cancel tasks and set to error state
|
|
472
|
+
_cancel_interrupted_tasks_and_error_privacy_request(
|
|
473
|
+
db, privacy_request, exc.message
|
|
474
|
+
)
|
|
475
|
+
else:
|
|
476
|
+
# Exceeded retry limit, cancel tasks and set to error state
|
|
477
|
+
_cancel_interrupted_tasks_and_error_privacy_request(
|
|
478
|
+
db,
|
|
479
|
+
privacy_request,
|
|
480
|
+
f"Privacy request {privacy_request.id} exceeded max retry attempts "
|
|
481
|
+
f"({max_retries}), canceling tasks and setting to error state",
|
|
482
|
+
)
|
|
483
|
+
# Reset retry count since we're giving up
|
|
484
|
+
reset_privacy_request_retry_count(privacy_request.id)
|
|
485
|
+
|
|
486
|
+
except Exception as cache_exc:
|
|
487
|
+
# If cache operations fail (Redis down, network issues, etc.), fail safe by canceling
|
|
488
|
+
_cancel_interrupted_tasks_and_error_privacy_request(
|
|
489
|
+
db,
|
|
490
|
+
privacy_request,
|
|
491
|
+
f"Cache operation failed for privacy request {privacy_request.id}, "
|
|
492
|
+
f"failing safe by canceling tasks: {cache_exc}",
|
|
493
|
+
)
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def _get_request_task_ids_in_progress(
|
|
497
|
+
db: Session, privacy_request_id: str
|
|
498
|
+
) -> List[str]:
|
|
499
|
+
"""Get the IDs of request tasks that are currently in progress for a privacy request."""
|
|
500
|
+
request_tasks_in_progress = (
|
|
501
|
+
db.query(RequestTask.id)
|
|
502
|
+
.filter(RequestTask.privacy_request_id == privacy_request_id)
|
|
503
|
+
.filter(
|
|
504
|
+
RequestTask.status.in_(
|
|
505
|
+
[
|
|
506
|
+
ExecutionLogStatus.in_processing,
|
|
507
|
+
ExecutionLogStatus.pending,
|
|
508
|
+
]
|
|
509
|
+
)
|
|
510
|
+
)
|
|
511
|
+
.all()
|
|
512
|
+
)
|
|
513
|
+
return [task[0] for task in request_tasks_in_progress]
|
|
514
|
+
|
|
515
|
+
|
|
417
516
|
# pylint: disable=too-many-branches
|
|
418
517
|
@celery_app.task(base=DatabaseTask, bind=True)
|
|
419
518
|
def requeue_interrupted_tasks(self: DatabaseTask) -> None:
|
|
@@ -463,17 +562,40 @@ def requeue_interrupted_tasks(self: DatabaseTask) -> None:
|
|
|
463
562
|
)
|
|
464
563
|
|
|
465
564
|
# Get task IDs from the queue in a memory-efficient way
|
|
466
|
-
|
|
565
|
+
try:
|
|
566
|
+
queued_tasks_ids = _get_task_ids_from_dsr_queue(redis_conn)
|
|
567
|
+
except Exception as queue_exc:
|
|
568
|
+
logger.warning(
|
|
569
|
+
f"Failed to get task IDs from queue, skipping queue state checks: {queue_exc}"
|
|
570
|
+
)
|
|
571
|
+
return
|
|
467
572
|
|
|
468
573
|
# Check each privacy request
|
|
469
574
|
for privacy_request in in_progress_requests:
|
|
470
575
|
should_requeue = False
|
|
471
576
|
logger.debug(f"Checking tasks for privacy request {privacy_request.id}")
|
|
472
577
|
|
|
473
|
-
|
|
578
|
+
try:
|
|
579
|
+
task_id = get_cached_task_id(privacy_request.id)
|
|
580
|
+
except Exception as cache_exc:
|
|
581
|
+
# If we can't get the task ID due to cache failure, fail safe by canceling
|
|
582
|
+
_cancel_interrupted_tasks_and_error_privacy_request(
|
|
583
|
+
db,
|
|
584
|
+
privacy_request,
|
|
585
|
+
f"Cache failure when getting task ID for privacy request {privacy_request.id}, "
|
|
586
|
+
f"failing safe by canceling tasks: {cache_exc}",
|
|
587
|
+
)
|
|
588
|
+
continue
|
|
474
589
|
|
|
475
590
|
# If the task ID is not cached, we can't check if it's running
|
|
591
|
+
# This means the request is stuck - cancel it
|
|
476
592
|
if not task_id:
|
|
593
|
+
_cancel_interrupted_tasks_and_error_privacy_request(
|
|
594
|
+
db,
|
|
595
|
+
privacy_request,
|
|
596
|
+
f"No task ID found for privacy request {privacy_request.id}, "
|
|
597
|
+
f"request is stuck without a running task - canceling",
|
|
598
|
+
)
|
|
477
599
|
continue
|
|
478
600
|
|
|
479
601
|
# Check if the main privacy request task is active
|
|
@@ -491,30 +613,36 @@ def requeue_interrupted_tasks(self: DatabaseTask) -> None:
|
|
|
491
613
|
)
|
|
492
614
|
should_requeue = True
|
|
493
615
|
|
|
494
|
-
|
|
495
|
-
db.
|
|
496
|
-
.filter(RequestTask.privacy_request_id == privacy_request.id)
|
|
497
|
-
.filter(
|
|
498
|
-
RequestTask.status.in_(
|
|
499
|
-
[
|
|
500
|
-
ExecutionLogStatus.in_processing,
|
|
501
|
-
ExecutionLogStatus.pending,
|
|
502
|
-
]
|
|
503
|
-
)
|
|
504
|
-
)
|
|
505
|
-
.all()
|
|
616
|
+
request_task_ids_in_progress = _get_request_task_ids_in_progress(
|
|
617
|
+
db, privacy_request.id
|
|
506
618
|
)
|
|
507
|
-
request_task_ids_in_progress = [
|
|
508
|
-
task[0] for task in request_tasks_in_progress
|
|
509
|
-
]
|
|
510
619
|
|
|
511
620
|
# Check each individual request task
|
|
512
621
|
for request_task_id in request_task_ids_in_progress:
|
|
513
|
-
|
|
622
|
+
try:
|
|
623
|
+
subtask_id = get_cached_task_id(request_task_id)
|
|
624
|
+
except Exception as cache_exc:
|
|
625
|
+
# If we can't get the subtask ID due to cache failure, fail safe by canceling
|
|
626
|
+
_cancel_interrupted_tasks_and_error_privacy_request(
|
|
627
|
+
db,
|
|
628
|
+
privacy_request,
|
|
629
|
+
f"Cache failure when getting subtask ID for request task {request_task_id} "
|
|
630
|
+
f"(privacy request {privacy_request.id}), failing safe by canceling tasks: {cache_exc}",
|
|
631
|
+
)
|
|
632
|
+
should_requeue = False
|
|
633
|
+
break
|
|
514
634
|
|
|
515
635
|
# If the task ID is not cached, we can't check if it's running
|
|
636
|
+
# This means the subtask is stuck - cancel the entire privacy request
|
|
516
637
|
if not subtask_id:
|
|
517
|
-
|
|
638
|
+
_cancel_interrupted_tasks_and_error_privacy_request(
|
|
639
|
+
db,
|
|
640
|
+
privacy_request,
|
|
641
|
+
f"No task ID found for request task {request_task_id} "
|
|
642
|
+
f"(privacy request {privacy_request.id}), subtask is stuck - canceling privacy request",
|
|
643
|
+
)
|
|
644
|
+
should_requeue = False
|
|
645
|
+
break
|
|
518
646
|
|
|
519
647
|
if (
|
|
520
648
|
subtask_id not in queued_tasks_ids
|
|
@@ -528,12 +656,4 @@ def requeue_interrupted_tasks(self: DatabaseTask) -> None:
|
|
|
528
656
|
|
|
529
657
|
# Requeue the privacy request if needed
|
|
530
658
|
if should_requeue:
|
|
531
|
-
|
|
532
|
-
PrivacyRequestError,
|
|
533
|
-
_requeue_privacy_request,
|
|
534
|
-
)
|
|
535
|
-
|
|
536
|
-
try:
|
|
537
|
-
_requeue_privacy_request(db, privacy_request)
|
|
538
|
-
except PrivacyRequestError as exc:
|
|
539
|
-
logger.error(exc.message)
|
|
659
|
+
_handle_privacy_request_requeue(db, privacy_request)
|
|
File without changes
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import operator as py_operator
|
|
2
|
+
from typing import Any, Union
|
|
3
|
+
|
|
4
|
+
from loguru import logger
|
|
5
|
+
from sqlalchemy.orm import Session
|
|
6
|
+
|
|
7
|
+
from fides.api.graph.config import FieldPath
|
|
8
|
+
from fides.api.task.conditional_dependencies.schemas import (
|
|
9
|
+
Condition,
|
|
10
|
+
ConditionGroup,
|
|
11
|
+
ConditionLeaf,
|
|
12
|
+
GroupOperator,
|
|
13
|
+
Operator,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
operator_methods = {
|
|
17
|
+
Operator.exists: lambda a, _: a is not None,
|
|
18
|
+
Operator.not_exists: lambda a, _: a is None,
|
|
19
|
+
Operator.eq: py_operator.eq,
|
|
20
|
+
Operator.neq: py_operator.ne,
|
|
21
|
+
Operator.lt: lambda a, b: a < b if a is not None else False,
|
|
22
|
+
Operator.lte: lambda a, b: a <= b if a is not None else False,
|
|
23
|
+
Operator.gt: lambda a, b: a > b if a is not None else False,
|
|
24
|
+
Operator.gte: lambda a, b: a >= b if a is not None else False,
|
|
25
|
+
Operator.list_contains: lambda a, b: b in a if isinstance(a, list) else False,
|
|
26
|
+
Operator.not_in_list: lambda a, b: a not in b if isinstance(b, list) else True,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class ConditionEvaluator:
|
|
31
|
+
"""Evaluates nested conditions for manual task creation"""
|
|
32
|
+
|
|
33
|
+
def __init__(self, db: Session):
|
|
34
|
+
self.db = db
|
|
35
|
+
|
|
36
|
+
def evaluate_rule(self, rule: Condition, data: Union[dict, Any]) -> bool:
|
|
37
|
+
"""Evaluate a nested condition rule against input data"""
|
|
38
|
+
if isinstance(rule, ConditionLeaf):
|
|
39
|
+
return self._evaluate_leaf_condition(rule, data)
|
|
40
|
+
# ConditionGroup
|
|
41
|
+
return self._evaluate_group_condition(rule, data)
|
|
42
|
+
|
|
43
|
+
def _evaluate_leaf_condition(
|
|
44
|
+
self, condition: ConditionLeaf, data: Union[dict, Any]
|
|
45
|
+
) -> bool:
|
|
46
|
+
"""Evaluate a leaf condition against input data"""
|
|
47
|
+
actual_value = self._get_nested_value(data, condition.field_address.split("."))
|
|
48
|
+
# Apply operator and return result
|
|
49
|
+
return self._apply_operator(actual_value, condition.operator, condition.value)
|
|
50
|
+
|
|
51
|
+
def _evaluate_group_condition(
|
|
52
|
+
self, group: ConditionGroup, data: Union[dict, Any]
|
|
53
|
+
) -> bool:
|
|
54
|
+
"""Evaluate a group condition against input data"""
|
|
55
|
+
results = [
|
|
56
|
+
self.evaluate_rule(condition, data) for condition in group.conditions
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
logical_operators = {GroupOperator.and_: all, GroupOperator.or_: any}
|
|
60
|
+
operator_func = logical_operators.get(group.logical_operator)
|
|
61
|
+
|
|
62
|
+
if operator_func is None:
|
|
63
|
+
logger.warning(f"Unknown logical operator: {group.logical_operator}")
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
return operator_func(results)
|
|
67
|
+
|
|
68
|
+
def _get_nested_value(self, data: Union[dict, Any], keys: list[str]) -> Any:
|
|
69
|
+
"""Get nested value from data using dot notation
|
|
70
|
+
|
|
71
|
+
Supports both simple dictionary access and Fides reference structures:
|
|
72
|
+
- Simple dict: data["user"]["name"]
|
|
73
|
+
- Fides FieldAddress: data.get_field_value(FieldAddress("dataset", "collection", "field_address"))
|
|
74
|
+
- Fides Collection: data.get_field_value(FieldPath("field_address", "subfield"))
|
|
75
|
+
"""
|
|
76
|
+
if not keys:
|
|
77
|
+
return data
|
|
78
|
+
|
|
79
|
+
current = data
|
|
80
|
+
|
|
81
|
+
# Try Fides reference structures first
|
|
82
|
+
if hasattr(current, "get_field_value"):
|
|
83
|
+
try:
|
|
84
|
+
field_path = FieldPath(*keys) if len(keys) > 1 else FieldPath(keys[0])
|
|
85
|
+
return current.get_field_value(field_path)
|
|
86
|
+
except (AttributeError, ValueError):
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
# Fall back to dictionary access
|
|
90
|
+
for key in keys:
|
|
91
|
+
if not isinstance(current, dict):
|
|
92
|
+
current = current.get(key, {}) if hasattr(current, "get") else None
|
|
93
|
+
else:
|
|
94
|
+
current = current.get(key, {})
|
|
95
|
+
|
|
96
|
+
return current if current != {} else None
|
|
97
|
+
|
|
98
|
+
def _apply_operator(
|
|
99
|
+
self, actual_value: Any, operator: Operator, expected_value: Any
|
|
100
|
+
) -> bool:
|
|
101
|
+
"""Apply operator to actual and expected values"""
|
|
102
|
+
|
|
103
|
+
# Get the method for the operator and execute it
|
|
104
|
+
operator_method = operator_methods.get(operator)
|
|
105
|
+
if operator_method is None:
|
|
106
|
+
logger.warning(f"Unknown operator: {operator}")
|
|
107
|
+
return False
|
|
108
|
+
|
|
109
|
+
return operator_method(actual_value, expected_value)
|