ethyca-fides 2.71.1b0__py2.py3-none-any.whl → 2.71.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.

Potentially problematic release.


This version of ethyca-fides might be problematic. Click here for more details.

Files changed (182) hide show
  1. {ethyca_fides-2.71.1b0.dist-info → ethyca_fides-2.71.1rc0.dist-info}/METADATA +2 -2
  2. {ethyca_fides-2.71.1b0.dist-info → ethyca_fides-2.71.1rc0.dist-info}/RECORD +165 -151
  3. fides/_version.py +3 -3
  4. fides/api/alembic/migrations/versions/3efe14d4469a_adds_new_experience_configs_for_vendor_.py +79 -0
  5. fides/api/alembic/migrations/versions/4bfbeff34611_add_polling_status.py +35 -0
  6. fides/api/alembic/migrations/versions/7db29f9cd77b_create_new_sub_request_table.py +95 -0
  7. fides/api/alembic/migrations/versions/9caf76161e55_make_user_assigned_data_uses_nullable_.py +64 -0
  8. fides/api/alembic/migrations/versions/b97e92b038d2_add_digest_execution_model.py +117 -0
  9. fides/api/alembic/migrations/versions/f108fa05c579_adds_optional_duration_field_to_assets.py +28 -0
  10. fides/api/api/v1/endpoints/generic_overrides.py +3 -9
  11. fides/api/common_exceptions.py +4 -0
  12. fides/api/main.py +2 -2
  13. fides/api/models/asset.py +14 -1
  14. fides/api/models/attachment.py +1 -0
  15. fides/api/models/detection_discovery/core.py +57 -3
  16. fides/api/models/digest/__init__.py +2 -0
  17. fides/api/models/digest/digest_config.py +10 -1
  18. fides/api/models/digest/digest_execution.py +132 -0
  19. fides/api/models/event_audit.py +8 -0
  20. fides/api/models/privacy_experience.py +10 -0
  21. fides/api/models/privacy_notice.py +131 -20
  22. fides/api/models/privacy_request/request_task.py +98 -1
  23. fides/api/models/worker_task.py +8 -0
  24. fides/api/schemas/saas/async_polling_configuration.py +81 -0
  25. fides/api/schemas/saas/saas_config.py +10 -3
  26. fides/api/schemas/saas/strategy_configuration.py +0 -12
  27. fides/api/service/async_dsr/handlers/__init__.py +0 -0
  28. fides/api/service/async_dsr/handlers/polling_attachment_handler.py +155 -0
  29. fides/api/service/async_dsr/handlers/polling_request_handler.py +88 -0
  30. fides/api/service/async_dsr/handlers/polling_response_handler.py +261 -0
  31. fides/api/service/async_dsr/handlers/polling_sub_request_handler.py +123 -0
  32. fides/api/service/async_dsr/strategies/__init__.py +0 -0
  33. fides/api/service/async_dsr/strategies/async_dsr_strategy.py +52 -0
  34. fides/api/service/async_dsr/strategies/async_dsr_strategy_callback.py +199 -0
  35. fides/api/service/async_dsr/strategies/async_dsr_strategy_factory.py +72 -0
  36. fides/api/service/async_dsr/strategies/async_dsr_strategy_polling.py +678 -0
  37. fides/api/service/async_dsr/utils.py +130 -0
  38. fides/api/service/connectors/fides/fides_client.py +63 -1
  39. fides/api/service/connectors/query_configs/saas_query_config.py +4 -5
  40. fides/api/service/connectors/saas_connector.py +77 -69
  41. fides/api/service/privacy_request/attachment_handling.py +9 -2
  42. fides/api/service/privacy_request/request_runner_service.py +9 -83
  43. fides/api/service/privacy_request/request_service.py +47 -74
  44. fides/api/service/saas_request/saas_request_override_factory.py +66 -1
  45. fides/api/task/execute_request_tasks.py +5 -2
  46. fides/api/task/filter_results.py +35 -2
  47. fides/api/task/graph_task.py +34 -2
  48. fides/config/execution_settings.py +7 -3
  49. fides/service/dataset/dataset_service.py +0 -39
  50. fides/service/privacy_request/privacy_request_service.py +48 -103
  51. fides/ui-build/static/admin/404.html +1 -1
  52. fides/ui-build/static/admin/_next/static/chunks/155-c1ae010c664e2245.js +1 -0
  53. fides/ui-build/static/admin/_next/static/chunks/1817-1ad037b7d6d2f6d2.js +1 -0
  54. fides/ui-build/static/admin/_next/static/chunks/{3585-f728d32fda6f1ac1.js → 3585-efd5d41f08e180c4.js} +1 -1
  55. fides/ui-build/static/admin/_next/static/chunks/5279-12c9cbdc67ad7b14.js +1 -0
  56. fides/ui-build/static/admin/_next/static/chunks/6277-182efc294d413f64.js +1 -0
  57. fides/ui-build/static/admin/_next/static/chunks/7079-bbc7b856802a4834.js +1 -0
  58. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-75e99306393938e8.js → manual-4ec03eed67572861.js} +1 -1
  59. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{[id]-fd41ffaff543e05a.js → [id]-e1e2fd704ac2d71d.js} +1 -1
  60. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{new-e74cb5ea87f15b40.js → new-a5e738a234dadc7e.js} +1 -1
  61. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-9c23fbe813c997d0.js → [id]-5fc78b78a51c239c.js} +1 -1
  62. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-0e5e38bbcfe59fd2.js → new-b79bcb93b5f4c734.js} +1 -1
  63. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-29c1fb777bd464e0.js +1 -0
  64. fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-153eb88ab4e7dc6d.js +1 -0
  65. fides/ui-build/static/admin/_next/static/chunks/pages/integrations-f682b1def859931e.js +1 -0
  66. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-febf156d2977f3ac.js +1 -0
  67. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-4d658222ec800511.js +1 -0
  68. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-547c6ef0ad52b85d.js → [id]-4d470bbf199a2f9c.js} +1 -1
  69. fides/ui-build/static/admin/_next/static/css/f38242c11f7fea64.css +1 -0
  70. fides/ui-build/static/admin/_next/static/vSOB67a-1uIVzRUKBYMSo/_buildManifest.js +1 -0
  71. fides/ui-build/static/admin/add-systems/manual.html +1 -1
  72. fides/ui-build/static/admin/add-systems/multiple.html +1 -1
  73. fides/ui-build/static/admin/add-systems.html +1 -1
  74. fides/ui-build/static/admin/consent/configure/add-vendors.html +1 -1
  75. fides/ui-build/static/admin/consent/configure.html +1 -1
  76. fides/ui-build/static/admin/consent/privacy-experience/[id].html +1 -1
  77. fides/ui-build/static/admin/consent/privacy-experience/new.html +1 -1
  78. fides/ui-build/static/admin/consent/privacy-experience.html +1 -1
  79. fides/ui-build/static/admin/consent/privacy-notices/[id].html +1 -1
  80. fides/ui-build/static/admin/consent/privacy-notices/new.html +1 -1
  81. fides/ui-build/static/admin/consent/privacy-notices.html +1 -1
  82. fides/ui-build/static/admin/consent/properties.html +1 -1
  83. fides/ui-build/static/admin/consent/reporting.html +1 -1
  84. fides/ui-build/static/admin/consent.html +1 -1
  85. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn].html +1 -1
  86. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn].html +1 -1
  87. fides/ui-build/static/admin/data-catalog/[systemId]/projects.html +1 -1
  88. fides/ui-build/static/admin/data-catalog/[systemId]/resources/[resourceUrn].html +1 -1
  89. fides/ui-build/static/admin/data-catalog/[systemId]/resources.html +1 -1
  90. fides/ui-build/static/admin/data-catalog.html +1 -1
  91. fides/ui-build/static/admin/data-discovery/action-center/[monitorId]/[systemId].html +1 -1
  92. fides/ui-build/static/admin/data-discovery/action-center/[monitorId].html +1 -1
  93. fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
  94. fides/ui-build/static/admin/data-discovery/activity.html +1 -1
  95. fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
  96. fides/ui-build/static/admin/data-discovery/detection.html +1 -1
  97. fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
  98. fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
  99. fides/ui-build/static/admin/datamap.html +1 -1
  100. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
  101. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
  102. fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
  103. fides/ui-build/static/admin/dataset/new.html +1 -1
  104. fides/ui-build/static/admin/dataset.html +1 -1
  105. fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
  106. fides/ui-build/static/admin/datastore-connection/new.html +1 -1
  107. fides/ui-build/static/admin/datastore-connection.html +1 -1
  108. fides/ui-build/static/admin/index.html +1 -1
  109. fides/ui-build/static/admin/integrations/[id].html +1 -1
  110. fides/ui-build/static/admin/integrations.html +1 -1
  111. fides/ui-build/static/admin/lib/fides-headless.js +1 -1
  112. fides/ui-build/static/admin/lib/fides-preview.js +1 -1
  113. fides/ui-build/static/admin/lib/fides-tcf.js +3 -3
  114. fides/ui-build/static/admin/lib/fides.js +3 -3
  115. fides/ui-build/static/admin/login/[provider].html +1 -1
  116. fides/ui-build/static/admin/login.html +1 -1
  117. fides/ui-build/static/admin/messaging/[id].html +1 -1
  118. fides/ui-build/static/admin/messaging/add-template.html +1 -1
  119. fides/ui-build/static/admin/messaging.html +1 -1
  120. fides/ui-build/static/admin/poc/ant-components.html +1 -1
  121. fides/ui-build/static/admin/poc/form-experiments/AntForm.html +1 -1
  122. fides/ui-build/static/admin/poc/form-experiments/FormikAntFormItem.html +1 -1
  123. fides/ui-build/static/admin/poc/form-experiments/FormikControlled.html +1 -1
  124. fides/ui-build/static/admin/poc/form-experiments/FormikField.html +1 -1
  125. fides/ui-build/static/admin/poc/form-experiments/FormikSpreadField.html +1 -1
  126. fides/ui-build/static/admin/poc/forms.html +1 -1
  127. fides/ui-build/static/admin/poc/table-migration.html +1 -1
  128. fides/ui-build/static/admin/privacy-requests/[id].html +1 -1
  129. fides/ui-build/static/admin/privacy-requests/configure/storage.html +1 -1
  130. fides/ui-build/static/admin/privacy-requests/configure.html +1 -1
  131. fides/ui-build/static/admin/privacy-requests.html +1 -1
  132. fides/ui-build/static/admin/properties/[id].html +1 -1
  133. fides/ui-build/static/admin/properties/add-property.html +1 -1
  134. fides/ui-build/static/admin/properties.html +1 -1
  135. fides/ui-build/static/admin/reporting/datamap.html +1 -1
  136. fides/ui-build/static/admin/settings/about/alpha.html +1 -1
  137. fides/ui-build/static/admin/settings/about.html +1 -1
  138. fides/ui-build/static/admin/settings/consent/[configuration_id]/[purpose_id].html +1 -1
  139. fides/ui-build/static/admin/settings/consent.html +1 -1
  140. fides/ui-build/static/admin/settings/custom-fields/[id].html +1 -1
  141. fides/ui-build/static/admin/settings/custom-fields/new.html +1 -1
  142. fides/ui-build/static/admin/settings/custom-fields.html +1 -1
  143. fides/ui-build/static/admin/settings/domain-records.html +1 -1
  144. fides/ui-build/static/admin/settings/domains.html +1 -1
  145. fides/ui-build/static/admin/settings/email-templates.html +1 -1
  146. fides/ui-build/static/admin/settings/locations.html +1 -1
  147. fides/ui-build/static/admin/settings/messaging-providers/[key].html +1 -1
  148. fides/ui-build/static/admin/settings/messaging-providers/new.html +1 -1
  149. fides/ui-build/static/admin/settings/messaging-providers.html +1 -1
  150. fides/ui-build/static/admin/settings/organization.html +1 -1
  151. fides/ui-build/static/admin/settings/privacy-requests.html +1 -1
  152. fides/ui-build/static/admin/settings/regulations.html +1 -1
  153. fides/ui-build/static/admin/systems/configure/[id]/test-datasets.html +1 -1
  154. fides/ui-build/static/admin/systems/configure/[id].html +1 -1
  155. fides/ui-build/static/admin/systems.html +1 -1
  156. fides/ui-build/static/admin/taxonomy.html +1 -1
  157. fides/ui-build/static/admin/user-management/new.html +1 -1
  158. fides/ui-build/static/admin/user-management/profile/[id].html +1 -1
  159. fides/ui-build/static/admin/user-management.html +1 -1
  160. fides/api/service/async_dsr/async_dsr_service.py +0 -195
  161. fides/api/service/async_dsr/async_dsr_strategy.py +0 -5
  162. fides/api/service/async_dsr/async_dsr_strategy_callback.py +0 -16
  163. fides/api/service/async_dsr/async_dsr_strategy_factory.py +0 -63
  164. fides/api/service/async_dsr/async_dsr_strategy_polling.py +0 -94
  165. fides/ui-build/static/admin/_next/static/IPOgh7BMBX7b_r8-scpgv/_buildManifest.js +0 -1
  166. fides/ui-build/static/admin/_next/static/chunks/155-047c3806cc41295e.js +0 -1
  167. fides/ui-build/static/admin/_next/static/chunks/1817-ca6473f31a67a804.js +0 -1
  168. fides/ui-build/static/admin/_next/static/chunks/3700-08e0703b1ef770da.js +0 -1
  169. fides/ui-build/static/admin/_next/static/chunks/6084-d0943ee628bf4388.js +0 -1
  170. fides/ui-build/static/admin/_next/static/chunks/6416-0ccadfefcdad00cc.js +0 -1
  171. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-2e1e2b7808d3b21f.js +0 -1
  172. fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-01e025f878ba806c.js +0 -1
  173. fides/ui-build/static/admin/_next/static/chunks/pages/integrations-14120a529d7dac27.js +0 -1
  174. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-7dac2302f573f5ee.js +0 -1
  175. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-e5d781b28f8e29c8.js +0 -1
  176. fides/ui-build/static/admin/_next/static/css/073713cd1eddda79.css +0 -1
  177. {ethyca_fides-2.71.1b0.dist-info → ethyca_fides-2.71.1rc0.dist-info}/WHEEL +0 -0
  178. {ethyca_fides-2.71.1b0.dist-info → ethyca_fides-2.71.1rc0.dist-info}/entry_points.txt +0 -0
  179. {ethyca_fides-2.71.1b0.dist-info → ethyca_fides-2.71.1rc0.dist-info}/licenses/LICENSE +0 -0
  180. {ethyca_fides-2.71.1b0.dist-info → ethyca_fides-2.71.1rc0.dist-info}/top_level.txt +0 -0
  181. /fides/ui-build/static/admin/_next/static/chunks/pages/{_app-a77584f9ad3334af.js → _app-a7c02dd2ff07f9e1.js} +0 -0
  182. /fides/ui-build/static/admin/_next/static/{IPOgh7BMBX7b_r8-scpgv → vSOB67a-1uIVzRUKBYMSo}/_ssgManifest.js +0 -0
fides/_version.py CHANGED
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2025-09-29T16:40:09-0600",
11
+ "date": "2025-10-03T10:03:48-0700",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "830e00751b37962f36c99b0d915abf68e75325a8",
15
- "version": "2.71.1b0"
14
+ "full-revisionid": "43bcd93b9c3859ad4a79f3825f1ebc789a58caf3",
15
+ "version": "2.71.1rc0"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -0,0 +1,79 @@
1
+ """adds new experience configs for vendor asset disclosure
2
+
3
+ Revision ID: 3efe14d4469a
4
+ Revises: f108fa05c579
5
+ Create Date: 2025-09-09 11:19:24.060587
6
+
7
+ """
8
+
9
+ import sqlalchemy as sa
10
+ from alembic import op
11
+ from sqlalchemy.dialects import postgresql
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision = "3efe14d4469a"
15
+ down_revision = "f108fa05c579"
16
+ branch_labels = None
17
+ depends_on = None
18
+
19
+
20
+ def upgrade():
21
+ # ### commands auto generated by Alembic - please adjust! ###
22
+ op.add_column(
23
+ "experienceconfigtemplate",
24
+ sa.Column(
25
+ "asset_disclosure_include_types",
26
+ postgresql.ARRAY(sa.String()),
27
+ nullable=True,
28
+ ),
29
+ )
30
+ op.add_column(
31
+ "experienceconfigtemplate",
32
+ sa.Column(
33
+ "allow_vendor_asset_disclosure",
34
+ sa.Boolean(),
35
+ server_default="f",
36
+ nullable=False,
37
+ ),
38
+ )
39
+ op.add_column(
40
+ "privacyexperienceconfig",
41
+ sa.Column(
42
+ "asset_disclosure_include_types",
43
+ postgresql.ARRAY(sa.String()),
44
+ nullable=True,
45
+ ),
46
+ )
47
+ op.add_column(
48
+ "privacyexperienceconfig",
49
+ sa.Column(
50
+ "allow_vendor_asset_disclosure",
51
+ sa.Boolean(),
52
+ server_default="f",
53
+ nullable=False,
54
+ ),
55
+ )
56
+ op.add_column(
57
+ "privacyexperienceconfighistory",
58
+ sa.Column("allow_vendor_asset_disclosure", sa.Boolean(), nullable=True),
59
+ )
60
+ op.add_column(
61
+ "privacyexperienceconfighistory",
62
+ sa.Column(
63
+ "asset_disclosure_include_types",
64
+ postgresql.ARRAY(sa.String()),
65
+ nullable=True,
66
+ ),
67
+ )
68
+ # ### end Alembic commands ###
69
+
70
+
71
+ def downgrade():
72
+ # ### commands auto generated by Alembic - please adjust! ###
73
+ op.drop_column("privacyexperienceconfighistory", "asset_disclosure_include_types")
74
+ op.drop_column("privacyexperienceconfighistory", "allow_vendor_asset_disclosure")
75
+ op.drop_column("privacyexperienceconfig", "allow_vendor_asset_disclosure")
76
+ op.drop_column("privacyexperienceconfig", "asset_disclosure_include_types")
77
+ op.drop_column("experienceconfigtemplate", "allow_vendor_asset_disclosure")
78
+ op.drop_column("experienceconfigtemplate", "asset_disclosure_include_types")
79
+ # ### end Alembic commands ###
@@ -0,0 +1,35 @@
1
+ """add polling status
2
+
3
+ Revision ID: 4bfbeff34611
4
+ Revises: 7db29f9cd77b
5
+ Create Date: 2025-09-20 23:02:45.550170
6
+
7
+ """
8
+
9
+ import sqlalchemy as sa
10
+ from alembic import op
11
+
12
+ # revision identifiers, used by Alembic.
13
+ revision = "4bfbeff34611"
14
+ down_revision = "7db29f9cd77b"
15
+ branch_labels = None
16
+ depends_on = None
17
+
18
+
19
+ def upgrade():
20
+ # Check if value already exists
21
+ connection = op.get_bind()
22
+ result = connection.execute(
23
+ sa.text(
24
+ "SELECT 1 FROM pg_enum WHERE enumlabel = 'polling' "
25
+ "AND enumtypid = (SELECT oid FROM pg_type WHERE typname = 'executionlogstatus')"
26
+ )
27
+ )
28
+
29
+ if not result.fetchone():
30
+ op.execute("ALTER TYPE executionlogstatus ADD VALUE 'polling'")
31
+
32
+
33
+ def downgrade():
34
+ # The 'polling' value will remain in the enum but won't be used by older app versions
35
+ pass
@@ -0,0 +1,95 @@
1
+ """Create new Sub Request Table
2
+
3
+ Revision ID: 7db29f9cd77b
4
+ Revises: b97e92b038d2
5
+ Create Date: 2025-09-16 14:00:16.282996
6
+
7
+ """
8
+
9
+ import sqlalchemy as sa
10
+ from alembic import op
11
+ from sqlalchemy_utils.types.encrypted.encrypted_type import (
12
+ AesGcmEngine,
13
+ StringEncryptedType,
14
+ )
15
+
16
+ from fides.api.db.base_class import JSONTypeOverride
17
+ from fides.config import CONFIG
18
+
19
+ # revision identifiers, used by Alembic.
20
+ revision = "7db29f9cd77b"
21
+ down_revision = "b97e92b038d2"
22
+ branch_labels = None
23
+ depends_on = None
24
+
25
+
26
+ def upgrade():
27
+ op.create_table(
28
+ "request_task_sub_request",
29
+ sa.Column("id", sa.String(length=255), nullable=False),
30
+ sa.Column(
31
+ "created_at",
32
+ sa.DateTime(timezone=True),
33
+ server_default=sa.text("now()"),
34
+ nullable=True,
35
+ ),
36
+ sa.Column(
37
+ "updated_at",
38
+ sa.DateTime(timezone=True),
39
+ server_default=sa.text("now()"),
40
+ nullable=True,
41
+ ),
42
+ sa.Column("request_task_id", sa.String(length=255), nullable=False),
43
+ sa.Column(
44
+ "param_values",
45
+ StringEncryptedType(
46
+ type_in=JSONTypeOverride,
47
+ key=CONFIG.security.app_encryption_key,
48
+ engine=AesGcmEngine,
49
+ padding="pkcs5",
50
+ ),
51
+ nullable=False,
52
+ ),
53
+ sa.Column("status", sa.String(), nullable=False),
54
+ sa.Column(
55
+ "access_data",
56
+ StringEncryptedType(
57
+ type_in=JSONTypeOverride,
58
+ key=CONFIG.security.app_encryption_key,
59
+ engine=AesGcmEngine,
60
+ padding="pkcs5",
61
+ ),
62
+ nullable=True,
63
+ ),
64
+ sa.Column("rows_masked", sa.Integer(), nullable=True),
65
+ sa.ForeignKeyConstraint(
66
+ ["request_task_id"],
67
+ ["requesttask.id"],
68
+ name="request_task_sub_request_request_task_id_fkey",
69
+ ondelete="CASCADE",
70
+ ),
71
+ sa.PrimaryKeyConstraint("id"),
72
+ )
73
+ op.create_index(
74
+ op.f("ix_request_task_sub_request_id"),
75
+ "request_task_sub_request",
76
+ ["id"],
77
+ unique=False,
78
+ )
79
+ op.create_index(
80
+ op.f("ix_request_task_sub_request_request_task_id"),
81
+ "request_task_sub_request",
82
+ ["request_task_id"],
83
+ unique=False,
84
+ )
85
+
86
+
87
+ def downgrade():
88
+ op.drop_index(
89
+ op.f("ix_request_task_sub_request_request_task_id"),
90
+ table_name="request_task_sub_request",
91
+ )
92
+ op.drop_index(
93
+ op.f("ix_request_task_sub_request_id"), table_name="request_task_sub_request"
94
+ )
95
+ op.drop_table("request_task_sub_request")
@@ -0,0 +1,64 @@
1
+ """Update user_assigned_data_uses so it's nullable and defaults to none
2
+
3
+ Revision ID: 9caf76161e55
4
+ Revises: 918aefc950c9
5
+ Create Date: 2025-09-29 14:14:37.837616
6
+
7
+ """
8
+
9
+ import sqlalchemy as sa
10
+ from alembic import op
11
+ from sqlalchemy.dialects import postgresql
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision = "9caf76161e55"
15
+ down_revision = "918aefc950c9"
16
+ branch_labels = None
17
+ depends_on = None
18
+
19
+
20
+ def upgrade():
21
+ # ### commands auto generated by Alembic - please adjust! ###
22
+ op.alter_column(
23
+ "stagedresource",
24
+ "user_assigned_data_uses",
25
+ existing_type=postgresql.ARRAY(sa.VARCHAR()),
26
+ nullable=True,
27
+ server_default=None,
28
+ default=None,
29
+ existing_server_default=sa.text("'{}'::character varying[]"),
30
+ )
31
+
32
+ # Update web monitor resources with empty array to null
33
+ op.execute(
34
+ """
35
+ UPDATE stagedresource
36
+ SET user_assigned_data_uses = NULL
37
+ WHERE
38
+ resource_type IN ('Cookie', 'Browser request', 'Image', 'iFrame', 'Javascript tag')
39
+ AND user_assigned_data_uses = '{}'
40
+ """
41
+ )
42
+ # ### end Alembic commands ###
43
+
44
+
45
+ def downgrade():
46
+ # ### commands auto generated by Alembic - please adjust! ###
47
+ op.execute(
48
+ """
49
+ UPDATE stagedresource
50
+ SET user_assigned_data_uses = '{}'
51
+ WHERE user_assigned_data_uses IS NULL
52
+ """
53
+ )
54
+
55
+ op.alter_column(
56
+ "stagedresource",
57
+ "user_assigned_data_uses",
58
+ existing_type=postgresql.ARRAY(sa.VARCHAR()),
59
+ nullable=False,
60
+ server_default=sa.text("'{}'::character varying[]"),
61
+ existing_server_default=None,
62
+ default=dict,
63
+ )
64
+ # ### end Alembic commands ###
@@ -0,0 +1,117 @@
1
+ """add digest execution model
2
+
3
+ Revision ID: b97e92b038d2
4
+ Revises: 3efe14d4469a
5
+ Create Date: 2025-10-01 16:42:41.900651
6
+
7
+ """
8
+
9
+ import sqlalchemy as sa
10
+ from alembic import op
11
+ from sqlalchemy.dialects import postgresql
12
+
13
+ # revision identifiers, used by Alembic.
14
+ revision = "b97e92b038d2"
15
+ down_revision = "3efe14d4469a"
16
+ branch_labels = None
17
+ depends_on = None
18
+
19
+
20
+ def upgrade():
21
+ # ### commands auto generated by Alembic - please adjust! ###
22
+ op.create_table(
23
+ "digest_task_execution",
24
+ sa.Column("id", sa.String(length=255), nullable=False),
25
+ sa.Column(
26
+ "created_at",
27
+ sa.DateTime(timezone=True),
28
+ server_default=sa.text("now()"),
29
+ nullable=True,
30
+ ),
31
+ sa.Column(
32
+ "updated_at",
33
+ sa.DateTime(timezone=True),
34
+ server_default=sa.text("now()"),
35
+ nullable=True,
36
+ ),
37
+ sa.Column("action_type", sa.String(), nullable=False),
38
+ sa.Column("digest_config_id", sa.String(), nullable=False),
39
+ sa.Column("celery_task_id", sa.String(), nullable=True),
40
+ sa.Column(
41
+ "status",
42
+ postgresql.ENUM(name="executionlogstatus", create_type=False),
43
+ nullable=False,
44
+ ),
45
+ sa.Column("total_recipients", sa.Integer(), nullable=True),
46
+ sa.Column("processed_recipients", sa.Integer(), nullable=False),
47
+ sa.Column("successful_communications", sa.Integer(), nullable=False),
48
+ sa.Column("failed_communications", sa.Integer(), nullable=False),
49
+ sa.Column(
50
+ "execution_state", postgresql.JSONB(astext_type=sa.Text()), nullable=True
51
+ ),
52
+ sa.Column(
53
+ "processed_user_ids", postgresql.JSONB(astext_type=sa.Text()), nullable=True
54
+ ),
55
+ sa.Column("started_at", sa.DateTime(timezone=True), nullable=True),
56
+ sa.Column("completed_at", sa.DateTime(timezone=True), nullable=True),
57
+ sa.Column("last_checkpoint_at", sa.DateTime(timezone=True), nullable=True),
58
+ sa.Column("error_message", sa.Text(), nullable=True),
59
+ sa.ForeignKeyConstraint(
60
+ ["digest_config_id"], ["digest_config.id"], ondelete="CASCADE"
61
+ ),
62
+ sa.PrimaryKeyConstraint("id"),
63
+ )
64
+ op.create_index(
65
+ op.f("ix_digest_task_execution_action_type"),
66
+ "digest_task_execution",
67
+ ["action_type"],
68
+ unique=False,
69
+ )
70
+ op.create_index(
71
+ op.f("ix_digest_task_execution_celery_task_id"),
72
+ "digest_task_execution",
73
+ ["celery_task_id"],
74
+ unique=False,
75
+ )
76
+ op.create_index(
77
+ op.f("ix_digest_task_execution_digest_config_id"),
78
+ "digest_task_execution",
79
+ ["digest_config_id"],
80
+ unique=False,
81
+ )
82
+ op.create_index(
83
+ op.f("ix_digest_task_execution_id"),
84
+ "digest_task_execution",
85
+ ["id"],
86
+ unique=False,
87
+ )
88
+ op.create_index(
89
+ op.f("ix_digest_task_execution_status"),
90
+ "digest_task_execution",
91
+ ["status"],
92
+ unique=False,
93
+ )
94
+ # ### end Alembic commands ###
95
+
96
+
97
+ def downgrade():
98
+ # ### commands auto generated by Alembic - please adjust! ###
99
+ op.drop_index(
100
+ op.f("ix_digest_task_execution_status"), table_name="digest_task_execution"
101
+ )
102
+ op.drop_index(
103
+ op.f("ix_digest_task_execution_id"), table_name="digest_task_execution"
104
+ )
105
+ op.drop_index(
106
+ op.f("ix_digest_task_execution_digest_config_id"),
107
+ table_name="digest_task_execution",
108
+ )
109
+ op.drop_index(
110
+ op.f("ix_digest_task_execution_celery_task_id"),
111
+ table_name="digest_task_execution",
112
+ )
113
+ op.drop_index(
114
+ op.f("ix_digest_task_execution_action_type"), table_name="digest_task_execution"
115
+ )
116
+ op.drop_table("digest_task_execution")
117
+ # ### end Alembic commands ###
@@ -0,0 +1,28 @@
1
+ """adds optional duration field to assets
2
+
3
+ Revision ID: f108fa05c579
4
+ Revises: 9caf76161e55
5
+ Create Date: 2025-08-22 12:47:31.374493
6
+
7
+ """
8
+
9
+ import sqlalchemy as sa
10
+ from alembic import op
11
+
12
+ # revision identifiers, used by Alembic.
13
+ revision = "f108fa05c579"
14
+ down_revision = "9caf76161e55"
15
+ branch_labels = None
16
+ depends_on = None
17
+
18
+
19
+ def upgrade():
20
+ # ### commands auto generated by Alembic - please adjust! ###
21
+ op.add_column("asset", sa.Column("duration", sa.String(), nullable=True))
22
+ # ### end Alembic commands ###
23
+
24
+
25
+ def downgrade():
26
+ # ### commands auto generated by Alembic - please adjust! ###
27
+ op.drop_column("asset", "duration")
28
+ # ### end Alembic commands ###
@@ -54,7 +54,6 @@ from fides.common.api.v1.urn_registry import DATASETS_CLEAN, V1_URL_PREFIX
54
54
  from fides.service.dataset.dataset_service import (
55
55
  DatasetNotFoundException,
56
56
  DatasetService,
57
- LinkedDatasetException,
58
57
  )
59
58
  from fides.service.taxonomy.taxonomy_service import TaxonomyService
60
59
 
@@ -127,7 +126,7 @@ async def update_dataset(
127
126
  except DatasetNotFoundException as e:
128
127
  raise HTTPException(
129
128
  status_code=HTTP_404_NOT_FOUND,
130
- detail=str(e),
129
+ detail={"message": str(e)},
131
130
  )
132
131
 
133
132
 
@@ -249,7 +248,7 @@ async def get_dataset(
249
248
  except DatasetNotFoundException as e:
250
249
  raise HTTPException(
251
250
  status_code=HTTP_404_NOT_FOUND,
252
- detail=str(e),
251
+ detail={"message": str(e)},
253
252
  )
254
253
 
255
254
 
@@ -276,12 +275,7 @@ async def delete_dataset(
276
275
  except DatasetNotFoundException as e:
277
276
  raise HTTPException(
278
277
  status_code=HTTP_404_NOT_FOUND,
279
- detail=str(e),
280
- )
281
- except LinkedDatasetException as e:
282
- raise HTTPException(
283
- status_code=HTTP_400_BAD_REQUEST,
284
- detail=str(e),
278
+ detail={"message": str(e)},
285
279
  )
286
280
 
287
281
 
@@ -179,6 +179,10 @@ class AwaitingAsyncTask(BaseException):
179
179
  """Request Task is Awaiting Processing - Awaiting Async Task"""
180
180
 
181
181
 
182
+ class AwaitingAsyncProcessing(BaseException):
183
+ """Request Task is actively being processed by external system - Fides is polling"""
184
+
185
+
182
186
  class UpstreamTasksNotReady(BaseException):
183
187
  """Privacy Request Task awaiting upstream tasks"""
184
188
 
fides/api/main.py CHANGED
@@ -45,9 +45,9 @@ from fides.api.service.privacy_request.email_batch_service import (
45
45
  initiate_scheduled_batch_email_send,
46
46
  )
47
47
  from fides.api.service.privacy_request.request_service import (
48
- initiate_async_tasks_status_polling,
49
48
  initiate_interrupted_task_requeue_poll,
50
49
  initiate_poll_for_exited_privacy_request_tasks,
50
+ initiate_polling_task_requeue,
51
51
  initiate_scheduled_dsr_data_removal,
52
52
  )
53
53
 
@@ -103,7 +103,7 @@ async def lifespan(wrapped_app: FastAPI) -> AsyncGenerator[None, None]:
103
103
  initiate_poll_for_exited_privacy_request_tasks()
104
104
  initiate_scheduled_dsr_data_removal()
105
105
  initiate_interrupted_task_requeue_poll()
106
- initiate_async_tasks_status_polling()
106
+ initiate_polling_task_requeue()
107
107
  initiate_bcrypt_migration_task()
108
108
  initiate_post_upgrade_index_creation()
109
109
 
fides/api/models/asset.py CHANGED
@@ -17,7 +17,7 @@ from sqlalchemy import (
17
17
  from sqlalchemy.dialects.postgresql import JSONB
18
18
  from sqlalchemy.ext.asyncio import AsyncSession
19
19
  from sqlalchemy.ext.mutable import MutableDict
20
- from sqlalchemy.orm import relationship
20
+ from sqlalchemy.orm import relationship, selectinload
21
21
 
22
22
  from fides.api.db.base_class import Base
23
23
  from fides.api.db.util import EnumColumn
@@ -47,6 +47,7 @@ class Asset(Base):
47
47
  name = Column(String, index=True, nullable=False)
48
48
  asset_type = Column(String, index=True, nullable=False)
49
49
  domain = Column(String, index=True)
50
+ duration = Column(String, nullable=True)
50
51
  parent = Column(ARRAY(String), server_default="{}", nullable=False)
51
52
  parent_domain = Column(String)
52
53
  locations = Column(ARRAY(String), server_default="{}", nullable=False)
@@ -190,5 +191,17 @@ class Asset(Base):
190
191
  .where(System.fides_key == system_fides_key)
191
192
  )
192
193
 
194
+ # Explicitly eager load the `system` relationship to make this query's
195
+ # performance predictable and prevent N+1 issues.
196
+ query = query.options(selectinload(cls.system)) # type: ignore[attr-defined]
193
197
  result = await async_session.execute(query)
194
198
  return result.scalars().all()
199
+
200
+ # Expose related System attributes for API serialization convenience
201
+ @property
202
+ def system_name(self) -> Optional[str]:
203
+ return self.system.name if self.system else None
204
+
205
+ @property
206
+ def system_fides_key(self) -> Optional[str]:
207
+ return self.system.fides_key if self.system else None
@@ -52,6 +52,7 @@ class AttachmentReferenceType(str, EnumType):
52
52
  privacy_request = "privacy_request"
53
53
  comment = "comment"
54
54
  manual_task_submission = "manual_task_submission"
55
+ request_task = "request_task"
55
56
 
56
57
 
57
58
  class AttachmentReference(Base):
@@ -60,6 +60,56 @@ class MonitorFrequency(Enum):
60
60
  QUARTERLY_MONTH_PATTERN = r"^\d+,\d+,\d+,\d+$"
61
61
 
62
62
 
63
+ class StagedResourceType(str, Enum):
64
+ """
65
+ Enum representing the type of staged resource.
66
+ The resource_type column is a string in the DB, this is just for
67
+ application-level use.
68
+ """
69
+
70
+ # Note: If you add a new resource type, make sure to update either
71
+ # get_datastore_resource_types or get_website_monitor_resource_types
72
+
73
+ # Datastore staged resources
74
+ DATABASE = "Database"
75
+ SCHEMA = "Schema"
76
+ TABLE = "Table"
77
+ FIELD = "Field"
78
+ ENDPOINT = "Endpoint"
79
+ # Website monitor staged resources
80
+ COOKIE = "Cookie"
81
+ BROWSER_REQUEST = "Browser request"
82
+ IMAGE_BROWSER_REQUEST = "Image"
83
+ IFRAME_BROWSER_REQUEST = "iFrame"
84
+ JAVASCRIPT_BROWSER_REQUEST = "Javascript tag"
85
+
86
+ @staticmethod
87
+ def get_datastore_resource_types() -> List["StagedResourceType"]:
88
+ return [
89
+ StagedResourceType.DATABASE,
90
+ StagedResourceType.SCHEMA,
91
+ StagedResourceType.TABLE,
92
+ StagedResourceType.FIELD,
93
+ StagedResourceType.ENDPOINT,
94
+ ]
95
+
96
+ @staticmethod
97
+ def get_website_monitor_resource_types() -> List["StagedResourceType"]:
98
+ return [
99
+ StagedResourceType.COOKIE,
100
+ StagedResourceType.BROWSER_REQUEST,
101
+ StagedResourceType.IMAGE_BROWSER_REQUEST,
102
+ StagedResourceType.IFRAME_BROWSER_REQUEST,
103
+ StagedResourceType.JAVASCRIPT_BROWSER_REQUEST,
104
+ ]
105
+
106
+ def is_datastore_resource(self) -> bool:
107
+ return self in self.get_datastore_resource_types()
108
+
109
+ def is_website_monitor_resource(self) -> bool:
110
+ return self in self.get_website_monitor_resource_types()
111
+
112
+
63
113
  class SharedMonitorConfig(Base, FidesBase):
64
114
  """SQL model for shareable monitor configurations"""
65
115
 
@@ -499,11 +549,15 @@ class StagedResource(Base):
499
549
  server_default="{}",
500
550
  default=dict,
501
551
  )
552
+ # This field is intentionally nullable to distinguish the case when no user assigned data uses
553
+ # have been set (default, value is None) from the case in which they have been explicitly set
554
+ # as empty (value is empty array). This allows users to remove all data uses from a StagedResource,
555
+ # which was not possible when this field was not nullable.
502
556
  user_assigned_data_uses = Column(
503
557
  ARRAY(String),
504
- nullable=False,
505
- server_default="{}",
506
- default=dict,
558
+ nullable=True,
559
+ server_default=None,
560
+ default=None,
507
561
  )
508
562
  user_assigned_system_id = Column(String, nullable=True, index=True)
509
563
 
@@ -5,10 +5,12 @@ from fides.api.models.digest.conditional_dependencies import (
5
5
  DigestConditionType,
6
6
  )
7
7
  from fides.api.models.digest.digest_config import DigestConfig, DigestType
8
+ from fides.api.models.digest.digest_execution import DigestTaskExecution
8
9
 
9
10
  __all__ = [
10
11
  "DigestConfig",
11
12
  "DigestType",
12
13
  "DigestCondition",
13
14
  "DigestConditionType",
15
+ "DigestTaskExecution",
14
16
  ]
@@ -1,5 +1,5 @@
1
1
  from enum import Enum
2
- from typing import Optional, Union
2
+ from typing import TYPE_CHECKING, Optional, Union
3
3
 
4
4
  from sqlalchemy import Boolean, Column, DateTime, String, Text
5
5
  from sqlalchemy.dialects.postgresql import JSONB
@@ -19,6 +19,9 @@ from fides.api.task.conditional_dependencies.schemas import (
19
19
  ConditionLeaf,
20
20
  )
21
21
 
22
+ if TYPE_CHECKING:
23
+ from fides.api.models.digest.digest_execution import DigestTaskExecution
24
+
22
25
 
23
26
  class DigestType(str, Enum):
24
27
  """Types of digests that can be configured."""
@@ -60,6 +63,12 @@ class DigestConfig(Base):
60
63
  back_populates="digest_config",
61
64
  cascade="all, delete-orphan",
62
65
  )
66
+ executions = relationship(
67
+ "DigestTaskExecution",
68
+ back_populates="digest_config",
69
+ cascade="all, delete-orphan",
70
+ order_by="DigestTaskExecution.created_at.desc()",
71
+ )
63
72
 
64
73
  def get_receiver_condition(
65
74
  self, db: Session