ethyca-fides 2.68.1b1__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.1b1.dist-info → ethyca_fides-2.68.1b3.dist-info}/METADATA +3 -1
- {ethyca_fides-2.68.1b1.dist-info → ethyca_fides-2.68.1b3.dist-info}/RECORD +247 -224
- fides/_version.py +3 -3
- fides/api/alembic/migrations/versions/3baf42d251a6_add_generic_taxonomy_models.py +239 -0
- fides/api/alembic/migrations/versions/90502bcda282_update_request_tasks_add_polling_async.py +35 -0
- fides/api/api/v1/endpoints/generic_overrides.py +64 -167
- fides/api/api/v1/endpoints/privacy_request_endpoints.py +1 -1
- fides/api/common_exceptions.py +12 -3
- fides/api/db/base.py +6 -0
- fides/api/models/detection_discovery/core.py +6 -0
- fides/api/models/privacy_request/request_task.py +25 -0
- fides/api/models/taxonomy.py +275 -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/deps.py +5 -0
- fides/api/service/privacy_request/dsr_package/dsr_report_builder.py +6 -4
- fides/api/service/privacy_request/request_service.py +56 -3
- 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 +14 -4
- fides/api/util/connection_type.py +68 -33
- fides/config/execution_settings.py +4 -0
- fides/data/sample_project/docker-compose.yml +3 -3
- fides/service/privacy_request/privacy_request_service.py +1 -9
- fides/service/taxonomy/__init__.py +0 -0
- fides/service/taxonomy/handlers/__init__.py +11 -0
- fides/service/taxonomy/handlers/base.py +42 -0
- fides/service/taxonomy/handlers/legacy_handler.py +95 -0
- fides/service/taxonomy/taxonomy_service.py +261 -0
- fides/service/taxonomy/utils.py +160 -0
- 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-65723cd4b8fc36ac.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]-766e57bcf38b5b1e.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/92441453b27e9c34.css +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].html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
- fides/ui-build/static/admin/data-discovery/activity.html +1 -1
- fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-discovery/detection.html +1 -1
- fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
- fides/ui-build/static/admin/datamap.html +1 -1
- fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
- fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
- fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
- fides/ui-build/static/admin/dataset/new.html +1 -1
- fides/ui-build/static/admin/dataset.html +1 -1
- fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
- fides/ui-build/static/admin/datastore-connection/new.html +1 -1
- fides/ui-build/static/admin/datastore-connection.html +1 -1
- fides/ui-build/static/admin/index.html +1 -1
- fides/ui-build/static/admin/integrations/[id].html +1 -1
- fides/ui-build/static/admin/integrations.html +1 -1
- fides/ui-build/static/admin/lib/fides-preview.js +1 -1
- fides/ui-build/static/admin/lib/fides-tcf.js +2 -2
- fides/ui-build/static/admin/lib/fides.js +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-53a763e49ce34a74.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-f43a988542813110.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/css/e1628f15dd5f019b.css +0 -1
- fides/ui-build/static/admin/_next/static/tzF4yti8NslASlGnxnZ8m/_buildManifest.js +0 -1
- {ethyca_fides-2.68.1b1.dist-info → ethyca_fides-2.68.1b3.dist-info}/WHEEL +0 -0
- {ethyca_fides-2.68.1b1.dist-info → ethyca_fides-2.68.1b3.dist-info}/entry_points.txt +0 -0
- {ethyca_fides-2.68.1b1.dist-info → ethyca_fides-2.68.1b3.dist-info}/licenses/LICENSE +0 -0
- {ethyca_fides-2.68.1b1.dist-info → ethyca_fides-2.68.1b3.dist-info}/top_level.txt +0 -0
- /fides/ui-build/static/admin/_next/static/{tzF4yti8NslASlGnxnZ8m → _BLI2ArqQzY5XnXbrcxa2}/_ssgManifest.js +0 -0
|
@@ -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
|
+
)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""S3-specific storage client implementation."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Any, Optional
|
|
6
|
+
|
|
7
|
+
from fideslang.validation import AnyHttpUrlString
|
|
8
|
+
from loguru import logger
|
|
9
|
+
|
|
10
|
+
from fides.api.schemas.storage.storage import AWSAuthMethod, StorageSecrets
|
|
11
|
+
from fides.api.service.storage.s3 import create_presigned_url_for_s3
|
|
12
|
+
from fides.api.service.storage.streaming.base_storage_client import BaseStorageClient
|
|
13
|
+
from fides.api.util.aws_util import get_s3_client
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class S3StorageClient(BaseStorageClient):
|
|
17
|
+
"""S3-specific storage client implementation.
|
|
18
|
+
|
|
19
|
+
Handles S3-specific URI construction, transport parameters, and presigned URL
|
|
20
|
+
generation for the smart-open storage client.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, storage_secrets: dict[StorageSecrets, Any]):
|
|
24
|
+
"""Initialize the storage client with secrets.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
storage_secrets: Provider-specific storage credentials and configuration using StorageSecrets enum keys
|
|
28
|
+
"""
|
|
29
|
+
super().__init__(storage_secrets)
|
|
30
|
+
self.storage_secrets: dict[StorageSecrets, Any] = storage_secrets
|
|
31
|
+
|
|
32
|
+
def build_uri(self, bucket: str, key: str) -> str:
|
|
33
|
+
"""Build the S3 URI for the storage location.
|
|
34
|
+
|
|
35
|
+
Args:
|
|
36
|
+
bucket: S3 bucket name
|
|
37
|
+
key: Object key/path
|
|
38
|
+
|
|
39
|
+
Returns:
|
|
40
|
+
S3 URI string for smart-open
|
|
41
|
+
"""
|
|
42
|
+
# Handle custom endpoint (e.g., MinIO) - endpoint_url is not in StorageSecrets enum
|
|
43
|
+
# For now, we'll use standard S3 URI since we only have enum keys
|
|
44
|
+
return f"s3://{bucket}/{key}"
|
|
45
|
+
|
|
46
|
+
def get_transport_params(self) -> dict[str, Any]:
|
|
47
|
+
"""Get S3-specific transport parameters for smart-open.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
Dictionary of S3 transport parameters for smart-open
|
|
51
|
+
"""
|
|
52
|
+
params = {}
|
|
53
|
+
|
|
54
|
+
if StorageSecrets.AWS_ACCESS_KEY_ID in self.storage_secrets:
|
|
55
|
+
params["access_key"] = self.storage_secrets[
|
|
56
|
+
StorageSecrets.AWS_ACCESS_KEY_ID
|
|
57
|
+
]
|
|
58
|
+
if StorageSecrets.AWS_SECRET_ACCESS_KEY in self.storage_secrets:
|
|
59
|
+
params["secret_key"] = self.storage_secrets[
|
|
60
|
+
StorageSecrets.AWS_SECRET_ACCESS_KEY
|
|
61
|
+
]
|
|
62
|
+
if StorageSecrets.REGION_NAME in self.storage_secrets:
|
|
63
|
+
params["region"] = self.storage_secrets[StorageSecrets.REGION_NAME]
|
|
64
|
+
if StorageSecrets.AWS_ASSUME_ROLE in self.storage_secrets:
|
|
65
|
+
params["assume_role_arn"] = self.storage_secrets[
|
|
66
|
+
StorageSecrets.AWS_ASSUME_ROLE
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
return params
|
|
70
|
+
|
|
71
|
+
def generate_presigned_url(
|
|
72
|
+
self, bucket: str, key: str, ttl_seconds: Optional[int] = None
|
|
73
|
+
) -> AnyHttpUrlString:
|
|
74
|
+
"""Generate an S3 presigned URL for the object.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
bucket: S3 bucket name
|
|
78
|
+
key: Object key/path
|
|
79
|
+
ttl_seconds: Time to live in seconds
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
S3 presigned URL for the object
|
|
83
|
+
|
|
84
|
+
Raises:
|
|
85
|
+
Exception: If presigned URL generation fails
|
|
86
|
+
"""
|
|
87
|
+
try:
|
|
88
|
+
# Storage secrets are already in the right format for get_s3_client
|
|
89
|
+
# get_s3_client expects dict[StorageSecrets, Any] with enum keys
|
|
90
|
+
s3_secrets = self.storage_secrets
|
|
91
|
+
|
|
92
|
+
# Determine auth method based on available credentials
|
|
93
|
+
# If AWS credentials are present, use SECRET_KEYS, otherwise use AUTOMATIC
|
|
94
|
+
if (
|
|
95
|
+
StorageSecrets.AWS_ACCESS_KEY_ID in self.storage_secrets
|
|
96
|
+
and StorageSecrets.AWS_SECRET_ACCESS_KEY in self.storage_secrets
|
|
97
|
+
and self.storage_secrets[StorageSecrets.AWS_ACCESS_KEY_ID]
|
|
98
|
+
and self.storage_secrets[StorageSecrets.AWS_SECRET_ACCESS_KEY]
|
|
99
|
+
):
|
|
100
|
+
auth_method = AWSAuthMethod.SECRET_KEYS.value
|
|
101
|
+
else:
|
|
102
|
+
auth_method = AWSAuthMethod.AUTOMATIC.value
|
|
103
|
+
|
|
104
|
+
# Extract assume_role_arn if present for role assumption
|
|
105
|
+
assume_role_arn = None
|
|
106
|
+
if StorageSecrets.AWS_ASSUME_ROLE in self.storage_secrets:
|
|
107
|
+
assume_role_arn = self.storage_secrets[StorageSecrets.AWS_ASSUME_ROLE]
|
|
108
|
+
|
|
109
|
+
s3_client = get_s3_client(auth_method, s3_secrets, assume_role_arn)
|
|
110
|
+
return create_presigned_url_for_s3(s3_client, bucket, key, ttl_seconds)
|
|
111
|
+
except Exception as e:
|
|
112
|
+
logger.error(f"Failed to generate S3 presigned URL: {e}")
|
|
113
|
+
raise
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from io import BytesIO
|
|
4
|
+
from typing import Any, Optional, Union
|
|
5
|
+
|
|
6
|
+
from fideslang.validation import AnyHttpUrlString
|
|
7
|
+
from loguru import logger
|
|
8
|
+
|
|
9
|
+
from fides.api.common_exceptions import StorageUploadError
|
|
10
|
+
from fides.api.models.privacy_request import PrivacyRequest
|
|
11
|
+
from fides.api.schemas.storage.storage import StorageSecrets, StorageSecretsS3
|
|
12
|
+
from fides.api.service.storage.s3 import generic_upload_to_s3
|
|
13
|
+
from fides.api.service.storage.streaming.retry import retry_cloud_storage_operation
|
|
14
|
+
from fides.api.service.storage.streaming.schemas import StorageUploadConfig
|
|
15
|
+
from fides.api.service.storage.streaming.smart_open_client import SmartOpenStorageClient
|
|
16
|
+
from fides.api.service.storage.streaming.smart_open_streaming_storage import (
|
|
17
|
+
SmartOpenStreamingStorage,
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@retry_cloud_storage_operation(
|
|
22
|
+
provider="s3_streaming",
|
|
23
|
+
operation_name="upload_to_s3_streaming",
|
|
24
|
+
max_retries=2,
|
|
25
|
+
base_delay=2.0,
|
|
26
|
+
max_delay=30.0,
|
|
27
|
+
)
|
|
28
|
+
def upload_to_s3_streaming(
|
|
29
|
+
storage_secrets: Union[StorageSecretsS3, dict[StorageSecrets, Any]],
|
|
30
|
+
data: dict,
|
|
31
|
+
bucket_name: str,
|
|
32
|
+
file_key: str,
|
|
33
|
+
resp_format: str,
|
|
34
|
+
privacy_request: PrivacyRequest,
|
|
35
|
+
document: Optional[BytesIO],
|
|
36
|
+
auth_method: str,
|
|
37
|
+
max_workers: int = 5,
|
|
38
|
+
) -> Optional[AnyHttpUrlString]:
|
|
39
|
+
"""Uploads arbitrary data to S3 using smart-open streaming for memory efficiency.
|
|
40
|
+
|
|
41
|
+
This function now uses smart-open for efficient cloud storage operations while maintaining
|
|
42
|
+
our DSR-specific business logic for package splitting and attachment processing.
|
|
43
|
+
"""
|
|
44
|
+
formatted_secrets = format_secrets(storage_secrets)
|
|
45
|
+
|
|
46
|
+
if document is not None:
|
|
47
|
+
_, response = generic_upload_to_s3(
|
|
48
|
+
formatted_secrets, # type: ignore[arg-type]
|
|
49
|
+
bucket_name,
|
|
50
|
+
file_key,
|
|
51
|
+
auth_method,
|
|
52
|
+
document,
|
|
53
|
+
)
|
|
54
|
+
return response
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
storage_client = SmartOpenStorageClient("s3", formatted_secrets)
|
|
58
|
+
|
|
59
|
+
# Create upload config for the streaming interface
|
|
60
|
+
upload_config = StorageUploadConfig(
|
|
61
|
+
bucket_name=bucket_name,
|
|
62
|
+
file_key=file_key,
|
|
63
|
+
resp_format=resp_format,
|
|
64
|
+
max_workers=max_workers,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
# Use the smart-open streaming implementation
|
|
68
|
+
streaming_storage = SmartOpenStreamingStorage(storage_client)
|
|
69
|
+
result = streaming_storage.upload_to_storage_streaming(
|
|
70
|
+
data,
|
|
71
|
+
upload_config,
|
|
72
|
+
privacy_request,
|
|
73
|
+
document,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
logger.debug(
|
|
77
|
+
"Successfully uploaded streaming archive to S3 using smart-open: {}",
|
|
78
|
+
file_key,
|
|
79
|
+
)
|
|
80
|
+
return result
|
|
81
|
+
|
|
82
|
+
except Exception as e:
|
|
83
|
+
logger.error("Unexpected error during smart-open streaming upload: {}", e)
|
|
84
|
+
raise StorageUploadError(
|
|
85
|
+
f"Unexpected error during smart-open streaming upload: {e}"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _process_storage_secrets_input(
|
|
90
|
+
storage_secrets: Union[StorageSecretsS3, dict[StorageSecrets, Any]] # type: ignore[misc]
|
|
91
|
+
) -> dict[str, Any]:
|
|
92
|
+
"""Process input and convert to string-keyed dictionary."""
|
|
93
|
+
final_secrets: dict[str, Any] = {}
|
|
94
|
+
|
|
95
|
+
if isinstance(storage_secrets, StorageSecretsS3):
|
|
96
|
+
# Convert StorageSecretsS3 model directly to string keys
|
|
97
|
+
for key, value in storage_secrets.model_dump().items():
|
|
98
|
+
if value is not None:
|
|
99
|
+
final_secrets[key] = value
|
|
100
|
+
else:
|
|
101
|
+
# Process dict input, converting enum keys to strings if needed
|
|
102
|
+
for key, value in (storage_secrets or {}).items(): # type: ignore[assignment]
|
|
103
|
+
if isinstance(key, str):
|
|
104
|
+
final_secrets[key] = value
|
|
105
|
+
elif isinstance(key, StorageSecrets):
|
|
106
|
+
final_secrets[key.value] = value
|
|
107
|
+
|
|
108
|
+
return final_secrets
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _validate_aws_credentials(final_secrets: dict[str, Any]) -> None:
|
|
112
|
+
"""Validate that required AWS credentials are present."""
|
|
113
|
+
has_access_key = (
|
|
114
|
+
"aws_access_key_id" in final_secrets and final_secrets["aws_access_key_id"]
|
|
115
|
+
)
|
|
116
|
+
has_secret_key = (
|
|
117
|
+
"aws_secret_access_key" in final_secrets
|
|
118
|
+
and final_secrets["aws_secret_access_key"]
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# If we have any AWS credentials, we need both for SECRET_KEYS auth
|
|
122
|
+
if has_access_key or has_secret_key:
|
|
123
|
+
if not has_access_key:
|
|
124
|
+
raise ValueError(
|
|
125
|
+
"Missing required AWS credentials for SECRET_KEYS auth: aws_access_key_id. "
|
|
126
|
+
"Storage configuration must include valid AWS access key and secret key."
|
|
127
|
+
)
|
|
128
|
+
if not has_secret_key:
|
|
129
|
+
raise ValueError(
|
|
130
|
+
"Missing required AWS credentials for SECRET_KEYS auth: aws_secret_access_key. "
|
|
131
|
+
"Storage configuration must include valid AWS access key and secret key."
|
|
132
|
+
)
|
|
133
|
+
else:
|
|
134
|
+
# AUTOMATIC authentication - check if region is provided
|
|
135
|
+
has_region = "region_name" in final_secrets and final_secrets["region_name"]
|
|
136
|
+
if not has_region:
|
|
137
|
+
raise ValueError(
|
|
138
|
+
"Missing required region_name for AUTOMATIC authentication. "
|
|
139
|
+
"Storage configuration must include a valid AWS region."
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _set_default_region(final_secrets: dict[str, Any]) -> None:
|
|
144
|
+
"""Set default region if missing."""
|
|
145
|
+
if "region_name" not in final_secrets or not final_secrets["region_name"]:
|
|
146
|
+
logger.debug("Setting default region to 'us-east-1'")
|
|
147
|
+
final_secrets["region_name"] = "us-east-1"
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def format_secrets(
|
|
151
|
+
storage_secrets: Union[StorageSecretsS3, dict[StorageSecrets, Any]] # type: ignore[misc]
|
|
152
|
+
) -> dict[str, Any]:
|
|
153
|
+
"""
|
|
154
|
+
Returns the correct format for the S3StorageClient.
|
|
155
|
+
|
|
156
|
+
This function handles multiple credential formats and processes them through several stages:
|
|
157
|
+
1. Input processing: Accepts either StorageSecretsS3 models (from API) or raw dicts (from database)
|
|
158
|
+
2. Key normalization: Converts all keys to string format for consistency
|
|
159
|
+
3. Validation: Ensures required AWS credentials are present based on auth method
|
|
160
|
+
4. Default setting: Sets default region if missing (after validation)
|
|
161
|
+
5. Return: Returns string-keyed dict ready for S3StorageClient
|
|
162
|
+
|
|
163
|
+
Input formats:
|
|
164
|
+
- StorageSecretsS3: Used by storage API endpoints (e.g., put_config_secrets)
|
|
165
|
+
- dict[StorageSecrets, Any]: Used by storage models (e.g., StorageConfig.secrets)
|
|
166
|
+
- dict[str, Any]: Used by database queries (e.g., StorageConfig.get_by)
|
|
167
|
+
|
|
168
|
+
Authentication methods:
|
|
169
|
+
- SECRET_KEYS: Requires aws_access_key_id, aws_secret_access_key, and region_name
|
|
170
|
+
- AUTOMATIC: Requires only region_name (relies on AWS SDK credential chain)
|
|
171
|
+
- Role assumption: Can be used with either auth method via assume_role_arn
|
|
172
|
+
|
|
173
|
+
Output format:
|
|
174
|
+
- dict[str, Any]: Required by S3StorageClient.generate_presigned_url() and other AWS operations
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
dict[str, Any]: Credentials with string keys like 'aws_access_key_id'
|
|
178
|
+
|
|
179
|
+
Raises:
|
|
180
|
+
ValueError: If required AWS credentials are missing for the chosen auth method
|
|
181
|
+
"""
|
|
182
|
+
logger.debug("format_secrets called with type: {}", type(storage_secrets).__name__)
|
|
183
|
+
|
|
184
|
+
# Stage 1: Process input and create final format directly
|
|
185
|
+
final_secrets = _process_storage_secrets_input(storage_secrets)
|
|
186
|
+
|
|
187
|
+
# Stage 2: Validate required credentials BEFORE setting defaults
|
|
188
|
+
_validate_aws_credentials(final_secrets)
|
|
189
|
+
|
|
190
|
+
# Stage 3: Set default region if missing (after validation)
|
|
191
|
+
_set_default_region(final_secrets)
|
|
192
|
+
|
|
193
|
+
logger.debug(
|
|
194
|
+
"format_secrets completed successfully with {} keys", len(final_secrets)
|
|
195
|
+
)
|
|
196
|
+
return final_secrets
|