ethyca-fides 2.71.1b1__py2.py3-none-any.whl → 2.71.2b0__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.71.1b1.dist-info → ethyca_fides-2.71.2b0.dist-info}/METADATA +1 -1
- {ethyca_fides-2.71.1b1.dist-info → ethyca_fides-2.71.2b0.dist-info}/RECORD +262 -248
- fides/_version.py +3 -3
- fides/api/alembic/migrations/versions/4bfbeff34611_add_polling_status.py +35 -0
- fides/api/alembic/migrations/versions/7db29f9cd77b_create_new_sub_request_table.py +95 -0
- fides/api/alembic/migrations/versions/b97e92b038d2_add_digest_execution_model.py +117 -0
- fides/api/common_exceptions.py +4 -0
- fides/api/main.py +2 -2
- fides/api/models/attachment.py +1 -0
- fides/api/models/digest/__init__.py +2 -0
- fides/api/models/digest/digest_config.py +10 -1
- fides/api/models/digest/digest_execution.py +132 -0
- fides/api/models/event_audit.py +8 -0
- fides/api/models/privacy_request/privacy_request.py +1 -1
- fides/api/models/privacy_request/request_task.py +98 -1
- fides/api/models/worker_task.py +8 -0
- fides/api/schemas/saas/async_polling_configuration.py +81 -0
- fides/api/schemas/saas/saas_config.py +10 -3
- fides/api/schemas/saas/strategy_configuration.py +0 -12
- fides/api/service/async_dsr/handlers/__init__.py +0 -0
- fides/api/service/async_dsr/handlers/polling_attachment_handler.py +155 -0
- fides/api/service/async_dsr/handlers/polling_request_handler.py +88 -0
- fides/api/service/async_dsr/handlers/polling_response_handler.py +261 -0
- fides/api/service/async_dsr/handlers/polling_sub_request_handler.py +123 -0
- fides/api/service/async_dsr/strategies/__init__.py +0 -0
- fides/api/service/async_dsr/strategies/async_dsr_strategy.py +52 -0
- fides/api/service/async_dsr/strategies/async_dsr_strategy_callback.py +199 -0
- fides/api/service/async_dsr/strategies/async_dsr_strategy_factory.py +72 -0
- fides/api/service/async_dsr/strategies/async_dsr_strategy_polling.py +678 -0
- fides/api/service/async_dsr/utils.py +130 -0
- fides/api/service/connectors/fides/fides_client.py +63 -1
- fides/api/service/connectors/query_configs/saas_query_config.py +20 -5
- fides/api/service/connectors/saas_connector.py +86 -70
- fides/api/service/privacy_request/attachment_handling.py +9 -2
- fides/api/service/privacy_request/request_runner_service.py +9 -83
- fides/api/service/privacy_request/request_service.py +47 -74
- fides/api/service/saas_request/saas_request_override_factory.py +66 -1
- fides/api/task/execute_request_tasks.py +5 -2
- fides/api/task/filter_results.py +35 -2
- fides/api/task/graph_task.py +34 -2
- fides/api/util/saas_util.py +1 -0
- fides/config/execution_settings.py +7 -3
- fides/data/language/languages.yml +2 -0
- fides/ui-build/static/admin/404.html +1 -1
- fides/ui-build/static/admin/_next/static/chunks/1099-27b2d4e59a569b38.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{1115-90baef2a89f361ad.js → 1115-8fb5000847f73ec7.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/1437-fb699e9d69c3e325.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/155-3d03f101c662c054.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{1817-1ad037b7d6d2f6d2.js → 1817-10b392e1dd9c4673.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{1975.78e719130cfe3fd6.js → 1975.50599b6774562644.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{2040-fdecc41a18e40bdc.js → 2040-01d5edb1802d3482.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/2397-40506a386a3a469a.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{2921-66f65496c3a09316.js → 2921-edfdffa353a611e4.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/3377-4c129258b4c4ea28.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{3696-90c8b336bbc46782.js → 3696-259be222ea12392a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/3700-42976bfaad3b91c9.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/3772-aa7beafe5df5e656.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{3923-98bea73b618292aa.js → 3923-fa1d71afc7dc9e6e.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{401-ffe4e8436e1eceb9.js → 401-5270e3e23b08ab2b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/431-e01ee730c8ad9ece.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/4817-5f9b9c9bafe888f1.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{5185-51eaa78e3ed6bfb7.js → 5185-325e5435a0c7dcb7.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/5258-2d28b2c64acf5de3.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{5279-12c9cbdc67ad7b14.js → 5279-21929b12c927b022.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/549-c3b4c51f45a84a52.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{5643-10a36584c399526c.js → 5643-7583c1f8dbd83273.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{5783-d119cb132abd8a91.js → 5783-55c0387508e50f3e.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{6277-182efc294d413f64.js → 6277-ca31d48a33202761.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/6315-2d4e86a32fc40458.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/6344-ca66a6e10d128179.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{6853-7004a8c420b1ca02.js → 6853-cb15625a713b3ef9.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{6954-4b24e1731c1cc3b3.js → 6954-08b59d28d7a2a43b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/7245-895bb7c6a6c797bc.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{7476-d055aa931da47ac0.js → 7476-f25fce73eeb1f9c6.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/7488-df17b592fbeaa1eb.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{7630-d0d3a0fe3f95e971.js → 7630-71582de0e818e28f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/7773-98d34d188ae0a694.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{796-02086581996a0548.js → 796-7944f684f8945506.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/8011-56031d82d5428981.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/8373-7f940f5ac9c38b1f.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/9046-31b16a7e872c22be.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/9070-13f237db78da586d.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/9330-cea1bf40f5d27e53.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{9676.b86ecbcfe5afd25d.js → 9676.1ea49d4b6e0541f2.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/9682-2754256aeae141bb.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{9826-ccedc28e978ca9e1.js → 9826-e71887e2c4283249.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/9899-15530c468d5656a1.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/9965-54037b6c8f36d14b.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{404-2eb8aed4939f1142.js → 404-2014144a1a0fa64f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{_app-c1c2f757b1f3da12.js → _app-9e869a44215709ed.js} +9 -9
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/manual-4f3ba02774f27795.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{multiple-2ca59996860a33c5.js → multiple-1c9cef80deccb15d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems-4adeec3bcec05f7b.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/{add-vendors-7a258b7ecd6da4b8.js → add-vendors-f7bdc2da53137ffd.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{configure-fb5017ff5fa54fcc.js → configure-a27b220fe76bdc8e.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{[id]-e1e2fd704ac2d71d.js → [id]-542fd4a4322122f1.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-experience-92182be6603c2842.js → privacy-experience-4254abef7bb52edd.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-5fc78b78a51c239c.js → [id]-d31d1c233e94dc1f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-b79bcb93b5f4c734.js → new-2ea057e6217993f9.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-notices-ab54b19609bff325.js → privacy-notices-8e76cbfaf71fdcb9.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{properties-069f4e3ee96ebf77.js → properties-eb557180eded0abd.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-93730a4316c0d980.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{consent-d2bf72508c3cad55.js → consent-c64589e53338e59a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/{[resourceUrn]-2fa4b3a58f75f81d.js → [resourceUrn]-d3d1841bc8105e56.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]-8cd4d1d6ba000333.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects-69b429a9d30ebaf1.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/{[resourceUrn]-5b31e3d7727b917a.js → [resourceUrn]-791c522ad1d9a2ce.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources-8c7103ebf01d6e6a.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog-a53ef0161235ee47.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-f85f4f8fcf1bfbdb.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/data-explorer-3619e454763dba09.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-ace848575e688200.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-53ca31c50c43007f.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-f87e4a0f664f4cde.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-844a8de0d1b506e2.js → [resourceUrn]-699762cb8a66237e.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-11b07cf2d91b17ef.js → detection-d970d0b82db36c88.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-5525cf287d4ab493.js → [resourceUrn]-a6343038a447699c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-ed4723e1b67d890e.js → discovery-ce82ba79f9bf825c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datamap-ae7a4c1c4eabc0d4.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/[...subfieldNames]-179e5d09ccff50c1.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]-59466e5e238c59d0.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]-36d90c8c39fb0d1c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/new-db54d01fd8f6df50.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset-55d312af5228e098.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/[id]-488beba62e832660.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/new-4aad5495f9bf81ce.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-496ec7ecca8259dc.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{index-b74d1e8608ae5b5d.js → index-8a2ba955d92d52ee.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-4580f70eb41b6a7d.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations-f69b04d3b4cf43b1.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{[id]-e8d2140787045acd.js → [id]-d6eed2dc5d27f625.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{add-template-e3f93462a08251bf.js → add-template-c7b43fa365f4be10.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{messaging-b5f7d6afdecd013d.js → messaging-c7ba755c2e0e2377.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/{ant-components-248ad9f65a872442.js → ant-components-f14bfd5890a0e939.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{AntForm-aedb66a62042b10a.js → AntForm-e4274a4d5d23eea9.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikAntFormItem-018df38b7cd77fdb.js → FormikAntFormItem-f5bce1ceb0502f04.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikControlled-6ca9099d03aab817.js → FormikControlled-90b4ea9f9deca6e2.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikField-0f2c90786ea005a4.js → FormikField-d6043ae8dae6ef7d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/{forms-200b51a725f8b2d1.js → forms-520ba5ae02f47bda.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/{table-migration-29fb7b39f8962650.js → table-migration-b46f432b51421fae.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-fe4ed3ad75151e36.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-de681c2d022c9e0d.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{configure-8f577df28ebca869.js → configure-04ebf2fb6fc4358a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-cd8ba2eef9085377.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/properties/{[id]-57a75c7e9659271a.js → [id]-f75a407d78857ab3.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/properties/{add-property-8964c2300206bc89.js → add-property-975ff605e1dbc09c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{properties-08472b2a6bf1d392.js → properties-e7fdf3a9a3e8c267.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-764b6ce8e030d4a1.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/{[purpose_id]-f3e6e74e0efb005c.js → [purpose_id]-da78452b3c41b29e.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{consent-4d658222ec800511.js → consent-6442e761ecb5c65a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-ea9baf24de35d3b1.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-744f669431b84f71.js → domain-records-44d06e280990854d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domains-a3275554ffe8e640.js → domains-6f61d886aa6f5ce7.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{email-templates-604790638c656fbd.js → email-templates-11148b182370fd83.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-be2a885150adc133.js → locations-33448bf0a1d53870.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/messaging-providers/{[key]-77239269acc2d31a.js → [key]-4a18d8135c0dbf2a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/messaging-providers/{new-8bf1821722b082e9.js → new-41957281b8ec26b1.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/messaging-providers-776258bad0941a7d.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-3c86162afe9759df.js → organization-3c315036f1fc9f90.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/privacy-requests-00917525c9245a90.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{regulations-4fe3b90747d885e5.js → regulations-5d924b2f1f926b88.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-2deb6becece69d46.js → test-datasets-8067e12227210f48.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-4d470bbf199a2f9c.js → [id]-1a902922759cae76.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems-277ea1f34c3714cf.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-cecea5832cb1f3b0.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/{[id]-98f737e735eaa0f0.js → [id]-726fbc0a34ffba1f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-562624e5461083ec.js → user-management-df9b75ab01c1a2ea.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{webpack-2766492c5dbceb0a.js → webpack-118fb4723d13c14f.js} +1 -1
- fides/ui-build/static/admin/_next/static/css/372d6a236c84613f.css +1 -0
- fides/ui-build/static/admin/_next/static/css/f38242c11f7fea64.css +1 -0
- fides/ui-build/static/admin/_next/static/zmEQMSpOBih8kmA1t-EWO/_buildManifest.js +1 -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]/data-explorer.html +1 -0
- 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/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/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/[id].html +1 -1
- fides/ui-build/static/admin/settings/custom-fields/new.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/messaging-providers/[key].html +1 -1
- fides/ui-build/static/admin/settings/messaging-providers/new.html +1 -1
- fides/ui-build/static/admin/settings/messaging-providers.html +1 -1
- fides/ui-build/static/admin/settings/organization.html +1 -1
- fides/ui-build/static/admin/settings/privacy-requests.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/api/service/async_dsr/async_dsr_service.py +0 -195
- fides/api/service/async_dsr/async_dsr_strategy.py +0 -5
- fides/api/service/async_dsr/async_dsr_strategy_callback.py +0 -16
- fides/api/service/async_dsr/async_dsr_strategy_factory.py +0 -63
- fides/api/service/async_dsr/async_dsr_strategy_polling.py +0 -94
- fides/ui-build/static/admin/_next/static/_IxwgneyQjdSaZFEF3Tqu/_buildManifest.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/155-b4337d0826d5addc.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/3585-f728d32fda6f1ac1.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/3855-ed226b8a8050bd40.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/3872-04d3afbfa41a7782.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/409-5c3d31163028339f.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/431-78bf05f35d7eec4f.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4558-8305aee48def1dcd.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4608-0c6ef78e30a51f84.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4718-3a412bdb90add82f.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/502-0d9f4ac29ef34a1c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/504-88caa30c03374e9b.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/5309-3b6cf0cc9d0c6a83.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/5574-c31ea831371610d5.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/6882-dbe0a25dcf1a8ee0.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7045-14e955890f1147e4.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7079-50571e9f3269d74d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7158-04745cc8d684b2e7.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7218-e2983b96b95e33b4.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7725-f2a7be705b75dcc3.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7929-0fd0d4948bc8d70e.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/8002-ed832921ad190832.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9046-b6616ba7b59d947e.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9226-4a7027057f55ca2a.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/manual-4ec03eed67572861.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems-19214babd1f219e3.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-c1a3caf3c286bf5d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]-4a1af12d2d7cd660.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects-99573a1ee3ef8f4c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources-6e429b7511028d60.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog-b7326c51d88cc2cc.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-5b57f9132426fe52.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-0d512528b498d75c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-040813022f0890c9.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-a28cc0e23bbe4fc8.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datamap-7d22222608ec3aac.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/[...subfieldNames]-0abd30eada811b5b.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]-007965429368d9a3.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]-60a4a9eb4aab4c11.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/new-d514cd4ec62e3b03.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset-0e3a6ac4797ffbbb.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/[id]-816e02b6cbe4a684.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/new-b6838162200141b3.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-223c2d1ded51bfb1.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-153eb88ab4e7dc6d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations-331544e9b85c4ac2.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-7dac2302f573f5ee.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-479890582973deaf.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-7af00f72cf694077.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-f7753e9effae3816.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-2fcd95c41e578d57.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/messaging-providers-8d92be437793c96f.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/privacy-requests-97221067330c0c27.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems-6c91bdea40875227.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-3059aba38adefa56.js +0 -1
- fides/ui-build/static/admin/_next/static/css/073713cd1eddda79.css +0 -1
- fides/ui-build/static/admin/_next/static/css/295d729ea1b11885.css +0 -1
- {ethyca_fides-2.71.1b1.dist-info → ethyca_fides-2.71.2b0.dist-info}/WHEEL +0 -0
- {ethyca_fides-2.71.1b1.dist-info → ethyca_fides-2.71.2b0.dist-info}/entry_points.txt +0 -0
- {ethyca_fides-2.71.1b1.dist-info → ethyca_fides-2.71.2b0.dist-info}/licenses/LICENSE +0 -0
- {ethyca_fides-2.71.1b1.dist-info → ethyca_fides-2.71.2b0.dist-info}/top_level.txt +0 -0
- /fides/ui-build/static/admin/_next/static/{_IxwgneyQjdSaZFEF3Tqu → zmEQMSpOBih8kmA1t-EWO}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Any, Dict, List, Optional, Union
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, Field, model_validator
|
|
5
|
+
|
|
6
|
+
from fides.api.schemas.saas.saas_config import SaaSRequest
|
|
7
|
+
from fides.api.schemas.saas.strategy_configuration import StrategyConfiguration
|
|
8
|
+
from fides.api.util.collection_util import Row
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class SupportedDataType(Enum):
|
|
12
|
+
"""Supported data types for polling async DSR result requests."""
|
|
13
|
+
|
|
14
|
+
# Structured data types that can be parsed into rows
|
|
15
|
+
json = "json" # Parsed into List[Row] from JSON response
|
|
16
|
+
csv = "csv" # Parsed into List[Row] from CSV response
|
|
17
|
+
# Binary/non-parseable data stored as raw bytes
|
|
18
|
+
attachment = "attachment" # Binary files (.zip, .pdf, .xml, etc.) stored as bytes
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PollingResultType(Enum):
|
|
22
|
+
"""Types of results from async polling operations."""
|
|
23
|
+
|
|
24
|
+
rows = "rows" # Structured data parsed into List[Row]
|
|
25
|
+
attachment = "attachment" # Binary file data stored as bytes
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class PollingResult(BaseModel):
|
|
29
|
+
"""
|
|
30
|
+
Flexible result container for async polling operations.
|
|
31
|
+
Handles both structured data and file attachments.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
data: Union[List[Row], bytes]
|
|
35
|
+
result_type: PollingResultType
|
|
36
|
+
metadata: Dict[str, Any] = Field(default_factory=dict)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class PollingStatusRequest(SaaSRequest):
|
|
40
|
+
"""
|
|
41
|
+
Extended SaaSRequest for checking async job status.
|
|
42
|
+
Uses request_override for custom status checking logic or standard fields for simple cases.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
status_path: Optional[str] = None
|
|
46
|
+
status_completed_value: Optional[Union[str, bool, int]] = None
|
|
47
|
+
|
|
48
|
+
@model_validator(mode="after")
|
|
49
|
+
def validate_status_fields(self) -> "PollingStatusRequest":
|
|
50
|
+
"""Ensure required fields are present unless using an override."""
|
|
51
|
+
if self.request_override:
|
|
52
|
+
return self
|
|
53
|
+
|
|
54
|
+
if not self.status_path:
|
|
55
|
+
raise ValueError("status_path is required when request_override is not set")
|
|
56
|
+
if self.status_completed_value is None:
|
|
57
|
+
raise ValueError(
|
|
58
|
+
"status_completed_value is required when request_override is not set"
|
|
59
|
+
)
|
|
60
|
+
return self
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class PollingResultRequest(SaaSRequest):
|
|
64
|
+
"""
|
|
65
|
+
Extended SaaSRequest for retrieving async job results.
|
|
66
|
+
Uses request_override for custom result retrieval or standard HTTP request for simple cases.
|
|
67
|
+
Data type is automatically inferred from response.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
result_path: Optional[str] = None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class AsyncPollingConfiguration(StrategyConfiguration):
|
|
74
|
+
"""
|
|
75
|
+
Simplified configuration for polling async DSR requests.
|
|
76
|
+
The main read request serves as the initial request.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
status_request: PollingStatusRequest
|
|
80
|
+
# result_request is optional for delete/update operations
|
|
81
|
+
result_request: Optional[PollingResultRequest] = None
|
|
@@ -2,7 +2,9 @@ from typing import Any, Dict, List, Optional, Set, Union
|
|
|
2
2
|
|
|
3
3
|
from fideslang.models import FidesCollectionKey, FidesDatasetReference
|
|
4
4
|
from fideslang.validation import FidesKey
|
|
5
|
-
from pydantic import BaseModel, ConfigDict
|
|
5
|
+
from pydantic import BaseModel, ConfigDict
|
|
6
|
+
from pydantic import Field as PydanticField
|
|
7
|
+
from pydantic import field_validator, model_validator
|
|
6
8
|
|
|
7
9
|
from fides.api.common_exceptions import ValidationError
|
|
8
10
|
from fides.api.graph.config import (
|
|
@@ -115,6 +117,10 @@ class SaaSRequest(BaseModel):
|
|
|
115
117
|
skip_missing_param_values: Optional[bool] = (
|
|
116
118
|
False # Skip instead of raising an exception if placeholders can't be populated in body
|
|
117
119
|
)
|
|
120
|
+
correlation_id_path: Optional[str] = PydanticField(
|
|
121
|
+
default=None,
|
|
122
|
+
description="The path to the correlation ID in the response. For use with async polling.",
|
|
123
|
+
)
|
|
118
124
|
model_config = ConfigDict(
|
|
119
125
|
from_attributes=True, use_enum_values=True, extra="forbid"
|
|
120
126
|
)
|
|
@@ -213,7 +219,7 @@ class SaaSRequest(BaseModel):
|
|
|
213
219
|
class ReadSaaSRequest(SaaSRequest):
|
|
214
220
|
"""
|
|
215
221
|
An extension of the base SaaSRequest that allows the inclusion of an output template
|
|
216
|
-
that is used to format each collection result.
|
|
222
|
+
that is used to format each collection result, and correlation_id_path for async polling.
|
|
217
223
|
"""
|
|
218
224
|
|
|
219
225
|
output: Optional[str] = None
|
|
@@ -230,7 +236,8 @@ class ReadSaaSRequest(SaaSRequest):
|
|
|
230
236
|
raise ValueError(
|
|
231
237
|
"A read request must specify a method if a path is provided and no request_override is specified"
|
|
232
238
|
)
|
|
233
|
-
|
|
239
|
+
|
|
240
|
+
if self.request_override:
|
|
234
241
|
allowed_fields = {
|
|
235
242
|
"request_override",
|
|
236
243
|
"param_values",
|
|
@@ -178,15 +178,3 @@ class OAuth2ClientCredentialsConfiguration(OAuth2BaseConfiguration):
|
|
|
178
178
|
"""
|
|
179
179
|
|
|
180
180
|
refresh_request: Optional[SaaSRequest] = Field(exclude=True)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
class PollingAsyncDSRConfiguration(StrategyConfiguration):
|
|
184
|
-
"""
|
|
185
|
-
Configuration for polling async DSR requests.
|
|
186
|
-
"""
|
|
187
|
-
|
|
188
|
-
status_request: SaaSRequest
|
|
189
|
-
status_path: str
|
|
190
|
-
status_completed_value: Optional[str] = None
|
|
191
|
-
result_request: SaaSRequest
|
|
192
|
-
result_path: str
|
|
File without changes
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"""Handler for storing and managing attachments from async polling operations."""
|
|
2
|
+
|
|
3
|
+
from io import BytesIO
|
|
4
|
+
from typing import List, Union
|
|
5
|
+
|
|
6
|
+
from loguru import logger
|
|
7
|
+
from sqlalchemy.orm import Session
|
|
8
|
+
|
|
9
|
+
from fides.api.common_exceptions import PrivacyRequestError
|
|
10
|
+
from fides.api.models.attachment import (
|
|
11
|
+
Attachment,
|
|
12
|
+
AttachmentReference,
|
|
13
|
+
AttachmentReferenceType,
|
|
14
|
+
AttachmentType,
|
|
15
|
+
)
|
|
16
|
+
from fides.api.models.privacy_request.request_task import RequestTask
|
|
17
|
+
from fides.api.models.storage import get_active_default_storage_config
|
|
18
|
+
from fides.api.util.collection_util import Row
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PollingAttachmentHandler:
|
|
22
|
+
"""Utility class for handling attachment storage and metadata in polling operations."""
|
|
23
|
+
|
|
24
|
+
@staticmethod
|
|
25
|
+
def store_attachment(
|
|
26
|
+
session: Session,
|
|
27
|
+
request_task: RequestTask,
|
|
28
|
+
attachment_data: bytes,
|
|
29
|
+
filename: str,
|
|
30
|
+
) -> str:
|
|
31
|
+
"""
|
|
32
|
+
Store polling attachment data and return attachment ID.
|
|
33
|
+
|
|
34
|
+
This utility function handles the storage of attachment data
|
|
35
|
+
from polling results and creates the necessary database records.
|
|
36
|
+
|
|
37
|
+
Args:
|
|
38
|
+
session: Database session
|
|
39
|
+
request_task: The request task associated with this attachment
|
|
40
|
+
attachment_data: The binary attachment data to store
|
|
41
|
+
filename: The filename for the attachment
|
|
42
|
+
|
|
43
|
+
Returns:
|
|
44
|
+
str: The ID of the created attachment
|
|
45
|
+
|
|
46
|
+
Raises:
|
|
47
|
+
PrivacyRequestError: If storage configuration is not found or storage fails
|
|
48
|
+
"""
|
|
49
|
+
try:
|
|
50
|
+
# Get active storage config
|
|
51
|
+
storage_config = get_active_default_storage_config(session)
|
|
52
|
+
if not storage_config:
|
|
53
|
+
raise PrivacyRequestError("No active storage configuration found")
|
|
54
|
+
|
|
55
|
+
# Create attachment record and upload to storage
|
|
56
|
+
attachment = Attachment.create_and_upload(
|
|
57
|
+
db=session,
|
|
58
|
+
data={
|
|
59
|
+
"file_name": filename,
|
|
60
|
+
"attachment_type": AttachmentType.include_with_access_package,
|
|
61
|
+
"storage_key": storage_config.key,
|
|
62
|
+
},
|
|
63
|
+
attachment_file=BytesIO(attachment_data),
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
# Create attachment references
|
|
67
|
+
AttachmentReference.create(
|
|
68
|
+
db=session,
|
|
69
|
+
data={
|
|
70
|
+
"attachment_id": attachment.id,
|
|
71
|
+
"reference_id": request_task.id,
|
|
72
|
+
"reference_type": AttachmentReferenceType.request_task,
|
|
73
|
+
},
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
AttachmentReference.create(
|
|
77
|
+
db=session,
|
|
78
|
+
data={
|
|
79
|
+
"attachment_id": attachment.id,
|
|
80
|
+
"reference_id": request_task.privacy_request.id,
|
|
81
|
+
"reference_type": AttachmentReferenceType.privacy_request,
|
|
82
|
+
},
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
logger.info(
|
|
86
|
+
f"Successfully stored polling attachment {attachment.id} for request_task {request_task.id}"
|
|
87
|
+
)
|
|
88
|
+
return attachment.id
|
|
89
|
+
|
|
90
|
+
except Exception as e:
|
|
91
|
+
logger.error(f"Failed to store polling attachment: {e}")
|
|
92
|
+
raise PrivacyRequestError(f"Failed to store polling attachment: {e}")
|
|
93
|
+
|
|
94
|
+
@staticmethod
|
|
95
|
+
def ensure_attachment_bytes(data: Union[List[Row], bytes]) -> bytes:
|
|
96
|
+
"""
|
|
97
|
+
Ensure attachment polling results provide bytes content.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
data: The data that should be bytes
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
bytes: The validated bytes data
|
|
104
|
+
|
|
105
|
+
Raises:
|
|
106
|
+
PrivacyRequestError: If data is not bytes
|
|
107
|
+
"""
|
|
108
|
+
if isinstance(data, bytes):
|
|
109
|
+
return data
|
|
110
|
+
raise PrivacyRequestError("Expected bytes data for attachment polling result")
|
|
111
|
+
|
|
112
|
+
@staticmethod
|
|
113
|
+
def add_metadata_to_rows(db: Session, attachment_id: str, rows: List[Row]) -> None:
|
|
114
|
+
"""
|
|
115
|
+
Add attachment metadata to rows collection (like manual tasks do).
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
db: Database session
|
|
119
|
+
attachment_id: The ID of the attachment to add metadata for
|
|
120
|
+
rows: The list of rows to add the attachment metadata to
|
|
121
|
+
"""
|
|
122
|
+
attachment_record = (
|
|
123
|
+
db.query(Attachment).filter(Attachment.id == attachment_id).first()
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
if attachment_record:
|
|
127
|
+
try:
|
|
128
|
+
size, url = attachment_record.retrieve_attachment()
|
|
129
|
+
attachment_info = {
|
|
130
|
+
"file_name": attachment_record.file_name,
|
|
131
|
+
"download_url": str(url) if url else None,
|
|
132
|
+
"file_size": size,
|
|
133
|
+
}
|
|
134
|
+
except Exception as exc:
|
|
135
|
+
logger.warning(
|
|
136
|
+
f"Could not retrieve attachment content for {attachment_record.file_name}: {exc}"
|
|
137
|
+
)
|
|
138
|
+
attachment_info = {
|
|
139
|
+
"file_name": attachment_record.file_name,
|
|
140
|
+
"download_url": None,
|
|
141
|
+
"file_size": None,
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
# Add attachment to the polling results
|
|
145
|
+
attachments_item = None
|
|
146
|
+
for item in rows:
|
|
147
|
+
if isinstance(item, dict) and "retrieved_attachments" in item:
|
|
148
|
+
attachments_item = item
|
|
149
|
+
break
|
|
150
|
+
|
|
151
|
+
if attachments_item is None:
|
|
152
|
+
attachments_item = {"retrieved_attachments": []}
|
|
153
|
+
rows.append(attachments_item)
|
|
154
|
+
|
|
155
|
+
attachments_item["retrieved_attachments"].append(attachment_info)
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pure HTTP utility for executing polling requests.
|
|
3
|
+
|
|
4
|
+
This module contains low-level HTTP request execution for async DSR polling,
|
|
5
|
+
with no business logic or orchestration dependencies.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from typing import Any, Dict, Optional
|
|
9
|
+
|
|
10
|
+
from requests import Response
|
|
11
|
+
|
|
12
|
+
from fides.api.common_exceptions import PrivacyRequestError
|
|
13
|
+
from fides.api.schemas.saas.async_polling_configuration import (
|
|
14
|
+
PollingResultRequest,
|
|
15
|
+
PollingStatusRequest,
|
|
16
|
+
)
|
|
17
|
+
from fides.api.service.connectors.saas.authenticated_client import AuthenticatedClient
|
|
18
|
+
from fides.api.util.saas_util import map_param_values
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PollingRequestHandler:
|
|
22
|
+
"""
|
|
23
|
+
Pure HTTP utility for executing polling requests.
|
|
24
|
+
|
|
25
|
+
Handles status checks and result retrieval with no business logic dependencies.
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
status_request: PollingStatusRequest,
|
|
31
|
+
result_request: Optional[PollingResultRequest] = None,
|
|
32
|
+
):
|
|
33
|
+
self.status_request = status_request
|
|
34
|
+
self.result_request = result_request
|
|
35
|
+
|
|
36
|
+
def get_status_response(
|
|
37
|
+
self,
|
|
38
|
+
client: AuthenticatedClient,
|
|
39
|
+
param_values: Dict[str, Any],
|
|
40
|
+
) -> Response:
|
|
41
|
+
"""Execute HTTP status request and return raw response."""
|
|
42
|
+
if not self.status_request:
|
|
43
|
+
raise PrivacyRequestError(
|
|
44
|
+
"status_request is not configured in the async polling configuration"
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
prepared_status_request = map_param_values(
|
|
48
|
+
action="status",
|
|
49
|
+
context="polling request",
|
|
50
|
+
current_request=self.status_request,
|
|
51
|
+
param_values=param_values,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
response: Response = client.send(prepared_status_request)
|
|
55
|
+
|
|
56
|
+
if not response.ok:
|
|
57
|
+
raise PrivacyRequestError(
|
|
58
|
+
f"Status request failed with status code {response.status_code}: {response.text}"
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return response
|
|
62
|
+
|
|
63
|
+
def get_result_response(
|
|
64
|
+
self,
|
|
65
|
+
client: AuthenticatedClient,
|
|
66
|
+
param_values: Dict[str, Any],
|
|
67
|
+
) -> Response:
|
|
68
|
+
"""Execute HTTP result request and return raw response."""
|
|
69
|
+
if not self.result_request:
|
|
70
|
+
raise PrivacyRequestError(
|
|
71
|
+
"result_request is not configured in the async polling configuration"
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
prepared_result_request = map_param_values(
|
|
75
|
+
action="result",
|
|
76
|
+
context="polling request",
|
|
77
|
+
current_request=self.result_request,
|
|
78
|
+
param_values=param_values,
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
response: Response = client.send(prepared_result_request)
|
|
82
|
+
|
|
83
|
+
if not response.ok:
|
|
84
|
+
raise PrivacyRequestError(
|
|
85
|
+
f"Result request failed with status code {response.status_code}: {response.text}"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
return response
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Pure response processing utility for async DSR polling.
|
|
3
|
+
|
|
4
|
+
This module handles data type inference, attachment classification,
|
|
5
|
+
and response parsing with no business logic dependencies.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import mimetypes
|
|
9
|
+
import os
|
|
10
|
+
from email.message import Message
|
|
11
|
+
from typing import Any, List, Optional
|
|
12
|
+
from urllib.parse import urlparse
|
|
13
|
+
|
|
14
|
+
import pydash
|
|
15
|
+
from loguru import logger
|
|
16
|
+
from requests import Response
|
|
17
|
+
|
|
18
|
+
from fides.api.common_exceptions import PrivacyRequestError
|
|
19
|
+
from fides.api.schemas.saas.async_polling_configuration import (
|
|
20
|
+
PollingResult,
|
|
21
|
+
PollingResultType,
|
|
22
|
+
SupportedDataType,
|
|
23
|
+
)
|
|
24
|
+
from fides.api.util.collection_util import Row
|
|
25
|
+
|
|
26
|
+
CONTENT_TYPE = "content-type"
|
|
27
|
+
CONTENT_DISPOSITION = "content-disposition"
|
|
28
|
+
|
|
29
|
+
# Content type mappings for data type detection
|
|
30
|
+
CONTENT_TYPE_MAP = {
|
|
31
|
+
"application/json": SupportedDataType.json,
|
|
32
|
+
"text/csv": SupportedDataType.csv,
|
|
33
|
+
"application/csv": SupportedDataType.csv,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# File extensions that indicate attachments
|
|
37
|
+
ATTACHMENT_EXTENSIONS = {
|
|
38
|
+
".zip",
|
|
39
|
+
".pdf",
|
|
40
|
+
".tar",
|
|
41
|
+
".gz",
|
|
42
|
+
".xlsx",
|
|
43
|
+
".xls",
|
|
44
|
+
".xml",
|
|
45
|
+
".csv",
|
|
46
|
+
".jpg",
|
|
47
|
+
".jpeg",
|
|
48
|
+
".png",
|
|
49
|
+
".gif",
|
|
50
|
+
".bmp",
|
|
51
|
+
".tiff",
|
|
52
|
+
".webp",
|
|
53
|
+
".svg",
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Content types that should always be attachments
|
|
57
|
+
ATTACHMENT_CONTENT_TYPES = {
|
|
58
|
+
"application/octet-stream",
|
|
59
|
+
"application/zip",
|
|
60
|
+
"application/pdf",
|
|
61
|
+
"application/xml",
|
|
62
|
+
"text/xml",
|
|
63
|
+
"image/",
|
|
64
|
+
"video/",
|
|
65
|
+
"text/csv",
|
|
66
|
+
"application/csv",
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# Initialize mimetypes module for content type inference
|
|
70
|
+
mimetypes.init()
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class PollingResponseProcessor:
|
|
74
|
+
"""Pure utility for processing async polling responses with smart data type inference."""
|
|
75
|
+
|
|
76
|
+
@classmethod
|
|
77
|
+
def process_result_response(
|
|
78
|
+
cls, request_path: str, response: Response, result_path: Optional[str] = None
|
|
79
|
+
) -> PollingResult:
|
|
80
|
+
"""Process response with smart data type inference."""
|
|
81
|
+
inferred_type = cls._infer_data_type(request_path, response)
|
|
82
|
+
|
|
83
|
+
if cls._should_store_as_attachment(response, request_path, inferred_type):
|
|
84
|
+
return cls._build_attachment_result(response, request_path, inferred_type)
|
|
85
|
+
|
|
86
|
+
rows = cls._parse_to_rows(response, inferred_type, result_path)
|
|
87
|
+
return PollingResult(
|
|
88
|
+
data=rows,
|
|
89
|
+
result_type=PollingResultType.rows,
|
|
90
|
+
metadata={
|
|
91
|
+
"inferred_type": inferred_type.value,
|
|
92
|
+
"content_type": response.headers.get(CONTENT_TYPE, ""),
|
|
93
|
+
"row_count": len(rows),
|
|
94
|
+
"parsed_to_rows": True,
|
|
95
|
+
},
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
@staticmethod
|
|
99
|
+
def evaluate_status_response(
|
|
100
|
+
response: Response,
|
|
101
|
+
status_path: str,
|
|
102
|
+
status_completed_value: Optional[Any] = None,
|
|
103
|
+
) -> bool:
|
|
104
|
+
"""Process status response and extract completion status."""
|
|
105
|
+
# Check if response is JSON
|
|
106
|
+
content_type = response.headers.get(CONTENT_TYPE, "").lower()
|
|
107
|
+
if "application/json" not in content_type:
|
|
108
|
+
return False
|
|
109
|
+
|
|
110
|
+
try:
|
|
111
|
+
status_value = pydash.get(response.json(), status_path)
|
|
112
|
+
except ValueError as e:
|
|
113
|
+
logger.error(f"Invalid JSON response in status check: {e}")
|
|
114
|
+
return False
|
|
115
|
+
|
|
116
|
+
# Handle list values - check first element
|
|
117
|
+
if isinstance(status_value, list) and status_value:
|
|
118
|
+
status_value = status_value[0]
|
|
119
|
+
|
|
120
|
+
# Direct comparison if completed value specified
|
|
121
|
+
if status_completed_value is not None:
|
|
122
|
+
return status_value == status_completed_value
|
|
123
|
+
|
|
124
|
+
# Otherwise, check truthiness
|
|
125
|
+
return bool(status_value)
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
def _infer_data_type(request_path: str, response: Response) -> SupportedDataType:
|
|
129
|
+
"""Infer data type from response characteristics."""
|
|
130
|
+
content_type = response.headers.get(CONTENT_TYPE, "").lower()
|
|
131
|
+
|
|
132
|
+
# Check for attachment content types first
|
|
133
|
+
if any(ct in content_type for ct in ATTACHMENT_CONTENT_TYPES):
|
|
134
|
+
return SupportedDataType.attachment
|
|
135
|
+
|
|
136
|
+
# Check content-type header (most reliable)
|
|
137
|
+
for content_type_pattern, data_type in CONTENT_TYPE_MAP.items():
|
|
138
|
+
if content_type_pattern in content_type:
|
|
139
|
+
return data_type
|
|
140
|
+
|
|
141
|
+
# Check URL extension
|
|
142
|
+
path = urlparse(request_path.lower()).path
|
|
143
|
+
if path.endswith(".csv"):
|
|
144
|
+
return SupportedDataType.csv
|
|
145
|
+
|
|
146
|
+
# Try parsing as JSON
|
|
147
|
+
try:
|
|
148
|
+
response.json()
|
|
149
|
+
return SupportedDataType.json
|
|
150
|
+
except (ValueError, TypeError):
|
|
151
|
+
pass
|
|
152
|
+
|
|
153
|
+
return SupportedDataType.attachment # Preserve unknown types as raw data
|
|
154
|
+
|
|
155
|
+
@staticmethod
|
|
156
|
+
def _should_store_as_attachment(
|
|
157
|
+
response: Response, request_path: str, inferred_type: SupportedDataType
|
|
158
|
+
) -> bool:
|
|
159
|
+
"""Determine if response should be treated as an attachment."""
|
|
160
|
+
content_type = response.headers.get(CONTENT_TYPE, "").lower()
|
|
161
|
+
|
|
162
|
+
# Check attachment-specific indicators
|
|
163
|
+
if inferred_type == SupportedDataType.attachment:
|
|
164
|
+
return True
|
|
165
|
+
|
|
166
|
+
if "attachment" in response.headers.get(CONTENT_DISPOSITION, "").lower():
|
|
167
|
+
return True
|
|
168
|
+
|
|
169
|
+
if any(ct in content_type for ct in ATTACHMENT_CONTENT_TYPES):
|
|
170
|
+
return True
|
|
171
|
+
|
|
172
|
+
# Check file extension in URL
|
|
173
|
+
path = urlparse(request_path.lower()).path
|
|
174
|
+
if any(path.endswith(ext) for ext in ATTACHMENT_EXTENSIONS):
|
|
175
|
+
return True
|
|
176
|
+
|
|
177
|
+
return False
|
|
178
|
+
|
|
179
|
+
@staticmethod
|
|
180
|
+
def _extract_filename(response: Response, request_url: str) -> str:
|
|
181
|
+
"""Extract filename from response or URL."""
|
|
182
|
+
# Try Content-Disposition header using email.message for proper parsing
|
|
183
|
+
disposition = response.headers.get(CONTENT_DISPOSITION)
|
|
184
|
+
if disposition:
|
|
185
|
+
msg = Message()
|
|
186
|
+
msg[CONTENT_DISPOSITION] = disposition
|
|
187
|
+
filename = msg.get_filename()
|
|
188
|
+
if filename:
|
|
189
|
+
return filename
|
|
190
|
+
|
|
191
|
+
# Extract from URL path using os.path.basename
|
|
192
|
+
path = urlparse(request_url).path
|
|
193
|
+
if path:
|
|
194
|
+
filename = os.path.basename(path)
|
|
195
|
+
if filename:
|
|
196
|
+
return filename
|
|
197
|
+
|
|
198
|
+
return "polling_result"
|
|
199
|
+
|
|
200
|
+
@staticmethod
|
|
201
|
+
def _build_attachment_result(
|
|
202
|
+
response: Response, request_url: str, inferred_type: SupportedDataType
|
|
203
|
+
) -> PollingResult:
|
|
204
|
+
"""Build a PollingResult for attachment data."""
|
|
205
|
+
filename = PollingResponseProcessor._extract_filename(response, request_url)
|
|
206
|
+
|
|
207
|
+
# Get content type from header, or infer from filename extension
|
|
208
|
+
content_type = response.headers.get(CONTENT_TYPE)
|
|
209
|
+
|
|
210
|
+
# If no content type or it's generic, try to infer from filename extension using mimetypes
|
|
211
|
+
if not content_type or content_type in (
|
|
212
|
+
"application/octet-stream",
|
|
213
|
+
"text/plain",
|
|
214
|
+
):
|
|
215
|
+
guessed_type, _ = mimetypes.guess_type(filename)
|
|
216
|
+
if guessed_type:
|
|
217
|
+
content_type = guessed_type
|
|
218
|
+
else:
|
|
219
|
+
content_type = content_type or "application/octet-stream"
|
|
220
|
+
|
|
221
|
+
return PollingResult(
|
|
222
|
+
data=response.content,
|
|
223
|
+
result_type=PollingResultType.attachment,
|
|
224
|
+
metadata={
|
|
225
|
+
"inferred_type": inferred_type.value,
|
|
226
|
+
"content_type": content_type,
|
|
227
|
+
"filename": filename,
|
|
228
|
+
"size": len(response.content),
|
|
229
|
+
"preserved_as_attachment": True,
|
|
230
|
+
},
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
@staticmethod
|
|
234
|
+
def _parse_to_rows(
|
|
235
|
+
response: Response,
|
|
236
|
+
data_type: SupportedDataType,
|
|
237
|
+
result_path: Optional[str] = None,
|
|
238
|
+
) -> List[Row]:
|
|
239
|
+
"""Parse response to List[Row] based on data type."""
|
|
240
|
+
if data_type == SupportedDataType.json:
|
|
241
|
+
try:
|
|
242
|
+
data = response.json()
|
|
243
|
+
if result_path:
|
|
244
|
+
data = pydash.get(data, result_path)
|
|
245
|
+
if data is None:
|
|
246
|
+
raise PrivacyRequestError(
|
|
247
|
+
f"Could not extract data from response using path: {result_path}"
|
|
248
|
+
)
|
|
249
|
+
|
|
250
|
+
if isinstance(data, list):
|
|
251
|
+
return data
|
|
252
|
+
if isinstance(data, dict):
|
|
253
|
+
return [data]
|
|
254
|
+
raise PrivacyRequestError(
|
|
255
|
+
f"Expected list or dict from result request, got: {type(data)}"
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
except ValueError as e:
|
|
259
|
+
raise PrivacyRequestError(f"Invalid JSON response: {e}")
|
|
260
|
+
|
|
261
|
+
raise PrivacyRequestError(f"Cannot parse {data_type} to rows")
|