ethyca-fides 2.68.1b2__py2.py3-none-any.whl → 2.68.1b4__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.

Files changed (318) hide show
  1. {ethyca_fides-2.68.1b2.dist-info → ethyca_fides-2.68.1b4.dist-info}/METADATA +3 -1
  2. {ethyca_fides-2.68.1b2.dist-info → ethyca_fides-2.68.1b4.dist-info}/RECORD +253 -232
  3. fides/_version.py +3 -3
  4. fides/api/alembic/migrations/versions/90502bcda282_update_request_tasks_add_polling_async.py +35 -0
  5. fides/api/alembic/migrations/versions/b1a2c3d4e5f6_add_location_to_privacy_request.py +26 -0
  6. fides/api/api/v1/api.py +2 -0
  7. fides/api/api/v1/endpoints/dsr_package_link.py +167 -0
  8. fides/api/api/v1/endpoints/privacy_request_endpoints.py +31 -1
  9. fides/api/api/v1/endpoints/user_endpoints.py +4 -0
  10. fides/api/common_exceptions.py +12 -3
  11. fides/api/models/detection_discovery/core.py +6 -0
  12. fides/api/models/privacy_request/privacy_request.py +1 -0
  13. fides/api/models/privacy_request/request_task.py +25 -0
  14. fides/api/models/privacy_request/webhook.py +33 -1
  15. fides/api/oauth/utils.py +122 -57
  16. fides/api/schemas/application_config.py +7 -0
  17. fides/api/schemas/connection_configuration/connection_type_system_map.py +6 -0
  18. fides/api/schemas/enums/__init__.py +0 -0
  19. fides/api/schemas/enums/connection_category.py +20 -0
  20. fides/api/schemas/enums/integration_feature.py +23 -0
  21. fides/api/schemas/external_https.py +9 -0
  22. fides/api/schemas/privacy_center_config.py +48 -19
  23. fides/api/schemas/privacy_request.py +16 -0
  24. fides/api/schemas/saas/display_info.py +19 -0
  25. fides/api/schemas/saas/saas_config.py +2 -0
  26. fides/api/schemas/storage/storage.py +4 -0
  27. fides/api/service/async_dsr/__init__.py +0 -0
  28. fides/api/service/async_dsr/async_dsr_service.py +75 -0
  29. fides/api/service/connectors/saas_connector.py +5 -6
  30. fides/api/service/privacy_request/dsr_package/dsr_report_builder.py +6 -4
  31. fides/api/service/privacy_request/request_runner_service.py +41 -4
  32. fides/api/service/privacy_request/request_service.py +50 -2
  33. fides/api/service/storage/storage_uploader_service.py +80 -5
  34. fides/api/service/storage/streaming/__init__.py +42 -0
  35. fides/api/service/storage/streaming/base_storage_client.py +61 -0
  36. fides/api/service/storage/streaming/dsr_storage.py +98 -0
  37. fides/api/service/storage/streaming/retry.py +282 -0
  38. fides/api/service/storage/streaming/s3/__init__.py +5 -0
  39. fides/api/service/storage/streaming/s3/s3_storage_client.py +113 -0
  40. fides/api/service/storage/streaming/s3/streaming_s3.py +196 -0
  41. fides/api/service/storage/streaming/schemas.py +173 -0
  42. fides/api/service/storage/streaming/smart_open_client.py +265 -0
  43. fides/api/service/storage/streaming/smart_open_streaming_storage.py +998 -0
  44. fides/api/service/storage/streaming/storage_client_factory.py +60 -0
  45. fides/api/task/graph_task.py +4 -4
  46. fides/api/task/manual/manual_task_graph_task.py +3 -4
  47. fides/api/util/connection_type.py +20 -0
  48. fides/api/util/text.py +51 -0
  49. fides/common/api/v1/urn_registry.py +3 -0
  50. fides/config/execution_settings.py +4 -0
  51. fides/service/privacy_request/privacy_request_service.py +84 -9
  52. fides/ui-build/static/admin/404.html +1 -1
  53. fides/ui-build/static/admin/_next/static/LRCvfOqg1kP5kGnkD84G4/_buildManifest.js +1 -0
  54. fides/ui-build/static/admin/_next/static/chunks/1099-b973dfdfc5c3de90.js +1 -0
  55. fides/ui-build/static/admin/_next/static/chunks/1345-b60d1f3442379c73.js +1 -0
  56. fides/ui-build/static/admin/_next/static/chunks/{1817-c90365325f8a3d75.js → 1817-74692de5d760a664.js} +1 -1
  57. fides/ui-build/static/admin/_next/static/chunks/{1975.e5cc7a1ccd477671.js → 1975.78e719130cfe3fd6.js} +1 -1
  58. fides/ui-build/static/admin/_next/static/chunks/{2921-46f9465c2852a46b.js → 2921-2d9261e8e2e127c0.js} +1 -1
  59. fides/ui-build/static/admin/_next/static/chunks/3620-ebd89f91b82661e8.js +1 -0
  60. fides/ui-build/static/admin/_next/static/chunks/3729-ccf90cdaae158f39.js +1 -0
  61. fides/ui-build/static/admin/_next/static/chunks/3847-2759bf1f47a1d29e.js +1 -0
  62. fides/ui-build/static/admin/_next/static/chunks/3855-4174a4d4c205d6e8.js +1 -0
  63. fides/ui-build/static/admin/_next/static/chunks/3872-660aba76572c811b.js +1 -0
  64. fides/ui-build/static/admin/_next/static/chunks/{3923-a33633feba5e655e.js → 3923-c6cdc2e5278ae9a7.js} +1 -1
  65. fides/ui-build/static/admin/_next/static/chunks/{401-741bb31b586b7c96.js → 401-8bc2c6c84172c096.js} +1 -1
  66. fides/ui-build/static/admin/_next/static/chunks/{4121-94354b50a41f8497.js → 4121-9a4ebceff9accb7f.js} +1 -1
  67. fides/ui-build/static/admin/_next/static/chunks/431-86ad2beeb93c95c9.js +1 -0
  68. fides/ui-build/static/admin/_next/static/chunks/4608-4d31340b0d0157c1.js +1 -0
  69. fides/ui-build/static/admin/_next/static/chunks/4786-aaef673b30c19e2e.js +1 -0
  70. fides/ui-build/static/admin/_next/static/chunks/4808-a654c7f7a1ca62c8.js +1 -0
  71. fides/ui-build/static/admin/_next/static/chunks/4844-cd7e1d0c7bb94094.js +1 -0
  72. fides/ui-build/static/admin/_next/static/chunks/5258-bc4a25d43e4aa07d.js +1 -0
  73. fides/ui-build/static/admin/_next/static/chunks/5487-37c78c4799ba5223.js +1 -0
  74. fides/ui-build/static/admin/_next/static/chunks/549-2213dc1c34143cda.js +1 -0
  75. fides/ui-build/static/admin/_next/static/chunks/{6084-02abe12327fc3dbc.js → 6084-55cc66e7c94f0686.js} +1 -1
  76. fides/ui-build/static/admin/_next/static/chunks/{6853-270261ef5537a106.js → 6853-313ce974d33432fb.js} +1 -1
  77. fides/ui-build/static/admin/_next/static/chunks/6954-021bd06d0ab59c3c.js +1 -0
  78. fides/ui-build/static/admin/_next/static/chunks/7476-2fc286c2a9125eb8.js +1 -0
  79. fides/ui-build/static/admin/_next/static/chunks/7630-b9a41262a69edf5e.js +1 -0
  80. fides/ui-build/static/admin/_next/static/chunks/768-034e121688a3bbdd.js +1 -0
  81. fides/ui-build/static/admin/_next/static/chunks/{787-5ba991cad1f7664a.js → 787-8df7118742e84908.js} +1 -1
  82. fides/ui-build/static/admin/_next/static/chunks/79-d2ace89108ead8ae.js +1 -0
  83. fides/ui-build/static/admin/_next/static/chunks/796-2de6dac5f311d54a.js +1 -0
  84. fides/ui-build/static/admin/_next/static/chunks/8002-cfdc6574bd841892.js +1 -0
  85. fides/ui-build/static/admin/_next/static/chunks/9046-c44e41da49338c6c.js +1 -0
  86. fides/ui-build/static/admin/_next/static/chunks/9676.b7d5d1d90b9da224.js +1 -0
  87. fides/ui-build/static/admin/_next/static/chunks/{9826-8c81c97a72510fcf.js → 9826-d9addbd5ac990fa4.js} +1 -1
  88. fides/ui-build/static/admin/_next/static/chunks/9951-6ee5c0a23951a07f.js +1 -0
  89. fides/ui-build/static/admin/_next/static/chunks/pages/{404-9174cdb70126c2c5.js → 404-9644eb282f2dcd71.js} +1 -1
  90. fides/ui-build/static/admin/_next/static/chunks/pages/{_app-2c10f6b217b7978b.js → _app-284cba7174fa1f16.js} +136 -135
  91. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-621416493c89ef01.js → manual-42b7fd34712f49bd.js} +1 -1
  92. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{multiple-0b9908c3e1dfe49e.js → multiple-4f164eab0960bbe0.js} +1 -1
  93. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems-985d3c9179e69d7f.js +1 -0
  94. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/{add-vendors-5bb1b31ae8752250.js → add-vendors-61090926e5f98a5d.js} +1 -1
  95. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure-17ffe691b91cee2e.js +1 -0
  96. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{[id]-4e4d9426743b5cb4.js → [id]-95c13bca5c1e575e.js} +1 -1
  97. fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-experience-d72460348fadcab8.js → privacy-experience-609399510a60beb9.js} +1 -1
  98. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-3e7ddc252da00c98.js → [id]-d7d8f228ac74b26e.js} +1 -1
  99. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-35a7c305beee9428.js → new-821c0f82d5a2b7d4.js} +1 -1
  100. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-8365782543cf6ab9.js +1 -0
  101. fides/ui-build/static/admin/_next/static/chunks/pages/consent/{properties-ab96939421639153.js → properties-40a7aa65f4d13cf9.js} +1 -1
  102. fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-e4bacfc5c2ed2324.js +1 -0
  103. fides/ui-build/static/admin/_next/static/chunks/pages/{consent-13240e3ca77acfeb.js → consent-70c5c6aa5389d99f.js} +1 -1
  104. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/{[resourceUrn]-aad6047a4604b945.js → [resourceUrn]-adc500a03e239857.js} +1 -1
  105. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/{[projectUrn]-bd37b407c80c6986.js → [projectUrn]-3207f62e5012611b.js} +1 -1
  106. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects-7b42dee0fb696658.js +1 -0
  107. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/{[resourceUrn]-b6b98cea25dd94fa.js → [resourceUrn]-c8b3d090e4ba60d3.js} +1 -1
  108. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog-31a45ea2ca2a7f04.js +1 -0
  109. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-6172c2eb539319fd.js +1 -0
  110. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-945d354ff057fb03.js +1 -0
  111. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-d9795e00f39cf4e9.js +1 -0
  112. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-657833fd8528280f.js +1 -0
  113. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-31e6c54794a9883e.js → [resourceUrn]-22eec362dfbb1d2a.js} +1 -1
  114. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-2822a423a7ad0550.js → detection-4decce5ef996e563.js} +1 -1
  115. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-f98dd251babb7e28.js → [resourceUrn]-01acdd1ad492fd89.js} +1 -1
  116. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-56eb4c014f0d96a3.js → discovery-85fdbf4cde60d910.js} +1 -1
  117. fides/ui-build/static/admin/_next/static/chunks/pages/{datamap-8f88dc31c5144ea8.js → datamap-3a4b89fb21d14753.js} +1 -1
  118. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/[...subfieldNames]-cb8d303f56091bd5.js +1 -0
  119. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]-401c8be76d9daec7.js +1 -0
  120. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]-97e2d375b21cfe43.js +1 -0
  121. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/new-40ef544ca1f2c9b9.js +1 -0
  122. fides/ui-build/static/admin/_next/static/chunks/pages/dataset-e3c763f8e71f8e24.js +1 -0
  123. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-67a7fe58b96ea739.js → [id]-152e5d15705ec072.js} +1 -1
  124. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/new-651b10cae0e99a05.js +1 -0
  125. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-03c54bc9fb18d2b0.js +1 -0
  126. fides/ui-build/static/admin/_next/static/chunks/pages/{index-876bfd7210040cec.js → index-3d19b9ffa15a928a.js} +1 -1
  127. fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-4b0bb4ccfb237d41.js +1 -0
  128. fides/ui-build/static/admin/_next/static/chunks/pages/integrations-78d4e0c14654148b.js +1 -0
  129. fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{[id]-6e796c3fe632280b.js → [id]-72cb360a6d14e701.js} +1 -1
  130. fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{add-template-fa0f3841c5bdfdeb.js → add-template-0ed67cf774d5cbf5.js} +1 -1
  131. fides/ui-build/static/admin/_next/static/chunks/pages/messaging-b06a2204e2a5b667.js +1 -0
  132. fides/ui-build/static/admin/_next/static/chunks/pages/poc/ant-components-7050899b3f792129.js +1 -0
  133. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{AntForm-11503454a62d8d7b.js → AntForm-7c3466f4d5797e55.js} +1 -1
  134. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikAntFormItem-a504941807bdb7f1.js → FormikAntFormItem-8de252f25871bab9.js} +1 -1
  135. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikControlled-0119403c8ff97f83.js → FormikControlled-cd6de0da47f980cf.js} +1 -1
  136. fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikField-94f6d57d6c94ddf7.js → FormikField-7c238a881fe30e28.js} +1 -1
  137. fides/ui-build/static/admin/_next/static/chunks/pages/poc/{forms-ed1a3ae09d72df89.js → forms-d4f3e8f67f76f146.js} +1 -1
  138. fides/ui-build/static/admin/_next/static/chunks/pages/poc/table-migration-c9220e20c1d93758.js +1 -0
  139. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-b9d6886a3f157120.js +1 -0
  140. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-9c1fd7867b2d80d7.js +1 -0
  141. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-fc959ed21dbce38c.js +1 -0
  142. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure-44a4a638dcb2722a.js +1 -0
  143. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-1433c9f9501a884f.js +1 -0
  144. fides/ui-build/static/admin/_next/static/chunks/pages/properties/{[id]-41976b28503623cd.js → [id]-16e0b42cb342aa5f.js} +1 -1
  145. fides/ui-build/static/admin/_next/static/chunks/pages/properties/{add-property-cb438d8f5ec6007a.js → add-property-ebd114a86b809391.js} +1 -1
  146. fides/ui-build/static/admin/_next/static/chunks/pages/{properties-b6db7036993709b3.js → properties-901be5fa4a48f48c.js} +1 -1
  147. fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-da9ced1e20681154.js +1 -0
  148. fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/alpha-0174554c0ac5958f.js +1 -0
  149. fides/ui-build/static/admin/_next/static/chunks/pages/settings/about-6f45ddbf675e66d2.js +1 -0
  150. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/[purpose_id]-275c49e6089c5c9f.js +1 -0
  151. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-1a8d05e19f06d857.js +1 -0
  152. fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-49d86b9ca4523ca6.js +1 -0
  153. fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-386368bf7cb31771.js → domain-records-f71b4b95d91db926.js} +1 -1
  154. fides/ui-build/static/admin/_next/static/chunks/pages/settings/domains-a595cad18cf04673.js +1 -0
  155. fides/ui-build/static/admin/_next/static/chunks/pages/settings/email-templates-6f7f9751689b042c.js +1 -0
  156. fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-b41fb5ad277088ab.js → locations-e2c88d7f779fe604.js} +1 -1
  157. fides/ui-build/static/admin/_next/static/chunks/pages/settings/organization-c65acd2b7ab04753.js +1 -0
  158. fides/ui-build/static/admin/_next/static/chunks/pages/settings/{regulations-a94dfeea43fbca7d.js → regulations-c1c699eeb40a9dc0.js} +1 -1
  159. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/test-datasets-a274e2191b87e315.js +1 -0
  160. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-18b316e2dad73731.js → [id]-4a48b4f996a64957.js} +1 -1
  161. fides/ui-build/static/admin/_next/static/chunks/pages/systems-30debc87925634d9.js +1 -0
  162. fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-ca625b1296a029f0.js +1 -0
  163. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/{[id]-3237881945acc0ee.js → [id]-7a3180b235eb8846.js} +1 -1
  164. fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-a3a50d9d79066935.js → user-management-5e2d0acf575252ca.js} +1 -1
  165. fides/ui-build/static/admin/_next/static/chunks/{webpack-69658aeaf6155d89.js → webpack-4502d4d67006b48f.js} +1 -1
  166. fides/ui-build/static/admin/_next/static/css/43d0c0fc207767eb.css +1 -0
  167. fides/ui-build/static/admin/add-systems/manual.html +1 -1
  168. fides/ui-build/static/admin/add-systems/multiple.html +1 -1
  169. fides/ui-build/static/admin/add-systems.html +1 -1
  170. fides/ui-build/static/admin/consent/configure/add-vendors.html +1 -1
  171. fides/ui-build/static/admin/consent/configure.html +1 -1
  172. fides/ui-build/static/admin/consent/privacy-experience/[id].html +1 -1
  173. fides/ui-build/static/admin/consent/privacy-experience/new.html +1 -1
  174. fides/ui-build/static/admin/consent/privacy-experience.html +1 -1
  175. fides/ui-build/static/admin/consent/privacy-notices/[id].html +1 -1
  176. fides/ui-build/static/admin/consent/privacy-notices/new.html +1 -1
  177. fides/ui-build/static/admin/consent/privacy-notices.html +1 -1
  178. fides/ui-build/static/admin/consent/properties.html +1 -1
  179. fides/ui-build/static/admin/consent/reporting.html +1 -1
  180. fides/ui-build/static/admin/consent.html +1 -1
  181. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn].html +1 -1
  182. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn].html +1 -1
  183. fides/ui-build/static/admin/data-catalog/[systemId]/projects.html +1 -1
  184. fides/ui-build/static/admin/data-catalog/[systemId]/resources/[resourceUrn].html +1 -1
  185. fides/ui-build/static/admin/data-catalog/[systemId]/resources.html +1 -1
  186. fides/ui-build/static/admin/data-catalog.html +1 -1
  187. fides/ui-build/static/admin/data-discovery/action-center/[monitorId]/[systemId].html +1 -1
  188. fides/ui-build/static/admin/data-discovery/action-center/[monitorId].html +1 -1
  189. fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
  190. fides/ui-build/static/admin/data-discovery/activity.html +1 -1
  191. fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
  192. fides/ui-build/static/admin/data-discovery/detection.html +1 -1
  193. fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
  194. fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
  195. fides/ui-build/static/admin/datamap.html +1 -1
  196. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
  197. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
  198. fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
  199. fides/ui-build/static/admin/dataset/new.html +1 -1
  200. fides/ui-build/static/admin/dataset.html +1 -1
  201. fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
  202. fides/ui-build/static/admin/datastore-connection/new.html +1 -1
  203. fides/ui-build/static/admin/datastore-connection.html +1 -1
  204. fides/ui-build/static/admin/index.html +1 -1
  205. fides/ui-build/static/admin/integrations/[id].html +1 -1
  206. fides/ui-build/static/admin/integrations.html +1 -1
  207. fides/ui-build/static/admin/lib/fides-preview.js +1 -1
  208. fides/ui-build/static/admin/lib/fides-tcf.js +2 -2
  209. fides/ui-build/static/admin/login/[provider].html +1 -1
  210. fides/ui-build/static/admin/login.html +1 -1
  211. fides/ui-build/static/admin/messaging/[id].html +1 -1
  212. fides/ui-build/static/admin/messaging/add-template.html +1 -1
  213. fides/ui-build/static/admin/messaging.html +1 -1
  214. fides/ui-build/static/admin/poc/ant-components.html +1 -1
  215. fides/ui-build/static/admin/poc/form-experiments/AntForm.html +1 -1
  216. fides/ui-build/static/admin/poc/form-experiments/FormikAntFormItem.html +1 -1
  217. fides/ui-build/static/admin/poc/form-experiments/FormikControlled.html +1 -1
  218. fides/ui-build/static/admin/poc/form-experiments/FormikField.html +1 -1
  219. fides/ui-build/static/admin/poc/form-experiments/FormikSpreadField.html +1 -1
  220. fides/ui-build/static/admin/poc/forms.html +1 -1
  221. fides/ui-build/static/admin/poc/table-migration.html +1 -1
  222. fides/ui-build/static/admin/privacy-requests/[id].html +1 -1
  223. fides/ui-build/static/admin/privacy-requests/configure/messaging.html +1 -1
  224. fides/ui-build/static/admin/privacy-requests/configure/storage.html +1 -1
  225. fides/ui-build/static/admin/privacy-requests/configure.html +1 -1
  226. fides/ui-build/static/admin/privacy-requests.html +1 -1
  227. fides/ui-build/static/admin/properties/[id].html +1 -1
  228. fides/ui-build/static/admin/properties/add-property.html +1 -1
  229. fides/ui-build/static/admin/properties.html +1 -1
  230. fides/ui-build/static/admin/reporting/datamap.html +1 -1
  231. fides/ui-build/static/admin/settings/about/alpha.html +1 -1
  232. fides/ui-build/static/admin/settings/about.html +1 -1
  233. fides/ui-build/static/admin/settings/consent/[configuration_id]/[purpose_id].html +1 -1
  234. fides/ui-build/static/admin/settings/consent.html +1 -1
  235. fides/ui-build/static/admin/settings/custom-fields.html +1 -1
  236. fides/ui-build/static/admin/settings/domain-records.html +1 -1
  237. fides/ui-build/static/admin/settings/domains.html +1 -1
  238. fides/ui-build/static/admin/settings/email-templates.html +1 -1
  239. fides/ui-build/static/admin/settings/locations.html +1 -1
  240. fides/ui-build/static/admin/settings/organization.html +1 -1
  241. fides/ui-build/static/admin/settings/regulations.html +1 -1
  242. fides/ui-build/static/admin/systems/configure/[id]/test-datasets.html +1 -1
  243. fides/ui-build/static/admin/systems/configure/[id].html +1 -1
  244. fides/ui-build/static/admin/systems.html +1 -1
  245. fides/ui-build/static/admin/taxonomy.html +1 -1
  246. fides/ui-build/static/admin/user-management/new.html +1 -1
  247. fides/ui-build/static/admin/user-management/profile/[id].html +1 -1
  248. fides/ui-build/static/admin/user-management.html +1 -1
  249. fides/ui-build/static/admin/_next/static/chunks/203-0c6cadcda98bdd33.js +0 -1
  250. fides/ui-build/static/admin/_next/static/chunks/3450-9314e1b15df8a8da.js +0 -1
  251. fides/ui-build/static/admin/_next/static/chunks/3855-4267fd8193e7f525.js +0 -1
  252. fides/ui-build/static/admin/_next/static/chunks/3872-ac5feefd40b61ae3.js +0 -1
  253. fides/ui-build/static/admin/_next/static/chunks/409-5bc4369b80a8c11d.js +0 -1
  254. fides/ui-build/static/admin/_next/static/chunks/4230-1ebc8c0ab293a077.js +0 -1
  255. fides/ui-build/static/admin/_next/static/chunks/431-a34d7ceff17c2169.js +0 -1
  256. fides/ui-build/static/admin/_next/static/chunks/4608-557fb24665b2e4bf.js +0 -1
  257. fides/ui-build/static/admin/_next/static/chunks/5309-ffdec884eec79d29.js +0 -1
  258. fides/ui-build/static/admin/_next/static/chunks/5574-831167a8da90e2e6.js +0 -1
  259. fides/ui-build/static/admin/_next/static/chunks/6662-499c189f932a35aa.js +0 -1
  260. fides/ui-build/static/admin/_next/static/chunks/6780-7d28e030f6516e5d.js +0 -1
  261. fides/ui-build/static/admin/_next/static/chunks/6882-7cc1d14e27a80c10.js +0 -1
  262. fides/ui-build/static/admin/_next/static/chunks/6954-7784e8d5ad6b8110.js +0 -1
  263. fides/ui-build/static/admin/_next/static/chunks/7476-4de465016d3433b4.js +0 -1
  264. fides/ui-build/static/admin/_next/static/chunks/7630-2a5c57787632693d.js +0 -1
  265. fides/ui-build/static/admin/_next/static/chunks/7725-c79513b04113112b.js +0 -1
  266. fides/ui-build/static/admin/_next/static/chunks/79-98cfab20bb831137.js +0 -1
  267. fides/ui-build/static/admin/_next/static/chunks/796-0b768155bf20505f.js +0 -1
  268. fides/ui-build/static/admin/_next/static/chunks/8735-f84afcc50885883c.js +0 -1
  269. fides/ui-build/static/admin/_next/static/chunks/9046-97a972cc8a8ed24d.js +0 -1
  270. fides/ui-build/static/admin/_next/static/chunks/9226-318dadf1c050ecda.js +0 -1
  271. fides/ui-build/static/admin/_next/static/chunks/9676.9e6828b42ef05e06.js +0 -1
  272. fides/ui-build/static/admin/_next/static/chunks/9951-4df2b67e0def5500.js +0 -1
  273. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems-18e96ce81dab51a4.js +0 -1
  274. fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure-54d7c7310763c66d.js +0 -1
  275. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-6bc3b73a21576869.js +0 -1
  276. fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-fe3d6887fecf0f86.js +0 -1
  277. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects-e4770acf7044e2f5.js +0 -1
  278. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog-0db635c3483c9da8.js +0 -1
  279. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-0c0e0a7798345541.js +0 -1
  280. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-3c56e5fe072a44c6.js +0 -1
  281. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-58827eb86516931f.js +0 -1
  282. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-6a90131dcecd694c.js +0 -1
  283. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/[...subfieldNames]-145fe9e4cfcb231d.js +0 -1
  284. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]-8a1e5d140785c1e9.js +0 -1
  285. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]-227b5db4b472a6a7.js +0 -1
  286. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/new-8401f17fe5d9a1dc.js +0 -1
  287. fides/ui-build/static/admin/_next/static/chunks/pages/dataset-7d77b3ad069be268.js +0 -1
  288. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/new-90a8df230cb89877.js +0 -1
  289. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-cfb25b02abb8da71.js +0 -1
  290. fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-4e286a1e501a0c73.js +0 -1
  291. fides/ui-build/static/admin/_next/static/chunks/pages/integrations-3fdc55d4c129e618.js +0 -1
  292. fides/ui-build/static/admin/_next/static/chunks/pages/messaging-8f9c006b6166f002.js +0 -1
  293. fides/ui-build/static/admin/_next/static/chunks/pages/poc/ant-components-6ba7ae4f26c06cb0.js +0 -1
  294. fides/ui-build/static/admin/_next/static/chunks/pages/poc/table-migration-e8db3ad525e7ddbd.js +0 -1
  295. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-c14dd24592369467.js +0 -1
  296. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-100d7d03930629a8.js +0 -1
  297. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-6f8d1b3ec83cfcf0.js +0 -1
  298. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure-3ce15577435d47cb.js +0 -1
  299. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-709bcb0bc6a5382d.js +0 -1
  300. fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-4bc3e281409265cc.js +0 -1
  301. fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/alpha-1ea40fcd6b4268bf.js +0 -1
  302. fides/ui-build/static/admin/_next/static/chunks/pages/settings/about-65c7600fadc6e55a.js +0 -1
  303. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/[purpose_id]-33dab986141b3663.js +0 -1
  304. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-1195042727c399ed.js +0 -1
  305. fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-71b98858ecb4e097.js +0 -1
  306. fides/ui-build/static/admin/_next/static/chunks/pages/settings/domains-cf427e04f862b5d2.js +0 -1
  307. fides/ui-build/static/admin/_next/static/chunks/pages/settings/email-templates-eabeeec5bf2773c6.js +0 -1
  308. fides/ui-build/static/admin/_next/static/chunks/pages/settings/organization-ee56698ae3a6a78b.js +0 -1
  309. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/test-datasets-0e2e98cc38ee5499.js +0 -1
  310. fides/ui-build/static/admin/_next/static/chunks/pages/systems-c32589c86081b750.js +0 -1
  311. fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-a8f09bf8f3204ca7.js +0 -1
  312. fides/ui-build/static/admin/_next/static/css/a72179b1754aadd3.css +0 -1
  313. fides/ui-build/static/admin/_next/static/qvk5eMANVfwYkdURE7fgG/_buildManifest.js +0 -1
  314. {ethyca_fides-2.68.1b2.dist-info → ethyca_fides-2.68.1b4.dist-info}/WHEEL +0 -0
  315. {ethyca_fides-2.68.1b2.dist-info → ethyca_fides-2.68.1b4.dist-info}/entry_points.txt +0 -0
  316. {ethyca_fides-2.68.1b2.dist-info → ethyca_fides-2.68.1b4.dist-info}/licenses/LICENSE +0 -0
  317. {ethyca_fides-2.68.1b2.dist-info → ethyca_fides-2.68.1b4.dist-info}/top_level.txt +0 -0
  318. /fides/ui-build/static/admin/_next/static/{qvk5eMANVfwYkdURE7fgG → LRCvfOqg1kP5kGnkD84G4}/_ssgManifest.js +0 -0
@@ -5,13 +5,12 @@ import zipfile
5
5
  from collections import defaultdict
6
6
  from io import BytesIO
7
7
  from pathlib import Path
8
- from typing import Any, Optional
8
+ from typing import TYPE_CHECKING, Any, Optional
9
9
 
10
10
  import jinja2
11
11
  from jinja2 import Environment, FileSystemLoader
12
12
  from loguru import logger
13
13
 
14
- from fides.api.models.privacy_request import PrivacyRequest
15
14
  from fides.api.schemas.policy import ActionType
16
15
  from fides.api.util.storage_util import StorageJSONEncoder, format_size
17
16
 
@@ -21,6 +20,9 @@ TEXT_COLOR = "#4A5568"
21
20
  HEADER_COLOR = "#FAFAFA"
22
21
  BORDER_COLOR = "#E2E8F0"
23
22
 
23
+ if TYPE_CHECKING:
24
+ from fides.api.models.privacy_request import PrivacyRequest # pragma: no cover
25
+
24
26
 
25
27
  # pylint: disable=too-many-instance-attributes
26
28
  class DsrReportBuilder:
@@ -42,7 +44,7 @@ class DsrReportBuilder:
42
44
 
43
45
  def __init__(
44
46
  self,
45
- privacy_request: PrivacyRequest,
47
+ privacy_request: "PrivacyRequest",
46
48
  dsr_data: dict[str, Any],
47
49
  ):
48
50
  """
@@ -387,7 +389,7 @@ class DsrReportBuilder:
387
389
  return self.baos
388
390
 
389
391
 
390
- def _map_privacy_request(privacy_request: PrivacyRequest) -> dict[str, Any]:
392
+ def _map_privacy_request(privacy_request: "PrivacyRequest") -> dict[str, Any]:
391
393
  """Creates a map with a subset of values from the privacy request"""
392
394
  request_data: dict[str, Any] = {}
393
395
  request_data["id"] = privacy_request.id
@@ -41,6 +41,9 @@ from fides.api.models.privacy_request import (
41
41
  ProvidedIdentityType,
42
42
  can_run_checkpoint,
43
43
  )
44
+ from fides.api.models.privacy_request.webhook import (
45
+ generate_privacy_request_download_token,
46
+ )
44
47
  from fides.api.schemas.base_class import FidesSchema
45
48
  from fides.api.schemas.messaging.messaging import (
46
49
  AccessRequestCompleteBodyParams,
@@ -49,6 +52,7 @@ from fides.api.schemas.messaging.messaging import (
49
52
  from fides.api.schemas.policy import ActionType, CurrentStep
50
53
  from fides.api.schemas.privacy_request import PrivacyRequestStatus
51
54
  from fides.api.schemas.redis_cache import Identity
55
+ from fides.api.schemas.storage.storage import StorageType
52
56
  from fides.api.service.connectors import FidesConnector, get_connector
53
57
  from fides.api.service.connectors.consent_email_connector import (
54
58
  CONSENT_EMAIL_CONNECTOR_TYPES,
@@ -82,6 +86,7 @@ from fides.api.util.logger import Pii, _log_exception, _log_warning
82
86
  from fides.api.util.logger_context_utils import LoggerContextKeys, log_context
83
87
  from fides.api.util.memory_watchdog import memory_limiter
84
88
  from fides.common.api.v1.urn_registry import (
89
+ PRIVACY_CENTER_DSR_PACKAGE,
85
90
  PRIVACY_REQUEST_TRANSFER_TO_PARENT,
86
91
  V1_URL_PREFIX,
87
92
  )
@@ -740,14 +745,17 @@ def run_privacy_request(
740
745
  else MessagingActionType.PRIVACY_REQUEST_COMPLETE_DELETION
741
746
  )
742
747
 
743
- if message_send_enabled(
748
+ message_send_result = message_send_enabled(
744
749
  session,
745
750
  privacy_request.property_id,
746
751
  action_type,
747
752
  legacy_request_completion_enabled,
748
- ) and not policy.get_rules_for_action(
753
+ )
754
+ has_consent_rules = policy.get_rules_for_action(
749
755
  action_type=ActionType.consent
750
- ):
756
+ )
757
+
758
+ if message_send_result and not has_consent_rules:
751
759
  if not access_result_urls:
752
760
  # For DSR 3.0, if the request had both access and erasure rules, this needs to be fetched
753
761
  # from the database because the Privacy Request would have exited
@@ -763,6 +771,7 @@ def run_privacy_request(
763
771
  access_result_urls,
764
772
  identity_data,
765
773
  privacy_request.property_id,
774
+ privacy_request.id,
766
775
  )
767
776
  except (
768
777
  IdentityNotFoundException,
@@ -780,6 +789,7 @@ def initiate_privacy_request_completion_email(
780
789
  access_result_urls: list[str],
781
790
  identity_data: dict[str, Any],
782
791
  property_id: Optional[str],
792
+ privacy_request_id: str,
783
793
  ) -> None:
784
794
  """
785
795
  :param session: SQLAlchemy Session
@@ -787,6 +797,7 @@ def initiate_privacy_request_completion_email(
787
797
  :param access_result_urls: list of urls generated by access request upload
788
798
  :param identity_data: Dict of identity data
789
799
  :param property_id: Property id associated with the privacy request
800
+ :param privacy_request_id: ID of the privacy request for generating DSR package links
790
801
  """
791
802
  config_proxy = ConfigProxy(session)
792
803
  if not (
@@ -801,6 +812,32 @@ def initiate_privacy_request_completion_email(
801
812
  phone_number=identity_data.get(ProvidedIdentityType.phone_number.value),
802
813
  )
803
814
  if policy.get_rules_for_action(action_type=ActionType.access):
815
+ # Check if any rule has enable_access_package_redirect=True and enable_streaming=True in storage config
816
+ # This can be extended to other storage types and non streaming access results in the future
817
+ use_dsr_package_links = False
818
+ for rule in policy.get_rules_for_action(action_type=ActionType.access):
819
+ storage_destination = rule.get_storage_destination(session)
820
+ if (
821
+ storage_destination.type == StorageType.s3
822
+ and storage_destination.details.get("enable_access_package_redirect")
823
+ and storage_destination.details.get("enable_streaming")
824
+ ):
825
+ use_dsr_package_links = True
826
+ break
827
+
828
+ # Generate appropriate URLs based on streaming configuration
829
+ if use_dsr_package_links and config_proxy.privacy_center.url:
830
+ # Use DSR package links instead of direct storage URLs
831
+ # Generate the download token for security
832
+ download_token = generate_privacy_request_download_token(privacy_request_id)
833
+
834
+ # Generate DSR package URLs for the messaging template system
835
+ dsr_package_url = f"{config_proxy.privacy_center.url}/api{PRIVACY_CENTER_DSR_PACKAGE.format(privacy_request_id=privacy_request_id)}?token={download_token}"
836
+ download_links = [dsr_package_url]
837
+ else:
838
+ # Use original direct storage URLs
839
+ download_links = access_result_urls
840
+
804
841
  # synchronous for now since failure to send complete emails is fatal to request
805
842
  dispatch_message(
806
843
  db=session,
@@ -808,7 +845,7 @@ def initiate_privacy_request_completion_email(
808
845
  to_identity=to_identity,
809
846
  service_type=config_proxy.notifications.notification_service_type,
810
847
  message_body_params=AccessRequestCompleteBodyParams(
811
- download_links=access_result_urls,
848
+ download_links=download_links,
812
849
  subject_request_download_time_in_days=CONFIG.security.subject_request_download_link_ttl_seconds
813
850
  / 86400,
814
851
  ),
@@ -11,12 +11,13 @@ from sqlalchemy import text
11
11
  from sqlalchemy.orm import Session
12
12
  from sqlalchemy.sql.elements import TextClause
13
13
 
14
- from fides.api.common_exceptions import PrivacyRequestNotFound
14
+ from fides.api.common_exceptions import PrivacyRequestError, PrivacyRequestNotFound
15
15
  from fides.api.models.privacy_request import (
16
16
  EXITED_EXECUTION_LOG_STATUSES,
17
17
  PrivacyRequest,
18
18
  RequestTask,
19
19
  )
20
+ from fides.api.models.privacy_request.request_task import AsyncTaskType
20
21
  from fides.api.models.worker_task import ExecutionLogStatus
21
22
  from fides.api.schemas.drp_privacy_request import DrpPrivacyRequestCreate
22
23
  from fides.api.schemas.policy import ActionType
@@ -43,6 +44,7 @@ from fides.config import CONFIG
43
44
  PRIVACY_REQUEST_STATUS_CHANGE_POLL = "privacy_request_status_change_poll"
44
45
  DSR_DATA_REMOVAL = "dsr_data_removal"
45
46
  INTERRUPTED_TASK_REQUEUE_POLL = "interrupted_task_requeue_poll"
47
+ ASYNC_TASKS_STATUS_POLLING = "async_tasks_status_polling"
46
48
 
47
49
 
48
50
  def build_required_privacy_request_kwargs(
@@ -357,6 +359,27 @@ def initiate_interrupted_task_requeue_poll() -> None:
357
359
  )
358
360
 
359
361
 
362
+ def initiate_async_tasks_status_polling() -> None:
363
+ """Initiates scheduler to check for and requeue pending polling async tasks"""
364
+ if CONFIG.test_mode:
365
+ return
366
+
367
+ assert (
368
+ scheduler.running
369
+ ), "Scheduler is not running! Cannot add async tasks status polling job."
370
+
371
+ logger.info("Initiating scheduler for async tasks status polling")
372
+ scheduler.add_job(
373
+ func=poll_async_tasks_status,
374
+ trigger="interval",
375
+ kwargs={},
376
+ id=ASYNC_TASKS_STATUS_POLLING,
377
+ coalesce=True,
378
+ replace_existing=True,
379
+ seconds=CONFIG.execution.async_tasks_status_polling_interval_seconds,
380
+ )
381
+
382
+
360
383
  def get_cached_task_id(entity_id: str) -> Optional[str]:
361
384
  """Gets the cached task ID for a privacy request or request task by ID.
362
385
 
@@ -465,7 +488,6 @@ def _handle_privacy_request_requeue(
465
488
  )
466
489
 
467
490
  from fides.service.privacy_request.privacy_request_service import ( # pylint: disable=cyclic-import
468
- PrivacyRequestError,
469
491
  _requeue_privacy_request,
470
492
  )
471
493
 
@@ -662,3 +684,29 @@ def requeue_interrupted_tasks(self: DatabaseTask) -> None:
662
684
  # Requeue the privacy request if needed
663
685
  if should_requeue:
664
686
  _handle_privacy_request_requeue(db, privacy_request)
687
+
688
+
689
+ @celery_app.task(base=DatabaseTask, bind=True)
690
+ def poll_async_tasks_status(self: DatabaseTask) -> None:
691
+ """
692
+ Poll the status of async tasks that are awaiting processing.
693
+ """
694
+
695
+ with self.get_new_session() as db:
696
+ logger.debug("Polling for async tasks status")
697
+
698
+ # Get all tasks that are awaiting processing and are from polling async tasks
699
+ async_tasks = (
700
+ db.query(RequestTask)
701
+ .filter(RequestTask.status == ExecutionLogStatus.awaiting_processing)
702
+ .filter(RequestTask.async_type == AsyncTaskType.polling)
703
+ .all()
704
+ )
705
+ if async_tasks:
706
+ logger.debug(f"Found {len(async_tasks)} async tasks to poll")
707
+ from fides.api.service.async_dsr.async_dsr_service import ( # pylint: disable=cyclic-import
708
+ requeue_polling_request,
709
+ )
710
+
711
+ for async_task in async_tasks:
712
+ requeue_polling_request(db, async_task)
@@ -14,6 +14,7 @@ from fides.api.schemas.storage.storage import (
14
14
  StorageDetails,
15
15
  StorageType,
16
16
  )
17
+ from fides.api.service.storage.streaming.s3.streaming_s3 import upload_to_s3_streaming
17
18
  from fides.api.tasks.storage import upload_to_gcs, upload_to_local, upload_to_s3
18
19
 
19
20
 
@@ -33,6 +34,8 @@ def upload(
33
34
  :param storage_key: Key representing where to upload data
34
35
  :return str representing location of upload (url or simply a description of where to find the data)
35
36
  """
37
+ logger.debug("upload called with storage_key: {}", storage_key)
38
+
36
39
  config: Optional[StorageConfig] = StorageConfig.get_by(
37
40
  db=db, field="key", value=storage_key
38
41
  )
@@ -40,16 +43,41 @@ def upload(
40
43
  if config is None:
41
44
  logger.warning("Storage type not found: {}", storage_key)
42
45
  raise StorageUploadError(f"Storage type not found: {storage_key}")
46
+
47
+ logger.debug(
48
+ "Retrieved storage config: key={}, type={}, has_secrets={}",
49
+ config.key,
50
+ config.type,
51
+ config.secrets is not None,
52
+ )
53
+
54
+ if config.secrets:
55
+ logger.debug("Storage config secrets type: {}", type(config.secrets))
56
+ if isinstance(config.secrets, dict):
57
+ logger.debug("Storage config secrets keys: {}", list(config.secrets.keys()))
58
+ else:
59
+ logger.debug("Storage config secrets is not a dict: {}", config.secrets)
60
+ else:
61
+ logger.warning("Storage config has no secrets!")
62
+
43
63
  uploader: Any = _get_uploader_from_config_type(config.type) # type: ignore
64
+ logger.debug(
65
+ "Using uploader: {}",
66
+ uploader.__name__ if hasattr(uploader, "__name__") else type(uploader),
67
+ )
68
+
44
69
  return uploader(db, config, data, privacy_request)
45
70
 
46
71
 
47
- def get_extension(resp_format: ResponseFormat) -> str:
72
+ def get_extension(resp_format: ResponseFormat, enable_streaming: bool = False) -> str:
48
73
  """
49
74
  Determine file extension for various response formats.
50
75
 
51
76
  CSV's and HTML reports are zipped together before uploading to s3.
52
77
  """
78
+ if enable_streaming:
79
+ return "zip"
80
+
53
81
  if resp_format == ResponseFormat.csv:
54
82
  return "zip"
55
83
 
@@ -62,7 +90,9 @@ def get_extension(resp_format: ResponseFormat) -> str:
62
90
  raise NotImplementedError(f"No extension defined for {resp_format}")
63
91
 
64
92
 
65
- def _construct_file_key(request_id: str, config: StorageConfig) -> str:
93
+ def _construct_file_key(
94
+ request_id: str, config: StorageConfig, enable_streaming: bool = False
95
+ ) -> str:
66
96
  """Constructs file key based on desired naming convention and request id, e.g. 23847234.json"""
67
97
  naming = config.details.get(
68
98
  StorageDetails.NAMING.value, FileNaming.request_id.value
@@ -70,7 +100,7 @@ def _construct_file_key(request_id: str, config: StorageConfig) -> str:
70
100
  if naming != FileNaming.request_id.value:
71
101
  raise ValueError(f"File naming of {naming} not supported")
72
102
 
73
- return f"{request_id}.{get_extension(config.format)}" # type: ignore
103
+ return f"{request_id}.{get_extension(config.format, enable_streaming)}" # type: ignore
74
104
 
75
105
 
76
106
  def _get_uploader_from_config_type(storage_type: StorageType) -> Any:
@@ -88,13 +118,58 @@ def _s3_uploader(
88
118
  data: Dict,
89
119
  privacy_request: PrivacyRequest,
90
120
  ) -> str:
91
- """Constructs necessary info needed for s3 before calling upload"""
92
- file_key: str = _construct_file_key(privacy_request.id, config)
121
+ """
122
+ Constructs necessary info needed for s3 before calling upload.
123
+ If `enable_streaming` is configured in the storage config, we use a streaming approach for better memory efficiency.
124
+ Otherwise we fall back to the traditional upload method.
125
+ """
126
+ logger.debug(
127
+ "_s3_uploader called with config: key={}, type={}, has_secrets={}",
128
+ config.key,
129
+ config.type,
130
+ config.secrets is not None,
131
+ )
132
+
133
+ if config.secrets:
134
+ logger.debug(
135
+ "Config secrets keys: {}",
136
+ (
137
+ list(config.secrets.keys())
138
+ if isinstance(config.secrets, dict)
139
+ else "Not a dict"
140
+ ),
141
+ )
142
+ logger.debug("Config secrets type: {}", type(config.secrets))
143
+ else:
144
+ logger.warning("Config secrets is None or empty!")
145
+
146
+ enable_streaming = config.details.get(StorageDetails.ENABLE_STREAMING.value, False)
147
+ file_key: str = _construct_file_key(privacy_request.id, config, enable_streaming)
93
148
 
94
149
  bucket_name = config.details[StorageDetails.BUCKET.value]
95
150
  auth_method = config.details[StorageDetails.AUTH_METHOD.value]
96
151
  document = None
97
152
 
153
+ if enable_streaming:
154
+ file_key = f"{privacy_request.id}.zip"
155
+ # Use streaming upload for better memory efficiency
156
+ logger.debug("Using streaming S3 upload for {}", file_key)
157
+ logger.debug("Calling upload_to_s3_streaming with secrets: {}", config.secrets)
158
+ return upload_to_s3_streaming(
159
+ config.secrets, # type: ignore
160
+ data,
161
+ bucket_name,
162
+ file_key,
163
+ config.format.value, # type: ignore
164
+ privacy_request,
165
+ document,
166
+ auth_method,
167
+ )
168
+
169
+ file_key = _construct_file_key(privacy_request.id, config)
170
+
171
+ # Fall back to traditional upload method
172
+ logger.debug("Using traditional S3 upload for {}", file_key)
98
173
  return upload_to_s3(
99
174
  config.secrets, # type: ignore
100
175
  data,
@@ -0,0 +1,42 @@
1
+ """Streaming storage module for efficient cloud-to-cloud data transfer."""
2
+
3
+ from .base_storage_client import BaseStorageClient
4
+ from .retry import (
5
+ PermanentError,
6
+ RetryableError,
7
+ RetryConfig,
8
+ TransientError,
9
+ create_retry_config_from_settings,
10
+ is_transient_error,
11
+ retry_cloud_storage_operation,
12
+ retry_with_backoff,
13
+ )
14
+ from .s3.s3_storage_client import S3StorageClient
15
+ from .schemas import AttachmentInfo, StorageUploadConfig, StreamingBufferConfig
16
+ from .smart_open_client import SmartOpenStorageClient
17
+ from .smart_open_streaming_storage import SmartOpenStreamingStorage
18
+ from .storage_client_factory import StorageClientFactory
19
+
20
+ __all__ = [
21
+ # Base classes and interfaces
22
+ "BaseStorageClient",
23
+ "StorageClientFactory",
24
+ # Provider-specific clients
25
+ "S3StorageClient",
26
+ # Main clients
27
+ "SmartOpenStorageClient",
28
+ "SmartOpenStreamingStorage",
29
+ # Schemas
30
+ "StorageUploadConfig",
31
+ "StreamingBufferConfig",
32
+ "AttachmentInfo",
33
+ # Retry utilities
34
+ "RetryConfig",
35
+ "RetryableError",
36
+ "TransientError",
37
+ "PermanentError",
38
+ "retry_with_backoff",
39
+ "retry_cloud_storage_operation",
40
+ "create_retry_config_from_settings",
41
+ "is_transient_error",
42
+ ]
@@ -0,0 +1,61 @@
1
+ """Base abstract class for cloud storage clients."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from abc import ABC, abstractmethod
6
+ from typing import Any, Optional
7
+
8
+ from fideslang.validation import AnyHttpUrlString
9
+
10
+
11
+ class BaseStorageClient(ABC):
12
+ """Abstract base class for cloud storage clients.
13
+
14
+ This class defines the interface that all provider-specific storage clients
15
+ must implement. It provides a common contract for URI building, transport
16
+ parameters, and presigned URL generation.
17
+ """
18
+
19
+ def __init__(self, storage_secrets: Any):
20
+ """Initialize the storage client with secrets.
21
+
22
+ Args:
23
+ storage_secrets: Provider-specific storage credentials and configuration.
24
+ Derived classes will specify the exact type they expect.
25
+ """
26
+ self.storage_secrets = storage_secrets
27
+
28
+ @abstractmethod
29
+ def build_uri(self, bucket: str, key: str) -> str:
30
+ """Build the URI for the storage location.
31
+
32
+ Args:
33
+ bucket: Storage bucket/container name
34
+ key: Object key/path
35
+
36
+ Returns:
37
+ URI string for smart-open
38
+ """
39
+
40
+ @abstractmethod
41
+ def get_transport_params(self) -> dict[str, Any]:
42
+ """Get transport parameters for smart-open.
43
+
44
+ Returns:
45
+ Dictionary of transport parameters for smart-open
46
+ """
47
+
48
+ @abstractmethod
49
+ def generate_presigned_url(
50
+ self, bucket: str, key: str, ttl_seconds: Optional[int] = None
51
+ ) -> AnyHttpUrlString:
52
+ """Generate a presigned URL for the object.
53
+
54
+ Args:
55
+ bucket: Storage bucket/container name
56
+ key: Object key/path
57
+ ttl_seconds: Time to live in seconds
58
+
59
+ Returns:
60
+ Presigned URL for the object
61
+ """
@@ -0,0 +1,98 @@
1
+ from __future__ import annotations
2
+
3
+ import zipfile
4
+ from datetime import datetime
5
+ from io import BytesIO
6
+ from typing import Any, Generator, Tuple
7
+
8
+ from loguru import logger
9
+ from stream_zip import _ZIP_32_TYPE
10
+
11
+ from fides.api.service.storage.streaming.schemas import AttachmentProcessingInfo
12
+ from fides.api.service.storage.streaming.smart_open_client import SmartOpenStorageClient
13
+
14
+
15
+ def stream_dsr_buffer_to_storage(
16
+ storage_client: SmartOpenStorageClient,
17
+ bucket_name: str,
18
+ file_key: str,
19
+ dsr_buffer: BytesIO,
20
+ content_type: str = "application/zip",
21
+ ) -> None:
22
+ """Stream DSR buffer to storage using smart-open streaming.
23
+
24
+ This function handles only the storage streaming concern, accepting a pre-generated
25
+ DSR buffer to maintain clear separation of concerns.
26
+
27
+ Args:
28
+ storage_client: The storage client for streaming uploads
29
+ bucket_name: Storage bucket name
30
+ file_key: File key in storage
31
+ dsr_buffer: Pre-generated DSR report buffer (BytesIO)
32
+ content_type: MIME type for the uploaded file (defaults to application/zip)
33
+ """
34
+ # Get the content from the buffer
35
+ content = dsr_buffer.getvalue()
36
+ try:
37
+ # Use smart-open's streaming upload for efficient memory usage
38
+ with storage_client.stream_upload(
39
+ bucket_name, file_key, content_type=content_type
40
+ ) as upload_stream:
41
+ upload_stream.write(content)
42
+
43
+ except Exception as e:
44
+ logger.error("Failed to upload DSR report using smart-open streaming: {}", e)
45
+ raise e
46
+
47
+
48
+ def create_dsr_report_files_generator(
49
+ dsr_buffer: BytesIO,
50
+ all_attachments: list["AttachmentProcessingInfo"],
51
+ bucket_name: str,
52
+ max_workers: int,
53
+ batch_size: int,
54
+ ) -> Generator[
55
+ Tuple[str, datetime, int, Any, Generator[bytes, None, None]], None, None
56
+ ]:
57
+ """Create a ZIP generator for DSR report HTML files only.
58
+
59
+ This method extracts and yields the HTML files from a DSR report buffer.
60
+ Note: This function only handles the DSR report files (HTML, CSS, etc.).
61
+ The caller is responsible for combining this with attachment files to create
62
+ the complete ZIP.
63
+
64
+ Args:
65
+ dsr_buffer: The DSR report buffer (ZIP file as BytesIO)
66
+ all_attachments: List of validated attachments (used for logging only)
67
+ bucket_name: Storage bucket name (used for logging only)
68
+ max_workers: Maximum parallel workers (used for logging only)
69
+ batch_size: Number of attachments to process in each batch (used for logging only)
70
+
71
+ Returns:
72
+ Generator yielding DSR report files in stream_zip format
73
+ """
74
+ logger.debug(
75
+ f"Creating DSR report files generator with {len(all_attachments)} attachments"
76
+ )
77
+
78
+ # Reset buffer position to ensure we can read from it
79
+ dsr_buffer.seek(0)
80
+
81
+ # Extract and yield the DSR report files from the buffer
82
+ # The dsr_buffer is already a ZIP file, so we need to extract and re-yield its contents
83
+ with zipfile.ZipFile(dsr_buffer) as dsr_zip:
84
+ for file_info in dsr_zip.filelist:
85
+ if not file_info.is_dir():
86
+ # Read the file content and yield it in stream_zip format
87
+ content = dsr_zip.read(file_info.filename)
88
+
89
+ def content_generator(
90
+ file_content: bytes,
91
+ ) -> Generator[bytes, None, None]:
92
+ yield file_content
93
+
94
+ yield file_info.filename, datetime.now(), 0o644, _ZIP_32_TYPE(), content_generator(
95
+ content
96
+ )
97
+
98
+ logger.debug("DSR report files extracted and ready for ZIP creation")