ethyca-fides 2.69.1b1__py2.py3-none-any.whl → 2.69.1rc0__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.
- {ethyca_fides-2.69.1b1.dist-info → ethyca_fides-2.69.1rc0.dist-info}/METADATA +2 -2
- {ethyca_fides-2.69.1b1.dist-info → ethyca_fides-2.69.1rc0.dist-info}/RECORD +241 -232
- fides/_version.py +3 -3
- fides/api/alembic/migrations/versions/78dbe23d8204_adding_privacy_request_redaction_patterns.py +52 -0
- fides/api/api/v1/api.py +2 -0
- fides/api/api/v1/endpoints/dsr_package_link.py +5 -3
- fides/api/api/v1/endpoints/oauth_endpoints.py +19 -7
- fides/api/api/v1/endpoints/privacy_request_redaction_patterns_endpoints.py +95 -0
- fides/api/api/v1/endpoints/user_endpoints.py +27 -4
- fides/api/app_setup.py +16 -2
- fides/api/db/base.py +3 -0
- fides/api/main.py +22 -0
- fides/api/models/client.py +1 -0
- fides/api/models/privacy_request_redaction_pattern.py +64 -0
- fides/api/oauth/utils.py +126 -12
- fides/api/schemas/privacy_request_redaction_patterns.py +55 -0
- fides/api/service/privacy_request/dsr_package/dsr_data_preprocessor.py +231 -0
- fides/api/service/privacy_request/dsr_package/dsr_report_builder.py +286 -120
- fides/api/service/privacy_request/dsr_package/templates/attachments_index.html +4 -2
- fides/api/service/privacy_request/dsr_package/templates/collection_index.html +3 -1
- fides/api/service/privacy_request/dsr_package/templates/dataset_index.html +1 -1
- fides/api/service/privacy_request/dsr_package/utils.py +268 -0
- fides/api/service/privacy_request/request_runner_service.py +8 -2
- fides/api/service/privacy_request/request_service.py +18 -1
- fides/api/service/storage/storage_uploader_service.py +1 -21
- fides/api/service/storage/streaming/dsr_storage.py +1 -5
- fides/api/service/storage/streaming/s3/s3_storage_client.py +78 -40
- fides/api/service/storage/streaming/s3/streaming_s3.py +9 -21
- fides/api/service/storage/streaming/smart_open_client.py +8 -7
- fides/api/service/storage/streaming/smart_open_streaming_storage.py +110 -197
- fides/api/service/storage/streaming/storage_client_factory.py +7 -3
- fides/api/service/storage/util.py +579 -0
- fides/api/task/manual/manual_task_graph_task.py +11 -9
- fides/api/tasks/storage.py +2 -2
- fides/api/util/endpoint_utils.py +0 -13
- fides/api/util/rate_limit.py +194 -0
- fides/common/api/scope_registry.py +8 -0
- fides/common/api/v1/urn_registry.py +3 -0
- fides/config/redis_settings.py +27 -3
- fides/config/security_settings.py +21 -6
- fides/ui-build/static/admin/404.html +1 -1
- fides/ui-build/static/admin/_next/static/OmXHlY9MvjoZH9jDkAytl/_buildManifest.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{1345-04e37a66c0d40dc1.js → 1345-5e1c5b66e25c566e.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{3729-f5f2976904dce90d.js → 3729-c17ac8031a4c4fd1.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{3847-2c0126e6eb54c526.js → 3847-230bf61b053bc706.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{3855-9dd54ded74f4036b.js → 3855-ef5194cdb228beb6.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/4164-355644b916ae0094.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{4608-d101417a3abb4ad6.js → 4608-be8cba73f5d7c326.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{4786-7aafb744445445b2.js → 4786-61154adf88e448e1.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{4808-357ca7ea7bbd24c6.js → 4808-dd4157aa72648068.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/4831-fd99c0b3784de128.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{4844-707b20a6c4b127cc.js → 4844-46324c3d848b8b6a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{5258-1a8b9f66b97761fc.js → 5258-b0de22a8521686ab.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{9046-058a4d8f0b5e08f9.js → 9046-712156d461165f56.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{9676.b7d5d1d90b9da224.js → 9676.9fd9552ef744c717.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{9951-b954027a046ce553.js → 9951-a88367a129b724ba.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{_app-7612a768b9a6304b.js → _app-fcdad91f6f66292b.js} +2 -2
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-20253dd047fb9736.js → manual-ace203dfacacbdc4.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{add-systems-169099ff7b305cf5.js → add-systems-bd0d82078e67cac3.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-28b192e2c074b0f3.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/{[resourceUrn]-adc500a03e239857.js → [resourceUrn]-2c29ff7a01198f30.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/{[resourceUrn]-c8b3d090e4ba60d3.js → [resourceUrn]-8eb581024bc0172f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/{[systemId]-cfb0b1200bc1a2c9.js → [systemId]-e1ba213fb666b3f4.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/{[monitorId]-fed8b879c13c2bf3.js → [monitorId]-6d133580045abdda.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{activity-58a110542d6bcd0f.js → activity-b6ae7adb8ef0b525.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-22eec362dfbb1d2a.js → [resourceUrn]-8f736b078e9842da.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-4decce5ef996e563.js → detection-eb814e3c22807871.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-01acdd1ad492fd89.js → [resourceUrn]-6875b7783fcfda2f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-85fdbf4cde60d910.js → discovery-172dbd7740e212ca.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{datamap-60e27b673c68bd27.js → datamap-c7390e046b2e2b7f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{new-2c7b1213b6604d30.js → new-e32fccc4ca520d2b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-7ed3f05700dc397b.js → [id]-927b7e476c4b47d0.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{new-95f6d64f84fc6bf3.js → new-cbe100d50df34285.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/{[id]-02ff0dd9d8f1d719.js → [id]-4c3c413a2668df53.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{integrations-0f12d5b658c98c9f.js → integrations-95402b5001c07ef2.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{[id]-b379873a5771e55b.js → [id]-0f25a76dd18c5e20.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{messaging-a9bb257906dcd7e0.js → messaging-ad6ad3e5bd72765d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{storage-de0036c74b78e9b7.js → storage-6032d82f0fc2893d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{privacy-requests-91f578139548652c.js → privacy-requests-baf31c3e4b081046.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/reporting/{datamap-83019a6753e14857.js → datamap-6903f42a0412bfa6.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{custom-fields-74f0fe9656f4a1f6.js → custom-fields-9ecb803099082bf4.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/privacy-requests-2ecc073f41628f62.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-e91da49c0681a300.js → [id]-6e15332935f6b538.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{taxonomy-78c3a5200d362cff.js → taxonomy-4d7827fc9c46b6b8.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/{new-de8cb3739ab99c09.js → new-92f52c43f522a350.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/{[id]-4f9001870e2b3aff.js → [id]-64452dfae2c5e614.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{webpack-b5eb3e1da37616d2.js → webpack-678e89d68dbcd94f.js} +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/privacy-requests.html +1 -0
- 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/LqjE2O1xWlQKqW38Sy4m3/_buildManifest.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4121-bd6f467aacd80f91.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/768-7eac4b30d7477b0a.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-60e3394c887f3d5e.js +0 -1
- {ethyca_fides-2.69.1b1.dist-info → ethyca_fides-2.69.1rc0.dist-info}/WHEEL +0 -0
- {ethyca_fides-2.69.1b1.dist-info → ethyca_fides-2.69.1rc0.dist-info}/entry_points.txt +0 -0
- {ethyca_fides-2.69.1b1.dist-info → ethyca_fides-2.69.1rc0.dist-info}/licenses/LICENSE +0 -0
- {ethyca_fides-2.69.1b1.dist-info → ethyca_fides-2.69.1rc0.dist-info}/top_level.txt +0 -0
- /fides/ui-build/static/admin/_next/static/{LqjE2O1xWlQKqW38Sy4m3 → OmXHlY9MvjoZH9jDkAytl}/_ssgManifest.js +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{1099-c34f76b4da5f3d15.js → 1099-79646e64f26d62fa.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{1817-e6934e258111a961.js → 1817-0ca16d288fad916d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{2921-0696287bb8de1f0b.js → 2921-52328140bc420d0f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{3620-8c0ee3d0b19c342d.js → 3620-602eb74dc896d556.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{3872-72ea3eb040366277.js → 3872-f78dec02f0d959ae.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{3923-c4f2b03706ddbe39.js → 3923-bb2417b8dcade7a4.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{401-959a15ed18a8abdf.js → 401-4af0a912e249d30f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{5487-fd9724519f31caff.js → 5487-02d00bad7c6830e0.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{549-d3bef0990071230c.js → 549-38ea1d91ee2addaa.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{6084-487d27d017c45e05.js → 6084-c153669d5567e242.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{6853-f7ab74e30abbdeaf.js → 6853-b17673391117c976.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{6954-b0a7b8ac6db238f8.js → 6954-5296188c19d7d0ac.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{7476-9a57db910472b48e.js → 7476-45c5088baa8b66af.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{7630-46807321449479c7.js → 7630-7ed6c6117775dffe.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{787-79e8e558bd80ece6.js → 787-a8c7eab617e2fceb.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{79-3e5047415bee9391.js → 79-65674011d455af4d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{796-fb2af44165f37ecc.js → 796-9e1ca1a4030707c5.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{8002-c1f66179adabece8.js → 8002-24af20d679efc04e.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{9826-0d9a7f61c08ed88a.js → 9826-dbae8dee941a7fac.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{404-9c9efb820bb6b432.js → 404-471a6b18e712f050.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{multiple-caadc3c0e86a7bfe.js → multiple-920fb469e0dda1d2.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/{add-vendors-acefb9e08c6c4082.js → add-vendors-406170eaae4329c6.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{configure-558fbb1667473abb.js → configure-7207ab23bdb36ce8.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{[id]-b1ff1c9683841815.js → [id]-f80cf2d3966816fd.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-experience-674906d2d9a0a3a6.js → privacy-experience-9dda4de5ec580279.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-5b7aa7971f070513.js → [id]-b378576cba255609.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-285958a12a66575e.js → new-2ca1de7b88094ab0.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-notices-dc56bbdb6dd7a670.js → privacy-notices-0d4844d0b808e6e4.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{properties-0843757f00eeaaec.js → properties-226efa1dcd41437f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{consent-5cba58ebecb4511f.js → consent-3e8bdefe714254ec.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/{[projectUrn]-0f90cac9f190130c.js → [projectUrn]-04cfe2cfba7b7cd8.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{projects-f9117645c941342c.js → projects-5f2d7b24804f861f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{resources-6c3714ee97a718c1.js → resources-de704de849960f01.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{data-catalog-c566640eecc6f3fe.js → data-catalog-30108b00ac769fc3.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{action-center-991659e916ad60b1.js → action-center-9a81d42a474e1e48.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/{[...subfieldNames]-3f4ba87513e030a2.js → [...subfieldNames]-dfd71c1e9c458b89.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/{[collectionName]-a6cd31103deba465.js → [collectionName]-7cdc42ec5493b83d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{[datasetId]-491d2c8dd559d0cb.js → [datasetId]-e12b11ba15bc3fc1.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{dataset-c8bcd568d3b0ee7f.js → dataset-7c59a6abf6ba6207.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{datastore-connection-9a33a412a5f36960.js → datastore-connection-cce20440b177050b.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{index-1299410f671fac23.js → index-6cd8708106331b8d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{[id]-ba232c4b17576ccb.js → [id]-3c6dc2f6e6bae960.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{add-template-b97883026fbc5b1e.js → add-template-4a6d4023a7791be8.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{messaging-38189c1058aa4acf.js → messaging-76b204c9b98d656f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{ant-components-3119bdb3811409bf.js → ant-components-bc0e2adf6e0d3ac7.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{AntForm-8f9d0434dc3eb8cf.js → AntForm-86ffcc1ad3fa912e.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikAntFormItem-a14e51c7f22f3395.js → FormikAntFormItem-ec04f595465bdf69.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikControlled-a40645b57822684d.js → FormikControlled-41d309754ff0c1de.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikField-c55df6527b700701.js → FormikField-cab1f78cec7808f9.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{forms-292d761616b162a0.js → forms-eb6058221403b156.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{table-migration-135bcf384b81820a.js → table-migration-48500551fd6a7602.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{configure-70efccbe0732786b.js → configure-d83e5bd52a638234.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{[id]-63c6f0193634add5.js → [id]-e784c05d056b2371.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{add-property-e76567f0374d5912.js → add-property-0a7a2db148a7561a.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{properties-2050b7dac0413c11.js → properties-da734840e4f9d04b.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/{alpha-913f8eab62460f31.js → alpha-a82f3df840d5c1b5.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{about-2ed1ee6017c0656a.js → about-d06fb16487705b9d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/{[purpose_id]-bffd6292e7e0302a.js → [purpose_id]-9495e2eb506606c7.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{consent-e54b4a7d80b81800.js → consent-93a978443bf299db.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-a649ca07ce51b0fe.js → domain-records-16fdd91a81074dd1.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domains-eb675ba600cea2a8.js → domains-4cdd6001e7cb9aee.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{email-templates-4db2f42f90867890.js → email-templates-1914de830ce5cfc4.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-89b404989b4ea21c.js → locations-2e635dcd11b78224.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-eb1ecff37fd85c72.js → organization-f547f1f33c12faf3.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{regulations-07d883b5aaec58f2.js → regulations-7c02e469d8c5bd74.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-61ecb7546830c345.js → test-datasets-20b1193ed76c56b0.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{systems-44d714017dd87e62.js → systems-fbc8761ef4d55516.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-583a7f0c1bead240.js → user-management-9cec020f89544426.js} +0 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
# pylint: disable=too-many-lines
|
|
3
2
|
from __future__ import annotations
|
|
4
3
|
|
|
5
4
|
import csv
|
|
@@ -18,7 +17,7 @@ from fides.api.common_exceptions import StorageUploadError
|
|
|
18
17
|
from fides.api.models.privacy_request import PrivacyRequest
|
|
19
18
|
from fides.api.schemas.storage.storage import ResponseFormat
|
|
20
19
|
from fides.api.service.privacy_request.dsr_package.dsr_report_builder import (
|
|
21
|
-
|
|
20
|
+
DSRReportBuilder,
|
|
22
21
|
)
|
|
23
22
|
from fides.api.service.storage.streaming.dsr_storage import (
|
|
24
23
|
create_dsr_report_files_generator,
|
|
@@ -34,6 +33,15 @@ from fides.api.service.storage.streaming.schemas import (
|
|
|
34
33
|
StreamingBufferConfig,
|
|
35
34
|
)
|
|
36
35
|
from fides.api.service.storage.streaming.smart_open_client import SmartOpenStorageClient
|
|
36
|
+
from fides.api.service.storage.util import (
|
|
37
|
+
convert_processed_attachments_to_attachment_processing_info,
|
|
38
|
+
determine_dataset_name_from_path,
|
|
39
|
+
extract_storage_key_from_attachment,
|
|
40
|
+
get_unique_filename,
|
|
41
|
+
process_attachments_contextually,
|
|
42
|
+
resolve_attachment_storage_path,
|
|
43
|
+
resolve_base_path_from_context,
|
|
44
|
+
)
|
|
37
45
|
|
|
38
46
|
DEFAULT_ATTACHMENT_NAME = "attachment"
|
|
39
47
|
DEFAULT_FILE_MODE = 0o644
|
|
@@ -68,6 +76,9 @@ class SmartOpenStreamingStorage:
|
|
|
68
76
|
"""
|
|
69
77
|
self.storage_client = storage_client
|
|
70
78
|
self.chunk_size = chunk_size
|
|
79
|
+
# Track used filenames per dataset to match DSR report builder behavior
|
|
80
|
+
# Maps dataset_name -> set of used filenames
|
|
81
|
+
self.used_filenames_per_dataset: dict[str, set[str]] = {}
|
|
71
82
|
|
|
72
83
|
def _parse_storage_url(self, storage_key: str) -> tuple[str, str]:
|
|
73
84
|
"""Parse storage URL and return (bucket, key).
|
|
@@ -229,143 +240,6 @@ class SmartOpenStreamingStorage:
|
|
|
229
240
|
|
|
230
241
|
return packages
|
|
231
242
|
|
|
232
|
-
def _collect_attachments(self, data: dict) -> list[dict]:
|
|
233
|
-
"""Collect all attachment data from the input data structure.
|
|
234
|
-
|
|
235
|
-
This method handles both direct attachments (under 'attachments' key) and
|
|
236
|
-
nested attachments within items. It returns raw attachment data without validation.
|
|
237
|
-
|
|
238
|
-
Args:
|
|
239
|
-
data: The data dictionary containing items with attachments
|
|
240
|
-
|
|
241
|
-
Returns:
|
|
242
|
-
List of raw attachment dictionaries with metadata
|
|
243
|
-
"""
|
|
244
|
-
all_attachments = []
|
|
245
|
-
|
|
246
|
-
for key, value in data.items():
|
|
247
|
-
logger.debug(f"Processing key '{key}' with value type: {type(value)}")
|
|
248
|
-
|
|
249
|
-
if not isinstance(value, list) or not value:
|
|
250
|
-
continue
|
|
251
|
-
|
|
252
|
-
# Collect direct attachments if this key is "attachments"
|
|
253
|
-
if key == "attachments":
|
|
254
|
-
all_attachments.extend(self._collect_direct_attachments(value))
|
|
255
|
-
|
|
256
|
-
# Collect nested attachments from items
|
|
257
|
-
all_attachments.extend(self._collect_nested_attachments(key, value))
|
|
258
|
-
|
|
259
|
-
logger.debug(f"Collected {len(all_attachments)} raw attachments")
|
|
260
|
-
return all_attachments
|
|
261
|
-
|
|
262
|
-
def _collect_direct_attachments(self, attachments_list: list) -> list[dict]:
|
|
263
|
-
"""Collect attachments from a direct attachments list.
|
|
264
|
-
|
|
265
|
-
Args:
|
|
266
|
-
attachments_list: List of attachment dictionaries
|
|
267
|
-
|
|
268
|
-
Returns:
|
|
269
|
-
List of attachment data dictionaries with metadata
|
|
270
|
-
"""
|
|
271
|
-
direct_attachments = []
|
|
272
|
-
|
|
273
|
-
logger.debug(
|
|
274
|
-
f"Found 'attachments' key with {len(attachments_list)} items - processing as direct attachments"
|
|
275
|
-
)
|
|
276
|
-
|
|
277
|
-
for idx, attachment in enumerate(attachments_list):
|
|
278
|
-
if not isinstance(attachment, dict):
|
|
279
|
-
continue
|
|
280
|
-
|
|
281
|
-
# Check if this looks like an attachment (has file_name or download_url)
|
|
282
|
-
if "file_name" in attachment or "download_url" in attachment:
|
|
283
|
-
# Transform download_url to internal access package URL for access package display
|
|
284
|
-
if "download_url" in attachment:
|
|
285
|
-
attachment["original_download_url"] = attachment["download_url"]
|
|
286
|
-
attachment["download_url"] = (
|
|
287
|
-
f"attachments/{attachment.get('file_name', f'attachment_{idx}')}"
|
|
288
|
-
)
|
|
289
|
-
|
|
290
|
-
direct_attachments.append(attachment)
|
|
291
|
-
|
|
292
|
-
return direct_attachments
|
|
293
|
-
|
|
294
|
-
def _collect_nested_attachments(self, key: str, items: list) -> list[dict]:
|
|
295
|
-
"""Collect attachments from nested items.
|
|
296
|
-
|
|
297
|
-
Args:
|
|
298
|
-
key: The key for the items list
|
|
299
|
-
items: List of items that may contain attachments
|
|
300
|
-
|
|
301
|
-
Returns:
|
|
302
|
-
List of attachment data dictionaries with metadata
|
|
303
|
-
"""
|
|
304
|
-
nested_attachments = []
|
|
305
|
-
|
|
306
|
-
for item in items:
|
|
307
|
-
if not isinstance(item, dict):
|
|
308
|
-
continue
|
|
309
|
-
|
|
310
|
-
# Recursively search for attachments in nested structures
|
|
311
|
-
item_attachments = self._find_attachments_recursive(item, key)
|
|
312
|
-
nested_attachments.extend(item_attachments)
|
|
313
|
-
|
|
314
|
-
return nested_attachments
|
|
315
|
-
|
|
316
|
-
def _find_attachments_recursive(
|
|
317
|
-
self, item: dict, context_key: str, path: str = ""
|
|
318
|
-
) -> list[dict]:
|
|
319
|
-
"""Recursively find attachments in nested dictionary structures.
|
|
320
|
-
|
|
321
|
-
Args:
|
|
322
|
-
item: Dictionary item to search
|
|
323
|
-
context_key: The top-level key for context
|
|
324
|
-
path: Current path in the nested structure
|
|
325
|
-
|
|
326
|
-
Returns:
|
|
327
|
-
List of attachment data dictionaries with metadata
|
|
328
|
-
"""
|
|
329
|
-
attachments = []
|
|
330
|
-
|
|
331
|
-
# Check if this item has direct attachments
|
|
332
|
-
if "attachments" in item and isinstance(item["attachments"], list):
|
|
333
|
-
for attachment in item["attachments"]:
|
|
334
|
-
if not isinstance(attachment, dict):
|
|
335
|
-
continue
|
|
336
|
-
|
|
337
|
-
# Check if this looks like an attachment
|
|
338
|
-
if "file_name" in attachment or "download_url" in attachment:
|
|
339
|
-
# Add context about which item this attachment belongs to
|
|
340
|
-
attachment_with_context = attachment.copy()
|
|
341
|
-
attachment_with_context["_context"] = {
|
|
342
|
-
"key": context_key,
|
|
343
|
-
"item_id": item.get("id", "unknown"),
|
|
344
|
-
"path": path,
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
# Transform download_url to internal access package URL
|
|
348
|
-
if "download_url" in attachment:
|
|
349
|
-
attachment_with_context["original_download_url"] = attachment[
|
|
350
|
-
"download_url"
|
|
351
|
-
]
|
|
352
|
-
attachment_with_context["download_url"] = (
|
|
353
|
-
f"attachments/{attachment.get('file_name', 'attachment')}"
|
|
354
|
-
)
|
|
355
|
-
|
|
356
|
-
attachments.append(attachment_with_context)
|
|
357
|
-
|
|
358
|
-
# Recursively search nested dictionaries
|
|
359
|
-
for key, value in item.items():
|
|
360
|
-
if isinstance(value, dict):
|
|
361
|
-
current_path = f"{path}.{key}" if path else key
|
|
362
|
-
nested_attachments = self._find_attachments_recursive(
|
|
363
|
-
value, context_key, current_path
|
|
364
|
-
)
|
|
365
|
-
attachments.extend(nested_attachments)
|
|
366
|
-
|
|
367
|
-
return attachments
|
|
368
|
-
|
|
369
243
|
def _validate_attachment(
|
|
370
244
|
self, attachment: dict
|
|
371
245
|
) -> Optional[AttachmentProcessingInfo]:
|
|
@@ -378,12 +252,8 @@ class SmartOpenStreamingStorage:
|
|
|
378
252
|
AttachmentProcessingInfo if valid, None otherwise
|
|
379
253
|
"""
|
|
380
254
|
try:
|
|
381
|
-
# Extract
|
|
382
|
-
storage_key = (
|
|
383
|
-
attachment.get("original_download_url")
|
|
384
|
-
or attachment.get("download_url")
|
|
385
|
-
or attachment.get("file_name", "")
|
|
386
|
-
)
|
|
255
|
+
# Extract storage key using shared utility
|
|
256
|
+
storage_key = extract_storage_key_from_attachment(attachment)
|
|
387
257
|
if not storage_key:
|
|
388
258
|
return None
|
|
389
259
|
|
|
@@ -395,11 +265,8 @@ class SmartOpenStreamingStorage:
|
|
|
395
265
|
content_type=attachment.get("content_type"),
|
|
396
266
|
)
|
|
397
267
|
|
|
398
|
-
#
|
|
399
|
-
base_path =
|
|
400
|
-
if attachment.get("_context"):
|
|
401
|
-
context = attachment["_context"]
|
|
402
|
-
base_path = f"{context['key']}/{context['item_id']}/attachments"
|
|
268
|
+
# Resolve base path using shared utility
|
|
269
|
+
base_path = resolve_base_path_from_context(attachment)
|
|
403
270
|
|
|
404
271
|
# Create AttachmentProcessingInfo
|
|
405
272
|
processing_info = AttachmentProcessingInfo(
|
|
@@ -408,9 +275,6 @@ class SmartOpenStreamingStorage:
|
|
|
408
275
|
item=attachment,
|
|
409
276
|
)
|
|
410
277
|
|
|
411
|
-
logger.debug(
|
|
412
|
-
f"Successfully validated attachment: {attachment_info.storage_key}"
|
|
413
|
-
)
|
|
414
278
|
return processing_info
|
|
415
279
|
|
|
416
280
|
except (ValueError, TypeError, KeyError) as e:
|
|
@@ -431,9 +295,6 @@ class SmartOpenStreamingStorage:
|
|
|
431
295
|
Iterator that yields chunks of the attachment content
|
|
432
296
|
"""
|
|
433
297
|
try:
|
|
434
|
-
logger.debug(
|
|
435
|
-
f"Starting streaming read of {storage_key} from bucket: {bucket}, key: {key}"
|
|
436
|
-
)
|
|
437
298
|
with self.storage_client.stream_read(bucket, key) as content_stream:
|
|
438
299
|
# Stream in chunks instead of reading entire file
|
|
439
300
|
chunk_count = 0
|
|
@@ -446,9 +307,6 @@ class SmartOpenStreamingStorage:
|
|
|
446
307
|
total_bytes += len(chunk)
|
|
447
308
|
yield chunk
|
|
448
309
|
|
|
449
|
-
logger.debug(
|
|
450
|
-
f"Completed streaming {chunk_count} chunks ({total_bytes} bytes) for {storage_key}"
|
|
451
|
-
)
|
|
452
310
|
except Exception as e:
|
|
453
311
|
logger.warning(f"Failed to stream attachment {storage_key}: {e}")
|
|
454
312
|
# Yield empty content on failure
|
|
@@ -457,10 +315,10 @@ class SmartOpenStreamingStorage:
|
|
|
457
315
|
def _collect_and_validate_attachments(
|
|
458
316
|
self, data: dict
|
|
459
317
|
) -> list[AttachmentProcessingInfo]:
|
|
460
|
-
"""Collect and validate
|
|
318
|
+
"""Collect and validate attachments using the same contextual approach as DSR report builder.
|
|
461
319
|
|
|
462
|
-
This method
|
|
463
|
-
|
|
320
|
+
This method uses the shared contextual processing logic to ensure consistency
|
|
321
|
+
between DSR report builder and streaming storage.
|
|
464
322
|
|
|
465
323
|
Args:
|
|
466
324
|
data: The data dictionary containing items with attachments
|
|
@@ -468,20 +326,64 @@ class SmartOpenStreamingStorage:
|
|
|
468
326
|
Returns:
|
|
469
327
|
List of validated AttachmentProcessingInfo objects
|
|
470
328
|
"""
|
|
471
|
-
#
|
|
472
|
-
|
|
329
|
+
# Initialize tracking structures (similar to DSR report builder)
|
|
330
|
+
used_filenames_data: set[str] = set()
|
|
331
|
+
used_filenames_attachments: set[str] = set()
|
|
332
|
+
processed_attachments: dict[tuple[str, str], str] = {}
|
|
333
|
+
|
|
334
|
+
# Use the shared contextual processing function
|
|
335
|
+
processed_attachments_list = process_attachments_contextually(
|
|
336
|
+
data,
|
|
337
|
+
used_filenames_data,
|
|
338
|
+
used_filenames_attachments,
|
|
339
|
+
processed_attachments,
|
|
340
|
+
enable_streaming=True, # Always use streaming mode for storage
|
|
341
|
+
)
|
|
473
342
|
|
|
474
|
-
#
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
if validated:
|
|
479
|
-
validated_attachments.append(validated)
|
|
343
|
+
# Convert to AttachmentProcessingInfo objects using shared utility
|
|
344
|
+
return convert_processed_attachments_to_attachment_processing_info(
|
|
345
|
+
processed_attachments_list, self._validate_attachment
|
|
346
|
+
)
|
|
480
347
|
|
|
481
|
-
|
|
482
|
-
|
|
348
|
+
def _collect_and_validate_attachments_from_dsr_builder(
|
|
349
|
+
self, data: dict, dsr_builder: "DSRReportBuilder"
|
|
350
|
+
) -> list[AttachmentProcessingInfo]:
|
|
351
|
+
"""Collect and validate attachments using the DSR report builder's processed attachments.
|
|
352
|
+
|
|
353
|
+
This method reuses the DSR report builder's processed attachments to avoid
|
|
354
|
+
duplicate processing and ensure consistency.
|
|
355
|
+
|
|
356
|
+
Args:
|
|
357
|
+
data: The data dictionary containing items with attachments
|
|
358
|
+
dsr_builder: The DSR report builder instance that has already processed attachments
|
|
359
|
+
|
|
360
|
+
Returns:
|
|
361
|
+
List of validated AttachmentProcessingInfo objects
|
|
362
|
+
"""
|
|
363
|
+
# Use the DSR report builder's processed attachments
|
|
364
|
+
# Create temporary sets for compatibility with the shared function
|
|
365
|
+
used_filenames_data = set()
|
|
366
|
+
used_filenames_attachments = set()
|
|
367
|
+
|
|
368
|
+
# Populate the temporary sets from the DSR builder's per-dataset tracking
|
|
369
|
+
for dataset_name, filenames in dsr_builder.used_filenames_per_dataset.items():
|
|
370
|
+
if dataset_name == "attachments":
|
|
371
|
+
used_filenames_attachments.update(filenames)
|
|
372
|
+
else:
|
|
373
|
+
used_filenames_data.update(filenames)
|
|
374
|
+
|
|
375
|
+
processed_attachments_list = process_attachments_contextually(
|
|
376
|
+
data,
|
|
377
|
+
used_filenames_data,
|
|
378
|
+
used_filenames_attachments,
|
|
379
|
+
dsr_builder.processed_attachments,
|
|
380
|
+
enable_streaming=True, # Always use streaming mode for storage
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
# Convert to AttachmentProcessingInfo objects using shared utility
|
|
384
|
+
return convert_processed_attachments_to_attachment_processing_info(
|
|
385
|
+
processed_attachments_list, self._validate_attachment
|
|
483
386
|
)
|
|
484
|
-
return validated_attachments
|
|
485
387
|
|
|
486
388
|
@retry_cloud_storage_operation(
|
|
487
389
|
provider="smart_open_streaming",
|
|
@@ -525,6 +427,9 @@ class SmartOpenStreamingStorage:
|
|
|
525
427
|
if not privacy_request:
|
|
526
428
|
raise ValueError("Privacy request must be provided")
|
|
527
429
|
|
|
430
|
+
# Reset used filenames for this upload operation
|
|
431
|
+
self.used_filenames_per_dataset.clear()
|
|
432
|
+
|
|
528
433
|
# Use default buffer config if none provided
|
|
529
434
|
if buffer_config is None:
|
|
530
435
|
buffer_config = StreamingBufferConfig()
|
|
@@ -639,18 +544,22 @@ class SmartOpenStreamingStorage:
|
|
|
639
544
|
"""
|
|
640
545
|
# Generate the DSR report first
|
|
641
546
|
try:
|
|
642
|
-
|
|
547
|
+
dsr_builder = DSRReportBuilder(
|
|
643
548
|
privacy_request=privacy_request,
|
|
644
549
|
dsr_data=data,
|
|
645
|
-
|
|
550
|
+
enable_streaming=True,
|
|
551
|
+
)
|
|
552
|
+
dsr_buffer = dsr_builder.generate()
|
|
646
553
|
# Reset buffer position to ensure it can be read multiple times
|
|
647
554
|
dsr_buffer.seek(0)
|
|
648
555
|
except Exception as e:
|
|
649
556
|
logger.error(f"Failed to generate DSR report: {e}")
|
|
650
557
|
raise StorageUploadError(f"Failed to generate DSR report: {e}") from e
|
|
651
558
|
|
|
652
|
-
#
|
|
653
|
-
all_attachments = self.
|
|
559
|
+
# Use the DSR report builder's processed attachments to avoid duplicates
|
|
560
|
+
all_attachments = self._collect_and_validate_attachments_from_dsr_builder(
|
|
561
|
+
data, dsr_builder
|
|
562
|
+
)
|
|
654
563
|
|
|
655
564
|
if not all_attachments:
|
|
656
565
|
# No attachments, just upload the DSR report
|
|
@@ -667,7 +576,7 @@ class SmartOpenStreamingStorage:
|
|
|
667
576
|
)
|
|
668
577
|
except Exception as e:
|
|
669
578
|
logger.error(
|
|
670
|
-
f"Failed to generate presigned URL for {config.
|
|
579
|
+
f"Failed to generate presigned URL for {config.file_key}: {e}"
|
|
671
580
|
)
|
|
672
581
|
raise StorageUploadError(
|
|
673
582
|
f"Failed to generate presigned URL: {e}"
|
|
@@ -693,7 +602,6 @@ class SmartOpenStreamingStorage:
|
|
|
693
602
|
with self.storage_client.stream_upload(
|
|
694
603
|
config.bucket_name,
|
|
695
604
|
config.file_key,
|
|
696
|
-
content_type="application/zip",
|
|
697
605
|
) as upload_stream:
|
|
698
606
|
for chunk in stream_zip(combined_entries):
|
|
699
607
|
upload_stream.write(chunk)
|
|
@@ -708,9 +616,7 @@ class SmartOpenStreamingStorage:
|
|
|
708
616
|
config.bucket_name, config.file_key
|
|
709
617
|
)
|
|
710
618
|
except Exception as e:
|
|
711
|
-
logger.error(
|
|
712
|
-
f"Failed to generate presigned URL for {config.bucket_name}/{config.file_key}: {e}"
|
|
713
|
-
)
|
|
619
|
+
logger.error(f"Failed to generate presigned URL for {config.file_key}: {e}")
|
|
714
620
|
raise StorageUploadError(f"Failed to generate presigned URL: {e}") from e
|
|
715
621
|
|
|
716
622
|
@retry_cloud_storage_operation(
|
|
@@ -747,7 +653,7 @@ class SmartOpenStreamingStorage:
|
|
|
747
653
|
batch_size: Number of attachments to process in each batch
|
|
748
654
|
resp_format: Response format (csv, json)
|
|
749
655
|
"""
|
|
750
|
-
# Collect and validate all attachments
|
|
656
|
+
# Collect and validate all attachments using shared contextual processing
|
|
751
657
|
all_attachments = self._collect_and_validate_attachments(data)
|
|
752
658
|
|
|
753
659
|
if not all_attachments:
|
|
@@ -770,9 +676,7 @@ class SmartOpenStreamingStorage:
|
|
|
770
676
|
)
|
|
771
677
|
|
|
772
678
|
# Use smart-open's streaming upload capability
|
|
773
|
-
with self.storage_client.stream_upload(
|
|
774
|
-
bucket_name, file_key, content_type="application/zip"
|
|
775
|
-
) as upload_stream:
|
|
679
|
+
with self.storage_client.stream_upload(bucket_name, file_key) as upload_stream:
|
|
776
680
|
for chunk in stream_zip(zip_generator):
|
|
777
681
|
upload_stream.write(chunk)
|
|
778
682
|
|
|
@@ -800,9 +704,7 @@ class SmartOpenStreamingStorage:
|
|
|
800
704
|
zip_generator = self._convert_to_stream_zip_format(data_files_generator)
|
|
801
705
|
|
|
802
706
|
# Use smart-open streaming upload
|
|
803
|
-
with self.storage_client.stream_upload(
|
|
804
|
-
bucket_name, file_key, content_type="application/zip"
|
|
805
|
-
) as upload_stream:
|
|
707
|
+
with self.storage_client.stream_upload(bucket_name, file_key) as upload_stream:
|
|
806
708
|
for chunk in stream_zip(zip_generator):
|
|
807
709
|
upload_stream.write(chunk)
|
|
808
710
|
|
|
@@ -838,12 +740,10 @@ class SmartOpenStreamingStorage:
|
|
|
838
740
|
data_files_generator = self._create_data_files(
|
|
839
741
|
data, resp_format, all_attachments
|
|
840
742
|
)
|
|
841
|
-
logger.debug("Yielding data files for ZIP")
|
|
842
743
|
yield from self._convert_to_stream_zip_format(data_files_generator)
|
|
843
744
|
|
|
844
745
|
# Then, yield attachment files (already in stream_zip format, stream directly)
|
|
845
746
|
attachment_files_generator = self._create_attachment_files(all_attachments)
|
|
846
|
-
logger.debug("Yielding attachment files for ZIP")
|
|
847
747
|
yield from attachment_files_generator
|
|
848
748
|
|
|
849
749
|
def _create_data_files(
|
|
@@ -958,16 +858,29 @@ class SmartOpenStreamingStorage:
|
|
|
958
858
|
|
|
959
859
|
try:
|
|
960
860
|
source_bucket, source_key = self._parse_storage_url(storage_key)
|
|
961
|
-
logger.debug(
|
|
962
|
-
f"Parsed storage URL - bucket: {source_bucket}, key: {source_key}"
|
|
963
|
-
)
|
|
964
861
|
except ValueError as e:
|
|
965
|
-
logger.error(f"Could not parse storage URL: {storage_key} - {e}")
|
|
966
862
|
raise StorageUploadError(
|
|
967
863
|
f"Could not parse storage URL: {storage_key} - {e}"
|
|
968
864
|
) from e
|
|
969
865
|
|
|
970
|
-
|
|
866
|
+
# Generate unique filename using same logic as DSR report builder
|
|
867
|
+
original_filename = (
|
|
868
|
+
attachment_info.attachment.file_name or DEFAULT_ATTACHMENT_NAME
|
|
869
|
+
)
|
|
870
|
+
|
|
871
|
+
# Determine dataset name from base_path using shared utility
|
|
872
|
+
dataset_name = determine_dataset_name_from_path(attachment_info.base_path)
|
|
873
|
+
|
|
874
|
+
if dataset_name not in self.used_filenames_per_dataset:
|
|
875
|
+
self.used_filenames_per_dataset[dataset_name] = set()
|
|
876
|
+
|
|
877
|
+
unique_filename = get_unique_filename(
|
|
878
|
+
original_filename, self.used_filenames_per_dataset[dataset_name]
|
|
879
|
+
)
|
|
880
|
+
self.used_filenames_per_dataset[dataset_name].add(unique_filename)
|
|
881
|
+
file_path = resolve_attachment_storage_path(
|
|
882
|
+
unique_filename, attachment_info.base_path
|
|
883
|
+
)
|
|
971
884
|
|
|
972
885
|
try:
|
|
973
886
|
content_stream = self._create_attachment_content_stream(
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
from typing import Any
|
|
5
|
+
from typing import Any, Optional
|
|
6
6
|
|
|
7
7
|
from fides.api.service.storage.streaming.base_storage_client import BaseStorageClient
|
|
8
8
|
from fides.api.service.storage.streaming.s3.s3_storage_client import S3StorageClient
|
|
@@ -17,7 +17,9 @@ class StorageClientFactory:
|
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
19
|
@staticmethod
|
|
20
|
-
def create_client(
|
|
20
|
+
def create_client(
|
|
21
|
+
storage_type: str, auth_method: Optional[str], storage_secrets: Any
|
|
22
|
+
) -> BaseStorageClient:
|
|
21
23
|
"""Create a provider-specific storage client.
|
|
22
24
|
|
|
23
25
|
Args:
|
|
@@ -34,7 +36,9 @@ class StorageClientFactory:
|
|
|
34
36
|
normalized_type = StorageClientFactory._normalize_storage_type(storage_type)
|
|
35
37
|
|
|
36
38
|
if normalized_type == "s3":
|
|
37
|
-
|
|
39
|
+
if not auth_method:
|
|
40
|
+
raise ValueError("auth_method is required for S3 storage")
|
|
41
|
+
return S3StorageClient(auth_method, storage_secrets)
|
|
38
42
|
if normalized_type == "gcs":
|
|
39
43
|
raise NotImplementedError("GCS storage is not yet supported")
|
|
40
44
|
raise ValueError(f"Unsupported storage type: {storage_type}")
|