ethyca-fides 2.68.1b2__py2.py3-none-any.whl → 2.68.1b3__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.68.1b2.dist-info → ethyca_fides-2.68.1b3.dist-info}/METADATA +3 -1
- {ethyca_fides-2.68.1b2.dist-info → ethyca_fides-2.68.1b3.dist-info}/RECORD +231 -216
- fides/_version.py +3 -3
- fides/api/alembic/migrations/versions/90502bcda282_update_request_tasks_add_polling_async.py +35 -0
- fides/api/api/v1/endpoints/privacy_request_endpoints.py +1 -1
- fides/api/common_exceptions.py +12 -3
- fides/api/models/detection_discovery/core.py +6 -0
- fides/api/models/privacy_request/request_task.py +25 -0
- fides/api/schemas/privacy_center_config.py +48 -19
- fides/api/schemas/storage/storage.py +2 -0
- fides/api/service/async_dsr/__init__.py +0 -0
- fides/api/service/async_dsr/async_dsr_service.py +75 -0
- fides/api/service/connectors/saas_connector.py +5 -6
- fides/api/service/privacy_request/dsr_package/dsr_report_builder.py +6 -4
- fides/api/service/privacy_request/request_service.py +50 -2
- fides/api/service/storage/storage_uploader_service.py +80 -5
- fides/api/service/storage/streaming/__init__.py +42 -0
- fides/api/service/storage/streaming/base_storage_client.py +61 -0
- fides/api/service/storage/streaming/dsr_storage.py +98 -0
- fides/api/service/storage/streaming/retry.py +282 -0
- fides/api/service/storage/streaming/s3/__init__.py +5 -0
- fides/api/service/storage/streaming/s3/s3_storage_client.py +113 -0
- fides/api/service/storage/streaming/s3/streaming_s3.py +196 -0
- fides/api/service/storage/streaming/schemas.py +173 -0
- fides/api/service/storage/streaming/smart_open_client.py +265 -0
- fides/api/service/storage/streaming/smart_open_streaming_storage.py +998 -0
- fides/api/service/storage/streaming/storage_client_factory.py +60 -0
- fides/api/task/graph_task.py +4 -4
- fides/api/task/manual/manual_task_graph_task.py +3 -4
- fides/config/execution_settings.py +4 -0
- fides/service/privacy_request/privacy_request_service.py +1 -9
- fides/ui-build/static/admin/404.html +1 -1
- fides/ui-build/static/admin/_next/static/_BLI2ArqQzY5XnXbrcxa2/_buildManifest.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/1099-7b2085a3931da9e4.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/1138-0d846ffef62c580f.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/1345-ab756811e19ff4fc.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{1817-c90365325f8a3d75.js → 1817-fd21f1f5ef0faffa.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{1975.e5cc7a1ccd477671.js → 1975.16126463309143e3.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{2921-46f9465c2852a46b.js → 2921-0e5cc63a82e31830.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/3620-6cceae71bae5b531.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/3729-7d2d52400f1f7413.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/3855-64541570e2f838fb.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/3872-7a18d18a5e287e4e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{3923-a33633feba5e655e.js → 3923-5c87b3d7f1626678.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{401-741bb31b586b7c96.js → 401-3902e3e98790d401.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{4121-94354b50a41f8497.js → 4121-64ef70ef906bbdd0.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/431-86ad2beeb93c95c9.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/4608-70521532195124de.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/4786-53ef1662f2d0d98c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/4808-8713433c84a62efe.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/4844-351f99b6644b654e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/5258-c6f96dc740eb5fb1.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/5487-338800277d36b8d7.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/549-e6453a3526023e85.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/602-80d113e801d7407d.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{6084-02abe12327fc3dbc.js → 6084-da63f20d9416a982.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{6853-270261ef5537a106.js → 6853-1d947b75eb07188c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/6954-24f9a4f27d67b732.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/7476-a0dd03bfccf60d0c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/7630-9fbe06cfb98266fe.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{787-5ba991cad1f7664a.js → 787-3dd31844cf7fec55.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/79-dcd20e8b09501c17.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/796-8773e04b64ce2260.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/8002-dcd02da6e5649a1c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/9046-57eab238570b8bf4.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/9676.bf0a8a6ff6dfd2af.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{9826-8c81c97a72510fcf.js → 9826-756c958aecab59a2.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/9951-cdf73904a3adb27b.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{404-9174cdb70126c2c5.js → 404-dd625a559ada46ca.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{_app-2c10f6b217b7978b.js → _app-b6b09b2878b77b21.js} +136 -135
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-621416493c89ef01.js → manual-92cf5e313be1f9e2.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{multiple-0b9908c3e1dfe49e.js → multiple-d6c525ee731a2993.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems-5664a3ea796e5ffb.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/{add-vendors-5bb1b31ae8752250.js → add-vendors-78f13de90111fd80.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure-0fc678f3d6d2fcec.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{[id]-4e4d9426743b5cb4.js → [id]-126db59dc25ca326.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-experience-d72460348fadcab8.js → privacy-experience-289605267d6cce7e.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-3e7ddc252da00c98.js → [id]-e9fd9b28ac9705af.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-35a7c305beee9428.js → new-28c003b6043bd16c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-c643eff04525298e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{properties-ab96939421639153.js → properties-3ef5d01779a26455.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-baa4a2f8f08ac224.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{consent-13240e3ca77acfeb.js → consent-8d4be9e7ec7d2a35.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/{[resourceUrn]-aad6047a4604b945.js → [resourceUrn]-f27ec4578c674181.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/{[projectUrn]-bd37b407c80c6986.js → [projectUrn]-27b6c255bd9e73b6.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects-0f66dac32040519c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/{[resourceUrn]-b6b98cea25dd94fa.js → [resourceUrn]-3b938562df81c4b0.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog-ebf5e7fa4e2ffb49.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-b27c660039d951c9.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-8ce5d24af470888e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-8e35e33928abbcdc.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-21c141279e66237a.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-31e6c54794a9883e.js → [resourceUrn]-3bc6a207693fd175.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-2822a423a7ad0550.js → detection-da16e73df395ad1d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-f98dd251babb7e28.js → [resourceUrn]-04b242632a114405.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-56eb4c014f0d96a3.js → discovery-900fe50183a40d72.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{datamap-8f88dc31c5144ea8.js → datamap-4f1f7c3a9531a8f4.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/[...subfieldNames]-343294dcb10d9532.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]-1c097a0809fa5b6f.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]-b47fa2498b534719.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/new-a31f881cab25704a.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset-858c59c9e67e318d.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-67a7fe58b96ea739.js → [id]-16c28d272225afb6.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{new-90a8df230cb89877.js → new-68f502d8b0b5792c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-1eb9acb17b133fd1.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{index-876bfd7210040cec.js → index-fec557d99211f577.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/{[id]-4e286a1e501a0c73.js → [id]-e613543818d6cbd2.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations-8069f7c33695fd45.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{[id]-6e796c3fe632280b.js → [id]-4a08ca7762a19700.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{add-template-fa0f3841c5bdfdeb.js → add-template-343a965dcdb3d11e.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/messaging-3ade4c54b1c8a11e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/ant-components-9103bfb854f71410.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{AntForm-11503454a62d8d7b.js → AntForm-3b97029bd4d3c3ea.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikAntFormItem-a504941807bdb7f1.js → FormikAntFormItem-9d9beb8f0d8a278c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikControlled-0119403c8ff97f83.js → FormikControlled-84a4d8fc60f839ed.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikField-94f6d57d6c94ddf7.js → FormikField-1fccf542ab2e33bf.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/{forms-ed1a3ae09d72df89.js → forms-aa75263ae1ba67bb.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/table-migration-db334a1cbb102255.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-11c1e4545c8f528c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-192a986f61c23268.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-9216ac993d71387e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure-e55ec84d5380401d.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-48f447b31c786b80.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/properties/{[id]-41976b28503623cd.js → [id]-a74b51b704b80cb2.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/properties/{add-property-cb438d8f5ec6007a.js → add-property-8d23f0c55ff6510a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{properties-b6db7036993709b3.js → properties-77acceac4f99e7af.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/reporting/{datamap-4bc3e281409265cc.js → datamap-e60d398e255f4e00.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/alpha-6aad3f563ed03b3f.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/about-c1b8f3606d160bb1.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/[purpose_id]-d9f7f78810d58d08.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-ee2c7dde99b1dafb.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-a4dad8ca9de2d07b.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-386368bf7cb31771.js → domain-records-31c270d228e00581.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/domains-996b3f250dd3ea1f.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/email-templates-ee94981326ddcbf4.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-b41fb5ad277088ab.js → locations-0b831c58966782b8.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/organization-94271ba4a224a353.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{regulations-a94dfeea43fbca7d.js → regulations-41b8136e50320fd3.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/test-datasets-52b45569cbc82e60.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-18b316e2dad73731.js → [id]-36d74e93e54aabaf.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems-24dfc8e2279ced2e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-d9675cf5e6083b27.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/{[id]-3237881945acc0ee.js → [id]-866826d7959df487.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-a3a50d9d79066935.js → user-management-e63b61a8f99ccd57.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{webpack-69658aeaf6155d89.js → webpack-6d0a487039bcf30c.js} +1 -1
- fides/ui-build/static/admin/_next/static/css/{a72179b1754aadd3.css → 92441453b27e9c34.css} +1 -1
- 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/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/chunks/203-0c6cadcda98bdd33.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/3450-9314e1b15df8a8da.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/3855-4267fd8193e7f525.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/3872-ac5feefd40b61ae3.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/409-5bc4369b80a8c11d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4230-1ebc8c0ab293a077.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/431-a34d7ceff17c2169.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4608-557fb24665b2e4bf.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/5309-ffdec884eec79d29.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/5574-831167a8da90e2e6.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/6662-499c189f932a35aa.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/6780-7d28e030f6516e5d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/6882-7cc1d14e27a80c10.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/6954-7784e8d5ad6b8110.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7476-4de465016d3433b4.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7630-2a5c57787632693d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7725-c79513b04113112b.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/79-98cfab20bb831137.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/796-0b768155bf20505f.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/8735-f84afcc50885883c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9046-97a972cc8a8ed24d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9226-318dadf1c050ecda.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9676.9e6828b42ef05e06.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9951-4df2b67e0def5500.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems-18e96ce81dab51a4.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure-54d7c7310763c66d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-6bc3b73a21576869.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-fe3d6887fecf0f86.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects-e4770acf7044e2f5.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog-0db635c3483c9da8.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-0c0e0a7798345541.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-3c56e5fe072a44c6.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-58827eb86516931f.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-6a90131dcecd694c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/[...subfieldNames]-145fe9e4cfcb231d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]-8a1e5d140785c1e9.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]-227b5db4b472a6a7.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/new-8401f17fe5d9a1dc.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset-7d77b3ad069be268.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-cfb25b02abb8da71.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations-3fdc55d4c129e618.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/messaging-8f9c006b6166f002.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/ant-components-6ba7ae4f26c06cb0.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/poc/table-migration-e8db3ad525e7ddbd.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-c14dd24592369467.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-100d7d03930629a8.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-6f8d1b3ec83cfcf0.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure-3ce15577435d47cb.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-709bcb0bc6a5382d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/alpha-1ea40fcd6b4268bf.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/about-65c7600fadc6e55a.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/[purpose_id]-33dab986141b3663.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-1195042727c399ed.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-71b98858ecb4e097.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/domains-cf427e04f862b5d2.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/email-templates-eabeeec5bf2773c6.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/organization-ee56698ae3a6a78b.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/test-datasets-0e2e98cc38ee5499.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems-c32589c86081b750.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-a8f09bf8f3204ca7.js +0 -1
- fides/ui-build/static/admin/_next/static/qvk5eMANVfwYkdURE7fgG/_buildManifest.js +0 -1
- {ethyca_fides-2.68.1b2.dist-info → ethyca_fides-2.68.1b3.dist-info}/WHEEL +0 -0
- {ethyca_fides-2.68.1b2.dist-info → ethyca_fides-2.68.1b3.dist-info}/entry_points.txt +0 -0
- {ethyca_fides-2.68.1b2.dist-info → ethyca_fides-2.68.1b3.dist-info}/licenses/LICENSE +0 -0
- {ethyca_fides-2.68.1b2.dist-info → ethyca_fides-2.68.1b3.dist-info}/top_level.txt +0 -0
- /fides/ui-build/static/admin/_next/static/{qvk5eMANVfwYkdURE7fgG → _BLI2ArqQzY5XnXbrcxa2}/_ssgManifest.js +0 -0
|
@@ -14,6 +14,7 @@ from fides.api.schemas.storage.storage import (
|
|
|
14
14
|
StorageDetails,
|
|
15
15
|
StorageType,
|
|
16
16
|
)
|
|
17
|
+
from fides.api.service.storage.streaming.s3.streaming_s3 import upload_to_s3_streaming
|
|
17
18
|
from fides.api.tasks.storage import upload_to_gcs, upload_to_local, upload_to_s3
|
|
18
19
|
|
|
19
20
|
|
|
@@ -33,6 +34,8 @@ def upload(
|
|
|
33
34
|
:param storage_key: Key representing where to upload data
|
|
34
35
|
:return str representing location of upload (url or simply a description of where to find the data)
|
|
35
36
|
"""
|
|
37
|
+
logger.debug("upload called with storage_key: {}", storage_key)
|
|
38
|
+
|
|
36
39
|
config: Optional[StorageConfig] = StorageConfig.get_by(
|
|
37
40
|
db=db, field="key", value=storage_key
|
|
38
41
|
)
|
|
@@ -40,16 +43,41 @@ def upload(
|
|
|
40
43
|
if config is None:
|
|
41
44
|
logger.warning("Storage type not found: {}", storage_key)
|
|
42
45
|
raise StorageUploadError(f"Storage type not found: {storage_key}")
|
|
46
|
+
|
|
47
|
+
logger.debug(
|
|
48
|
+
"Retrieved storage config: key={}, type={}, has_secrets={}",
|
|
49
|
+
config.key,
|
|
50
|
+
config.type,
|
|
51
|
+
config.secrets is not None,
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
if config.secrets:
|
|
55
|
+
logger.debug("Storage config secrets type: {}", type(config.secrets))
|
|
56
|
+
if isinstance(config.secrets, dict):
|
|
57
|
+
logger.debug("Storage config secrets keys: {}", list(config.secrets.keys()))
|
|
58
|
+
else:
|
|
59
|
+
logger.debug("Storage config secrets is not a dict: {}", config.secrets)
|
|
60
|
+
else:
|
|
61
|
+
logger.warning("Storage config has no secrets!")
|
|
62
|
+
|
|
43
63
|
uploader: Any = _get_uploader_from_config_type(config.type) # type: ignore
|
|
64
|
+
logger.debug(
|
|
65
|
+
"Using uploader: {}",
|
|
66
|
+
uploader.__name__ if hasattr(uploader, "__name__") else type(uploader),
|
|
67
|
+
)
|
|
68
|
+
|
|
44
69
|
return uploader(db, config, data, privacy_request)
|
|
45
70
|
|
|
46
71
|
|
|
47
|
-
def get_extension(resp_format: ResponseFormat) -> str:
|
|
72
|
+
def get_extension(resp_format: ResponseFormat, enable_streaming: bool = False) -> str:
|
|
48
73
|
"""
|
|
49
74
|
Determine file extension for various response formats.
|
|
50
75
|
|
|
51
76
|
CSV's and HTML reports are zipped together before uploading to s3.
|
|
52
77
|
"""
|
|
78
|
+
if enable_streaming:
|
|
79
|
+
return "zip"
|
|
80
|
+
|
|
53
81
|
if resp_format == ResponseFormat.csv:
|
|
54
82
|
return "zip"
|
|
55
83
|
|
|
@@ -62,7 +90,9 @@ def get_extension(resp_format: ResponseFormat) -> str:
|
|
|
62
90
|
raise NotImplementedError(f"No extension defined for {resp_format}")
|
|
63
91
|
|
|
64
92
|
|
|
65
|
-
def _construct_file_key(
|
|
93
|
+
def _construct_file_key(
|
|
94
|
+
request_id: str, config: StorageConfig, enable_streaming: bool = False
|
|
95
|
+
) -> str:
|
|
66
96
|
"""Constructs file key based on desired naming convention and request id, e.g. 23847234.json"""
|
|
67
97
|
naming = config.details.get(
|
|
68
98
|
StorageDetails.NAMING.value, FileNaming.request_id.value
|
|
@@ -70,7 +100,7 @@ def _construct_file_key(request_id: str, config: StorageConfig) -> str:
|
|
|
70
100
|
if naming != FileNaming.request_id.value:
|
|
71
101
|
raise ValueError(f"File naming of {naming} not supported")
|
|
72
102
|
|
|
73
|
-
return f"{request_id}.{get_extension(config.format)}" # type: ignore
|
|
103
|
+
return f"{request_id}.{get_extension(config.format, enable_streaming)}" # type: ignore
|
|
74
104
|
|
|
75
105
|
|
|
76
106
|
def _get_uploader_from_config_type(storage_type: StorageType) -> Any:
|
|
@@ -88,13 +118,58 @@ def _s3_uploader(
|
|
|
88
118
|
data: Dict,
|
|
89
119
|
privacy_request: PrivacyRequest,
|
|
90
120
|
) -> str:
|
|
91
|
-
"""
|
|
92
|
-
|
|
121
|
+
"""
|
|
122
|
+
Constructs necessary info needed for s3 before calling upload.
|
|
123
|
+
If `enable_streaming` is configured in the storage config, we use a streaming approach for better memory efficiency.
|
|
124
|
+
Otherwise we fall back to the traditional upload method.
|
|
125
|
+
"""
|
|
126
|
+
logger.debug(
|
|
127
|
+
"_s3_uploader called with config: key={}, type={}, has_secrets={}",
|
|
128
|
+
config.key,
|
|
129
|
+
config.type,
|
|
130
|
+
config.secrets is not None,
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
if config.secrets:
|
|
134
|
+
logger.debug(
|
|
135
|
+
"Config secrets keys: {}",
|
|
136
|
+
(
|
|
137
|
+
list(config.secrets.keys())
|
|
138
|
+
if isinstance(config.secrets, dict)
|
|
139
|
+
else "Not a dict"
|
|
140
|
+
),
|
|
141
|
+
)
|
|
142
|
+
logger.debug("Config secrets type: {}", type(config.secrets))
|
|
143
|
+
else:
|
|
144
|
+
logger.warning("Config secrets is None or empty!")
|
|
145
|
+
|
|
146
|
+
enable_streaming = config.details.get(StorageDetails.ENABLE_STREAMING.value, False)
|
|
147
|
+
file_key: str = _construct_file_key(privacy_request.id, config, enable_streaming)
|
|
93
148
|
|
|
94
149
|
bucket_name = config.details[StorageDetails.BUCKET.value]
|
|
95
150
|
auth_method = config.details[StorageDetails.AUTH_METHOD.value]
|
|
96
151
|
document = None
|
|
97
152
|
|
|
153
|
+
if enable_streaming:
|
|
154
|
+
file_key = f"{privacy_request.id}.zip"
|
|
155
|
+
# Use streaming upload for better memory efficiency
|
|
156
|
+
logger.debug("Using streaming S3 upload for {}", file_key)
|
|
157
|
+
logger.debug("Calling upload_to_s3_streaming with secrets: {}", config.secrets)
|
|
158
|
+
return upload_to_s3_streaming(
|
|
159
|
+
config.secrets, # type: ignore
|
|
160
|
+
data,
|
|
161
|
+
bucket_name,
|
|
162
|
+
file_key,
|
|
163
|
+
config.format.value, # type: ignore
|
|
164
|
+
privacy_request,
|
|
165
|
+
document,
|
|
166
|
+
auth_method,
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
file_key = _construct_file_key(privacy_request.id, config)
|
|
170
|
+
|
|
171
|
+
# Fall back to traditional upload method
|
|
172
|
+
logger.debug("Using traditional S3 upload for {}", file_key)
|
|
98
173
|
return upload_to_s3(
|
|
99
174
|
config.secrets, # type: ignore
|
|
100
175
|
data,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Streaming storage module for efficient cloud-to-cloud data transfer."""
|
|
2
|
+
|
|
3
|
+
from .base_storage_client import BaseStorageClient
|
|
4
|
+
from .retry import (
|
|
5
|
+
PermanentError,
|
|
6
|
+
RetryableError,
|
|
7
|
+
RetryConfig,
|
|
8
|
+
TransientError,
|
|
9
|
+
create_retry_config_from_settings,
|
|
10
|
+
is_transient_error,
|
|
11
|
+
retry_cloud_storage_operation,
|
|
12
|
+
retry_with_backoff,
|
|
13
|
+
)
|
|
14
|
+
from .s3.s3_storage_client import S3StorageClient
|
|
15
|
+
from .schemas import AttachmentInfo, StorageUploadConfig, StreamingBufferConfig
|
|
16
|
+
from .smart_open_client import SmartOpenStorageClient
|
|
17
|
+
from .smart_open_streaming_storage import SmartOpenStreamingStorage
|
|
18
|
+
from .storage_client_factory import StorageClientFactory
|
|
19
|
+
|
|
20
|
+
__all__ = [
|
|
21
|
+
# Base classes and interfaces
|
|
22
|
+
"BaseStorageClient",
|
|
23
|
+
"StorageClientFactory",
|
|
24
|
+
# Provider-specific clients
|
|
25
|
+
"S3StorageClient",
|
|
26
|
+
# Main clients
|
|
27
|
+
"SmartOpenStorageClient",
|
|
28
|
+
"SmartOpenStreamingStorage",
|
|
29
|
+
# Schemas
|
|
30
|
+
"StorageUploadConfig",
|
|
31
|
+
"StreamingBufferConfig",
|
|
32
|
+
"AttachmentInfo",
|
|
33
|
+
# Retry utilities
|
|
34
|
+
"RetryConfig",
|
|
35
|
+
"RetryableError",
|
|
36
|
+
"TransientError",
|
|
37
|
+
"PermanentError",
|
|
38
|
+
"retry_with_backoff",
|
|
39
|
+
"retry_cloud_storage_operation",
|
|
40
|
+
"create_retry_config_from_settings",
|
|
41
|
+
"is_transient_error",
|
|
42
|
+
]
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""Base abstract class for cloud storage clients."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from typing import Any, Optional
|
|
7
|
+
|
|
8
|
+
from fideslang.validation import AnyHttpUrlString
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class BaseStorageClient(ABC):
|
|
12
|
+
"""Abstract base class for cloud storage clients.
|
|
13
|
+
|
|
14
|
+
This class defines the interface that all provider-specific storage clients
|
|
15
|
+
must implement. It provides a common contract for URI building, transport
|
|
16
|
+
parameters, and presigned URL generation.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
def __init__(self, storage_secrets: Any):
|
|
20
|
+
"""Initialize the storage client with secrets.
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
storage_secrets: Provider-specific storage credentials and configuration.
|
|
24
|
+
Derived classes will specify the exact type they expect.
|
|
25
|
+
"""
|
|
26
|
+
self.storage_secrets = storage_secrets
|
|
27
|
+
|
|
28
|
+
@abstractmethod
|
|
29
|
+
def build_uri(self, bucket: str, key: str) -> str:
|
|
30
|
+
"""Build the URI for the storage location.
|
|
31
|
+
|
|
32
|
+
Args:
|
|
33
|
+
bucket: Storage bucket/container name
|
|
34
|
+
key: Object key/path
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
URI string for smart-open
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
@abstractmethod
|
|
41
|
+
def get_transport_params(self) -> dict[str, Any]:
|
|
42
|
+
"""Get transport parameters for smart-open.
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
Dictionary of transport parameters for smart-open
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
@abstractmethod
|
|
49
|
+
def generate_presigned_url(
|
|
50
|
+
self, bucket: str, key: str, ttl_seconds: Optional[int] = None
|
|
51
|
+
) -> AnyHttpUrlString:
|
|
52
|
+
"""Generate a presigned URL for the object.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
bucket: Storage bucket/container name
|
|
56
|
+
key: Object key/path
|
|
57
|
+
ttl_seconds: Time to live in seconds
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
Presigned URL for the object
|
|
61
|
+
"""
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import zipfile
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from io import BytesIO
|
|
6
|
+
from typing import Any, Generator, Tuple
|
|
7
|
+
|
|
8
|
+
from loguru import logger
|
|
9
|
+
from stream_zip import _ZIP_32_TYPE
|
|
10
|
+
|
|
11
|
+
from fides.api.service.storage.streaming.schemas import AttachmentProcessingInfo
|
|
12
|
+
from fides.api.service.storage.streaming.smart_open_client import SmartOpenStorageClient
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def stream_dsr_buffer_to_storage(
|
|
16
|
+
storage_client: SmartOpenStorageClient,
|
|
17
|
+
bucket_name: str,
|
|
18
|
+
file_key: str,
|
|
19
|
+
dsr_buffer: BytesIO,
|
|
20
|
+
content_type: str = "application/zip",
|
|
21
|
+
) -> None:
|
|
22
|
+
"""Stream DSR buffer to storage using smart-open streaming.
|
|
23
|
+
|
|
24
|
+
This function handles only the storage streaming concern, accepting a pre-generated
|
|
25
|
+
DSR buffer to maintain clear separation of concerns.
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
storage_client: The storage client for streaming uploads
|
|
29
|
+
bucket_name: Storage bucket name
|
|
30
|
+
file_key: File key in storage
|
|
31
|
+
dsr_buffer: Pre-generated DSR report buffer (BytesIO)
|
|
32
|
+
content_type: MIME type for the uploaded file (defaults to application/zip)
|
|
33
|
+
"""
|
|
34
|
+
# Get the content from the buffer
|
|
35
|
+
content = dsr_buffer.getvalue()
|
|
36
|
+
try:
|
|
37
|
+
# Use smart-open's streaming upload for efficient memory usage
|
|
38
|
+
with storage_client.stream_upload(
|
|
39
|
+
bucket_name, file_key, content_type=content_type
|
|
40
|
+
) as upload_stream:
|
|
41
|
+
upload_stream.write(content)
|
|
42
|
+
|
|
43
|
+
except Exception as e:
|
|
44
|
+
logger.error("Failed to upload DSR report using smart-open streaming: {}", e)
|
|
45
|
+
raise e
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def create_dsr_report_files_generator(
|
|
49
|
+
dsr_buffer: BytesIO,
|
|
50
|
+
all_attachments: list["AttachmentProcessingInfo"],
|
|
51
|
+
bucket_name: str,
|
|
52
|
+
max_workers: int,
|
|
53
|
+
batch_size: int,
|
|
54
|
+
) -> Generator[
|
|
55
|
+
Tuple[str, datetime, int, Any, Generator[bytes, None, None]], None, None
|
|
56
|
+
]:
|
|
57
|
+
"""Create a ZIP generator for DSR report HTML files only.
|
|
58
|
+
|
|
59
|
+
This method extracts and yields the HTML files from a DSR report buffer.
|
|
60
|
+
Note: This function only handles the DSR report files (HTML, CSS, etc.).
|
|
61
|
+
The caller is responsible for combining this with attachment files to create
|
|
62
|
+
the complete ZIP.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
dsr_buffer: The DSR report buffer (ZIP file as BytesIO)
|
|
66
|
+
all_attachments: List of validated attachments (used for logging only)
|
|
67
|
+
bucket_name: Storage bucket name (used for logging only)
|
|
68
|
+
max_workers: Maximum parallel workers (used for logging only)
|
|
69
|
+
batch_size: Number of attachments to process in each batch (used for logging only)
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
Generator yielding DSR report files in stream_zip format
|
|
73
|
+
"""
|
|
74
|
+
logger.debug(
|
|
75
|
+
f"Creating DSR report files generator with {len(all_attachments)} attachments"
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
# Reset buffer position to ensure we can read from it
|
|
79
|
+
dsr_buffer.seek(0)
|
|
80
|
+
|
|
81
|
+
# Extract and yield the DSR report files from the buffer
|
|
82
|
+
# The dsr_buffer is already a ZIP file, so we need to extract and re-yield its contents
|
|
83
|
+
with zipfile.ZipFile(dsr_buffer) as dsr_zip:
|
|
84
|
+
for file_info in dsr_zip.filelist:
|
|
85
|
+
if not file_info.is_dir():
|
|
86
|
+
# Read the file content and yield it in stream_zip format
|
|
87
|
+
content = dsr_zip.read(file_info.filename)
|
|
88
|
+
|
|
89
|
+
def content_generator(
|
|
90
|
+
file_content: bytes,
|
|
91
|
+
) -> Generator[bytes, None, None]:
|
|
92
|
+
yield file_content
|
|
93
|
+
|
|
94
|
+
yield file_info.filename, datetime.now(), 0o644, _ZIP_32_TYPE(), content_generator(
|
|
95
|
+
content
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
logger.debug("DSR report files extracted and ready for ZIP creation")
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import random
|
|
4
|
+
import time
|
|
5
|
+
from functools import wraps
|
|
6
|
+
from typing import Any, Callable, Optional, Type
|
|
7
|
+
|
|
8
|
+
from loguru import logger
|
|
9
|
+
|
|
10
|
+
from fides.config import CONFIG
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class RetryableError(Exception):
|
|
14
|
+
"""Base exception for errors that should trigger retries."""
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TransientError(RetryableError):
|
|
18
|
+
"""Exception for transient errors that should be retried."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PermanentError(RetryableError):
|
|
22
|
+
"""Exception for permanent errors that should not be retried."""
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class RetryConfig:
|
|
26
|
+
"""Configuration for retry behavior."""
|
|
27
|
+
|
|
28
|
+
def __init__(
|
|
29
|
+
self,
|
|
30
|
+
max_retries: int = 3,
|
|
31
|
+
base_delay: float = 1.0,
|
|
32
|
+
max_delay: float = 60.0,
|
|
33
|
+
backoff_factor: float = 2.0,
|
|
34
|
+
jitter: bool = True,
|
|
35
|
+
retry_on_exceptions: Optional[tuple[Type[Exception], ...]] = None,
|
|
36
|
+
):
|
|
37
|
+
self.max_retries = max_retries
|
|
38
|
+
self.base_delay = base_delay
|
|
39
|
+
self.max_delay = max_delay
|
|
40
|
+
self.backoff_factor = backoff_factor
|
|
41
|
+
self.jitter = jitter
|
|
42
|
+
self.retry_on_exceptions = retry_on_exceptions or (Exception,)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def is_transient_error(error: Exception) -> bool:
|
|
46
|
+
"""
|
|
47
|
+
Determine if an error is transient and should be retried.
|
|
48
|
+
|
|
49
|
+
This is a cloud-agnostic implementation that can be extended
|
|
50
|
+
with provider-specific logic.
|
|
51
|
+
"""
|
|
52
|
+
# Check if this is our custom TransientError
|
|
53
|
+
if isinstance(error, TransientError):
|
|
54
|
+
return True
|
|
55
|
+
|
|
56
|
+
error_str = str(error).lower()
|
|
57
|
+
|
|
58
|
+
# Common transient error patterns across cloud providers
|
|
59
|
+
transient_patterns = [
|
|
60
|
+
"timeout",
|
|
61
|
+
"timed out",
|
|
62
|
+
"connection",
|
|
63
|
+
"network",
|
|
64
|
+
"temporary",
|
|
65
|
+
"throttling",
|
|
66
|
+
"rate limit",
|
|
67
|
+
"too many requests",
|
|
68
|
+
"service unavailable",
|
|
69
|
+
"internal server error",
|
|
70
|
+
"bad gateway",
|
|
71
|
+
"gateway timeout",
|
|
72
|
+
"request timeout",
|
|
73
|
+
"connection reset",
|
|
74
|
+
"broken pipe",
|
|
75
|
+
]
|
|
76
|
+
|
|
77
|
+
return any(pattern in error_str for pattern in transient_patterns)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def calculate_backoff_delay(
|
|
81
|
+
attempt: int,
|
|
82
|
+
base_delay: float,
|
|
83
|
+
max_delay: float,
|
|
84
|
+
backoff_factor: float,
|
|
85
|
+
jitter: bool = True,
|
|
86
|
+
) -> float:
|
|
87
|
+
"""
|
|
88
|
+
Calculate exponential backoff delay with optional jitter.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
attempt: Current attempt number (0-based)
|
|
92
|
+
base_delay: Base delay in seconds
|
|
93
|
+
max_delay: Maximum delay in seconds
|
|
94
|
+
backoff_factor: Multiplier for each attempt
|
|
95
|
+
jitter: Whether to add random jitter to prevent thundering herd
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Delay in seconds before next retry
|
|
99
|
+
"""
|
|
100
|
+
if attempt == 0:
|
|
101
|
+
return 0
|
|
102
|
+
|
|
103
|
+
# Exponential backoff: base_delay * (backoff_factor ^ attempt)
|
|
104
|
+
delay = base_delay * (backoff_factor ** (attempt - 1))
|
|
105
|
+
|
|
106
|
+
# Cap at maximum delay
|
|
107
|
+
delay = min(delay, max_delay)
|
|
108
|
+
|
|
109
|
+
# Add jitter to prevent multiple retries from synchronizing
|
|
110
|
+
if jitter:
|
|
111
|
+
jitter_amount = delay * 0.1 # 10% jitter
|
|
112
|
+
delay += random.uniform(-jitter_amount, jitter_amount)
|
|
113
|
+
delay = max(0, delay) # Ensure non-negative
|
|
114
|
+
|
|
115
|
+
return delay
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def retry_with_backoff(
|
|
119
|
+
retry_config: Optional[RetryConfig] = None,
|
|
120
|
+
operation_name: str = "operation",
|
|
121
|
+
) -> Callable:
|
|
122
|
+
"""
|
|
123
|
+
Decorator for retrying operations with exponential backoff.
|
|
124
|
+
|
|
125
|
+
This is a cloud-agnostic retry mechanism that can be extended
|
|
126
|
+
with provider-specific retry logic.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
retry_config: Configuration for retry behavior
|
|
130
|
+
operation_name: Name of the operation for logging
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
Decorated function with retry logic
|
|
134
|
+
"""
|
|
135
|
+
if retry_config is None:
|
|
136
|
+
# Use default configuration from settings
|
|
137
|
+
settings = CONFIG.execution
|
|
138
|
+
retry_config = RetryConfig(
|
|
139
|
+
max_retries=settings.task_retry_count,
|
|
140
|
+
base_delay=settings.task_retry_delay,
|
|
141
|
+
backoff_factor=settings.task_retry_backoff,
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
def decorator(func: Callable) -> Callable:
|
|
145
|
+
@wraps(func)
|
|
146
|
+
def wrapper(*args: Any, **kwargs: Any) -> Any:
|
|
147
|
+
last_exception: Optional[Exception] = None
|
|
148
|
+
|
|
149
|
+
for attempt in range(retry_config.max_retries + 1):
|
|
150
|
+
try:
|
|
151
|
+
# Track retry attempts
|
|
152
|
+
if attempt > 0:
|
|
153
|
+
logger.debug(
|
|
154
|
+
"Retry attempt {}/{} for {}",
|
|
155
|
+
attempt,
|
|
156
|
+
retry_config.max_retries,
|
|
157
|
+
operation_name,
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
return func(*args, **kwargs)
|
|
161
|
+
|
|
162
|
+
except Exception as e:
|
|
163
|
+
last_exception = e
|
|
164
|
+
|
|
165
|
+
# Check if this is a permanent error that shouldn't be retried
|
|
166
|
+
if isinstance(e, PermanentError):
|
|
167
|
+
logger.error(
|
|
168
|
+
"Permanent error in {}: {}. Not retrying.",
|
|
169
|
+
operation_name,
|
|
170
|
+
e,
|
|
171
|
+
)
|
|
172
|
+
raise
|
|
173
|
+
|
|
174
|
+
# Check if we should retry this error
|
|
175
|
+
should_retry = (
|
|
176
|
+
attempt < retry_config.max_retries
|
|
177
|
+
and isinstance(e, retry_config.retry_on_exceptions)
|
|
178
|
+
and is_transient_error(e)
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
if not should_retry:
|
|
182
|
+
logger.error(
|
|
183
|
+
"Non-retryable error in {}: {}. Not retrying.",
|
|
184
|
+
operation_name,
|
|
185
|
+
e,
|
|
186
|
+
)
|
|
187
|
+
raise
|
|
188
|
+
|
|
189
|
+
# Calculate delay for next retry
|
|
190
|
+
delay = calculate_backoff_delay(
|
|
191
|
+
attempt + 1,
|
|
192
|
+
retry_config.base_delay,
|
|
193
|
+
retry_config.max_delay,
|
|
194
|
+
retry_config.backoff_factor,
|
|
195
|
+
retry_config.jitter,
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
logger.warning(
|
|
199
|
+
"Transient error in {} (attempt {}/{}): {}. "
|
|
200
|
+
"Retrying in {:.1f} seconds...",
|
|
201
|
+
operation_name,
|
|
202
|
+
attempt + 1,
|
|
203
|
+
retry_config.max_retries + 1,
|
|
204
|
+
e,
|
|
205
|
+
delay,
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# Sleep before retry
|
|
209
|
+
time.sleep(delay)
|
|
210
|
+
|
|
211
|
+
# If we get here, all retries were exhausted
|
|
212
|
+
logger.error(
|
|
213
|
+
"Operation {} failed after {} retry attempts. Last error: {}",
|
|
214
|
+
operation_name,
|
|
215
|
+
retry_config.max_retries,
|
|
216
|
+
last_exception,
|
|
217
|
+
)
|
|
218
|
+
if last_exception is not None:
|
|
219
|
+
raise last_exception
|
|
220
|
+
|
|
221
|
+
raise RuntimeError(
|
|
222
|
+
f"Operation {operation_name} failed after {retry_config.max_retries} retry attempts"
|
|
223
|
+
)
|
|
224
|
+
|
|
225
|
+
return wrapper
|
|
226
|
+
|
|
227
|
+
return decorator
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def retry_cloud_storage_operation(
|
|
231
|
+
provider: str = "cloud storage",
|
|
232
|
+
operation_name: str = "storage operation",
|
|
233
|
+
max_retries: int = 3,
|
|
234
|
+
base_delay: float = 1.0,
|
|
235
|
+
max_delay: float = 60.0,
|
|
236
|
+
backoff_factor: float = 2.0,
|
|
237
|
+
) -> Callable:
|
|
238
|
+
"""
|
|
239
|
+
Cloud-agnostic retry decorator for storage operations.
|
|
240
|
+
|
|
241
|
+
This decorator provides retry logic that works across different
|
|
242
|
+
cloud storage providers while allowing provider-specific optimizations.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
provider: Name of the cloud storage provider
|
|
246
|
+
operation_name: Name of the storage operation
|
|
247
|
+
max_retries: Maximum number of retry attempts
|
|
248
|
+
base_delay: Base delay in seconds
|
|
249
|
+
max_delay: Maximum delay in seconds
|
|
250
|
+
backoff_factor: Multiplier for each attempt
|
|
251
|
+
|
|
252
|
+
Returns:
|
|
253
|
+
Decorated function with cloud-agnostic retry logic
|
|
254
|
+
"""
|
|
255
|
+
retry_config = RetryConfig(
|
|
256
|
+
max_retries=max_retries,
|
|
257
|
+
base_delay=base_delay,
|
|
258
|
+
max_delay=max_delay,
|
|
259
|
+
backoff_factor=backoff_factor,
|
|
260
|
+
jitter=True,
|
|
261
|
+
)
|
|
262
|
+
|
|
263
|
+
return retry_with_backoff(
|
|
264
|
+
retry_config=retry_config, operation_name=f"{provider} {operation_name}"
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
def create_retry_config_from_settings() -> RetryConfig:
|
|
269
|
+
"""
|
|
270
|
+
Create a RetryConfig instance from application settings.
|
|
271
|
+
|
|
272
|
+
Returns:
|
|
273
|
+
RetryConfig configured with application defaults
|
|
274
|
+
"""
|
|
275
|
+
settings = CONFIG.execution
|
|
276
|
+
return RetryConfig(
|
|
277
|
+
max_retries=settings.task_retry_count,
|
|
278
|
+
base_delay=settings.task_retry_delay,
|
|
279
|
+
backoff_factor=settings.task_retry_backoff,
|
|
280
|
+
max_delay=60.0, # Cap at 1 minute
|
|
281
|
+
jitter=True,
|
|
282
|
+
)
|