ethyca-fides 2.66.0rc1__py2.py3-none-any.whl → 2.66.1b1__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 (243) hide show
  1. {ethyca_fides-2.66.0rc1.dist-info → ethyca_fides-2.66.1b1.dist-info}/METADATA +1 -1
  2. {ethyca_fides-2.66.0rc1.dist-info → ethyca_fides-2.66.1b1.dist-info}/RECORD +235 -228
  3. fides/_version.py +3 -3
  4. fides/api/alembic/migrations/versions/d0031087eacb_create_manualtaskconditionaldependency_.py +106 -0
  5. fides/api/api/v1/endpoints/dataset_config_endpoints.py +13 -5
  6. fides/api/api/v1/endpoints/user_endpoints.py +83 -7
  7. fides/api/db/base.py +1 -0
  8. fides/api/graph/execution.py +30 -0
  9. fides/api/models/manual_task/__init__.py +2 -0
  10. fides/api/models/manual_task/conditional_dependency.py +144 -0
  11. fides/api/models/{manual_task.py → manual_task/manual_task.py} +10 -0
  12. fides/api/oauth/roles.py +2 -0
  13. fides/api/schemas/application_config.py +11 -1
  14. fides/api/service/connectors/base_connector.py +1 -0
  15. fides/api/service/connectors/bigquery_connector.py +67 -19
  16. fides/api/service/connectors/dynamodb_connector.py +2 -1
  17. fides/api/service/connectors/fides_connector.py +1 -0
  18. fides/api/service/connectors/http_connector.py +1 -0
  19. fides/api/service/connectors/manual_task_connector.py +1 -0
  20. fides/api/service/connectors/manual_webhook_connector.py +2 -1
  21. fides/api/service/connectors/mongodb_connector.py +1 -0
  22. fides/api/service/connectors/okta_connector.py +1 -0
  23. fides/api/service/connectors/query_configs/bigquery_query_config.py +45 -20
  24. fides/api/service/connectors/rds_mysql_connector.py +1 -0
  25. fides/api/service/connectors/rds_postgres_connector.py +1 -0
  26. fides/api/service/connectors/s3_connector.py +1 -0
  27. fides/api/service/connectors/saas_connector.py +1 -0
  28. fides/api/service/connectors/scylla_connector.py +1 -0
  29. fides/api/service/connectors/sql_connector.py +36 -4
  30. fides/api/service/connectors/website_connector.py +1 -0
  31. fides/api/task/conditional_dependencies/__init__.py +0 -0
  32. fides/api/task/conditional_dependencies/evaluator.py +109 -0
  33. fides/api/task/conditional_dependencies/schemas.py +54 -0
  34. fides/api/task/deprecated_graph_task.py +24 -6
  35. fides/api/task/execute_request_tasks.py +88 -11
  36. fides/api/task/graph_task.py +11 -0
  37. fides/api/task/manual/manual_task_graph_task.py +1 -0
  38. fides/common/api/scope_registry.py +3 -0
  39. fides/config/utils.py +1 -0
  40. fides/ui-build/static/admin/404.html +1 -1
  41. fides/ui-build/static/admin/_next/static/cUz9aQNEfv77_K6F0m_Ja/_buildManifest.js +1 -0
  42. fides/ui-build/static/admin/_next/static/chunks/431-0e77abba7e220e31.js +1 -0
  43. fides/ui-build/static/admin/_next/static/chunks/6780-4b687168dd8daa84.js +1 -0
  44. fides/ui-build/static/admin/_next/static/chunks/8237-55049f8f5fd5e058.js +1 -0
  45. fides/ui-build/static/admin/_next/static/chunks/pages/{_app-8b3c5d3146ed7692.js → _app-a152f52351c84ef4.js} +56 -56
  46. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-031d7589a07fde30.js +1 -0
  47. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-efcbfd52186591bc.js +1 -0
  48. fides/ui-build/static/admin/_next/static/chunks/pages/{integrations-e2d5d7e2a5265e68.js → integrations-0f91eae539d47163.js} +1 -1
  49. fides/ui-build/static/admin/_next/static/chunks/pages/poc/{table-migration-69ad86b7a8a9a115.js → table-migration-8bfde56c86128ec3.js} +1 -1
  50. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-444ec9b3fec6e428.js +1 -0
  51. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-7eb7d26fa4b5cf17.js +1 -0
  52. fides/ui-build/static/admin/_next/static/css/{5bfb2473e5701527.css → 23cf870196941c9a.css} +1 -1
  53. fides/ui-build/static/admin/_next/static/css/b81194f2c3930152.css +1 -0
  54. fides/ui-build/static/admin/add-systems/manual.html +1 -1
  55. fides/ui-build/static/admin/add-systems/multiple.html +1 -1
  56. fides/ui-build/static/admin/add-systems.html +1 -1
  57. fides/ui-build/static/admin/consent/configure/add-vendors.html +1 -1
  58. fides/ui-build/static/admin/consent/configure.html +1 -1
  59. fides/ui-build/static/admin/consent/privacy-experience/[id].html +1 -1
  60. fides/ui-build/static/admin/consent/privacy-experience/new.html +1 -1
  61. fides/ui-build/static/admin/consent/privacy-experience.html +1 -1
  62. fides/ui-build/static/admin/consent/privacy-notices/[id].html +1 -1
  63. fides/ui-build/static/admin/consent/privacy-notices/new.html +1 -1
  64. fides/ui-build/static/admin/consent/privacy-notices.html +1 -1
  65. fides/ui-build/static/admin/consent/properties.html +1 -1
  66. fides/ui-build/static/admin/consent/reporting.html +1 -1
  67. fides/ui-build/static/admin/consent.html +1 -1
  68. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn].html +1 -1
  69. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn].html +1 -1
  70. fides/ui-build/static/admin/data-catalog/[systemId]/projects.html +1 -1
  71. fides/ui-build/static/admin/data-catalog/[systemId]/resources/[resourceUrn].html +1 -1
  72. fides/ui-build/static/admin/data-catalog/[systemId]/resources.html +1 -1
  73. fides/ui-build/static/admin/data-catalog.html +1 -1
  74. fides/ui-build/static/admin/data-discovery/action-center/[monitorId]/[systemId].html +1 -1
  75. fides/ui-build/static/admin/data-discovery/action-center/[monitorId].html +1 -1
  76. fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
  77. fides/ui-build/static/admin/data-discovery/activity.html +1 -1
  78. fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
  79. fides/ui-build/static/admin/data-discovery/detection.html +1 -1
  80. fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
  81. fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
  82. fides/ui-build/static/admin/datamap.html +1 -1
  83. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
  84. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
  85. fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
  86. fides/ui-build/static/admin/dataset/new.html +1 -1
  87. fides/ui-build/static/admin/dataset.html +1 -1
  88. fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
  89. fides/ui-build/static/admin/datastore-connection/new.html +1 -1
  90. fides/ui-build/static/admin/datastore-connection.html +1 -1
  91. fides/ui-build/static/admin/index.html +1 -1
  92. fides/ui-build/static/admin/integrations/[id].html +1 -1
  93. fides/ui-build/static/admin/integrations.html +1 -1
  94. fides/ui-build/static/admin/lib/fides-ext-gpp.js +1 -1
  95. fides/ui-build/static/admin/lib/fides-headless.js +1 -1
  96. fides/ui-build/static/admin/lib/fides-preview.js +1 -1
  97. fides/ui-build/static/admin/lib/fides-tcf.js +3 -3
  98. fides/ui-build/static/admin/lib/fides.js +3 -3
  99. fides/ui-build/static/admin/login/[provider].html +1 -1
  100. fides/ui-build/static/admin/login.html +1 -1
  101. fides/ui-build/static/admin/messaging/[id].html +1 -1
  102. fides/ui-build/static/admin/messaging/add-template.html +1 -1
  103. fides/ui-build/static/admin/messaging.html +1 -1
  104. fides/ui-build/static/admin/poc/ant-components.html +1 -1
  105. fides/ui-build/static/admin/poc/form-experiments/AntForm.html +1 -1
  106. fides/ui-build/static/admin/poc/form-experiments/FormikAntFormItem.html +1 -1
  107. fides/ui-build/static/admin/poc/form-experiments/FormikControlled.html +1 -1
  108. fides/ui-build/static/admin/poc/form-experiments/FormikField.html +1 -1
  109. fides/ui-build/static/admin/poc/form-experiments/FormikSpreadField.html +1 -1
  110. fides/ui-build/static/admin/poc/forms.html +1 -1
  111. fides/ui-build/static/admin/poc/table-migration.html +1 -1
  112. fides/ui-build/static/admin/privacy-requests/[id].html +1 -1
  113. fides/ui-build/static/admin/privacy-requests/configure/messaging.html +1 -1
  114. fides/ui-build/static/admin/privacy-requests/configure/storage.html +1 -1
  115. fides/ui-build/static/admin/privacy-requests/configure.html +1 -1
  116. fides/ui-build/static/admin/privacy-requests.html +1 -1
  117. fides/ui-build/static/admin/properties/[id].html +1 -1
  118. fides/ui-build/static/admin/properties/add-property.html +1 -1
  119. fides/ui-build/static/admin/properties.html +1 -1
  120. fides/ui-build/static/admin/reporting/datamap.html +1 -1
  121. fides/ui-build/static/admin/settings/about/alpha.html +1 -1
  122. fides/ui-build/static/admin/settings/about.html +1 -1
  123. fides/ui-build/static/admin/settings/consent/[configuration_id]/[purpose_id].html +1 -1
  124. fides/ui-build/static/admin/settings/consent.html +1 -1
  125. fides/ui-build/static/admin/settings/custom-fields.html +1 -1
  126. fides/ui-build/static/admin/settings/domain-records.html +1 -1
  127. fides/ui-build/static/admin/settings/domains.html +1 -1
  128. fides/ui-build/static/admin/settings/email-templates.html +1 -1
  129. fides/ui-build/static/admin/settings/locations.html +1 -1
  130. fides/ui-build/static/admin/settings/organization.html +1 -1
  131. fides/ui-build/static/admin/settings/regulations.html +1 -1
  132. fides/ui-build/static/admin/systems/configure/[id]/test-datasets.html +1 -1
  133. fides/ui-build/static/admin/systems/configure/[id].html +1 -1
  134. fides/ui-build/static/admin/systems.html +1 -1
  135. fides/ui-build/static/admin/taxonomy.html +1 -1
  136. fides/ui-build/static/admin/user-management/new.html +1 -1
  137. fides/ui-build/static/admin/user-management/profile/[id].html +1 -1
  138. fides/ui-build/static/admin/user-management.html +1 -1
  139. fides/ui-build/static/admin/_next/static/chunks/431-34f0b91c26f8d9ab.js +0 -1
  140. fides/ui-build/static/admin/_next/static/chunks/6780-5bd185892f6af46e.js +0 -1
  141. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-2265ecb899d45fbc.js +0 -1
  142. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-8ab33a1e0272df1f.js +0 -1
  143. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-32600543eb7b584f.js +0 -1
  144. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-cbe4c8f9096b6543.js +0 -1
  145. fides/ui-build/static/admin/_next/static/css/2cadb5f62dcd7c2b.css +0 -1
  146. fides/ui-build/static/admin/_next/static/t-DtndYMbhjqqd1SOdd0Y/_buildManifest.js +0 -1
  147. {ethyca_fides-2.66.0rc1.dist-info → ethyca_fides-2.66.1b1.dist-info}/WHEEL +0 -0
  148. {ethyca_fides-2.66.0rc1.dist-info → ethyca_fides-2.66.1b1.dist-info}/entry_points.txt +0 -0
  149. {ethyca_fides-2.66.0rc1.dist-info → ethyca_fides-2.66.1b1.dist-info}/licenses/LICENSE +0 -0
  150. {ethyca_fides-2.66.0rc1.dist-info → ethyca_fides-2.66.1b1.dist-info}/top_level.txt +0 -0
  151. /fides/ui-build/static/admin/_next/static/{t-DtndYMbhjqqd1SOdd0Y → cUz9aQNEfv77_K6F0m_Ja}/_ssgManifest.js +0 -0
  152. /fides/ui-build/static/admin/_next/static/chunks/{1817-e601e737e3cc7a0e.js → 1817-10f6ab6da28df4d2.js} +0 -0
  153. /fides/ui-build/static/admin/_next/static/chunks/{203-5a663f465ba26bb4.js → 203-d9fe5384a6e94799.js} +0 -0
  154. /fides/ui-build/static/admin/_next/static/chunks/{2921-455e6357b74d2f76.js → 2921-86f1547ac40a5cdf.js} +0 -0
  155. /fides/ui-build/static/admin/_next/static/chunks/{3450-0ba194991d0cca88.js → 3450-24041e1b2a846739.js} +0 -0
  156. /fides/ui-build/static/admin/_next/static/chunks/{3855-e172870d3e21b0dd.js → 3855-e2fa6db53d32c3de.js} +0 -0
  157. /fides/ui-build/static/admin/_next/static/chunks/{3872-46cebf7ec1b31a2b.js → 3872-b97dd7695d6532bc.js} +0 -0
  158. /fides/ui-build/static/admin/_next/static/chunks/{3923-6cc911dafccc5f63.js → 3923-13a6b4da2d51bf8f.js} +0 -0
  159. /fides/ui-build/static/admin/_next/static/chunks/{401-1b529d5800aa1f3a.js → 401-3cc1fee61494e3bd.js} +0 -0
  160. /fides/ui-build/static/admin/_next/static/chunks/{409-a257e14acebcd73b.js → 409-45a125437261580c.js} +0 -0
  161. /fides/ui-build/static/admin/_next/static/chunks/{4121-2bc09fc4ddbfe5cb.js → 4121-c4747258599705d0.js} +0 -0
  162. /fides/ui-build/static/admin/_next/static/chunks/{4230-60100f7ef3ddcde1.js → 4230-840c287045c88b34.js} +0 -0
  163. /fides/ui-build/static/admin/_next/static/chunks/{4608-bbb7bf511a05c3c2.js → 4608-b2d8a76e3a680db4.js} +0 -0
  164. /fides/ui-build/static/admin/_next/static/chunks/{5309-d73339f062763fed.js → 5309-67bdf9001531e972.js} +0 -0
  165. /fides/ui-build/static/admin/_next/static/chunks/{5574-b13021775a15bfd2.js → 5574-0b7c88e3f81b08fb.js} +0 -0
  166. /fides/ui-build/static/admin/_next/static/chunks/{6084-7178ff6ea6822475.js → 6084-7bc3756bd23c3a4c.js} +0 -0
  167. /fides/ui-build/static/admin/_next/static/chunks/{6662-507be5d46e5b719b.js → 6662-8c43dd2b75e0264c.js} +0 -0
  168. /fides/ui-build/static/admin/_next/static/chunks/{6853-2ad3e08fe6f9f5f2.js → 6853-4f8bf6558f8c6a46.js} +0 -0
  169. /fides/ui-build/static/admin/_next/static/chunks/{6882-6af16fef26c21e06.js → 6882-0fa957731918160e.js} +0 -0
  170. /fides/ui-build/static/admin/_next/static/chunks/{6954-bb875d9ac89f6030.js → 6954-d30c6dff440d46b0.js} +0 -0
  171. /fides/ui-build/static/admin/_next/static/chunks/{7476-281ee9a8286556f3.js → 7476-2d2c80dfe64c0f90.js} +0 -0
  172. /fides/ui-build/static/admin/_next/static/chunks/{7630-9aac73191ed5ed13.js → 7630-06f4207597225cbb.js} +0 -0
  173. /fides/ui-build/static/admin/_next/static/chunks/{787-fb41002f797eb2df.js → 787-32ca132be974eaf9.js} +0 -0
  174. /fides/ui-build/static/admin/_next/static/chunks/{79-7e87aff851423d4a.js → 79-056290dba15480c2.js} +0 -0
  175. /fides/ui-build/static/admin/_next/static/chunks/{796-329a5f823ec258a5.js → 796-00487f9036709b7b.js} +0 -0
  176. /fides/ui-build/static/admin/_next/static/chunks/{9046-ce9567c9074563e2.js → 9046-54877976a0529de2.js} +0 -0
  177. /fides/ui-build/static/admin/_next/static/chunks/{9226-746771d47dff6266.js → 9226-081cb18771536d64.js} +0 -0
  178. /fides/ui-build/static/admin/_next/static/chunks/{9826-111aaee8bd8dbd09.js → 9826-e77d5e5b8458fe77.js} +0 -0
  179. /fides/ui-build/static/admin/_next/static/chunks/{9951-9b753ad7c3f51bdf.js → 9951-595d0f1588215081.js} +0 -0
  180. /fides/ui-build/static/admin/_next/static/chunks/pages/{404-aece2c920ea14514.js → 404-2d803dab6a00f353.js} +0 -0
  181. /fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-98777246bec9dc2a.js → manual-a539d34e7c9ab0ee.js} +0 -0
  182. /fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{multiple-dc75dc6e37e52f05.js → multiple-fa0c254854fbf79c.js} +0 -0
  183. /fides/ui-build/static/admin/_next/static/chunks/pages/{add-systems-a71c0aff4e0e6535.js → add-systems-9c86d99f81d128ac.js} +0 -0
  184. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/{add-vendors-24d226b5a8de5c74.js → add-vendors-3a77a92d2a94426a.js} +0 -0
  185. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{configure-6a8ef51138ac926a.js → configure-886518c906a28271.js} +0 -0
  186. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{[id]-1edf582ba3cd3bbb.js → [id]-fc3a011154a2e1de.js} +0 -0
  187. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-experience-685771e5f7196d87.js → privacy-experience-a333f503aa057e06.js} +0 -0
  188. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-6ccedc70dc447089.js → [id]-7a1c8073262ff0b4.js} +0 -0
  189. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-944bca1cc57985b5.js → new-39ef289b3c88ebd4.js} +0 -0
  190. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-notices-84f4bd14ce8673bc.js → privacy-notices-deeb59b8332261c1.js} +0 -0
  191. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{properties-6f86ab63a08a6528.js → properties-057cad65e7414a44.js} +0 -0
  192. /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{reporting-afdbd4665657cfa1.js → reporting-8f891957c8944137.js} +0 -0
  193. /fides/ui-build/static/admin/_next/static/chunks/pages/{consent-73d3cbf68f7c3a31.js → consent-7507764f89f9d9c8.js} +0 -0
  194. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/{[projectUrn]-6ba9e160dae64695.js → [projectUrn]-6020cd361c57b387.js} +0 -0
  195. /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{projects-32eac8bbd217615a.js → projects-2fc76749940a06fd.js} +0 -0
  196. /fides/ui-build/static/admin/_next/static/chunks/pages/{data-catalog-6f630d42ac9fb6b4.js → data-catalog-7d1c53686a9a5d73.js} +0 -0
  197. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{action-center-9ddb52ebb7ac4c71.js → action-center-94acf9cc27209714.js} +0 -0
  198. /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{activity-9aa744d56cdacb0d.js → activity-5af9381f02b2aff6.js} +0 -0
  199. /fides/ui-build/static/admin/_next/static/chunks/pages/{datamap-7674b97d655c193b.js → datamap-e48f4ef032791c1d.js} +0 -0
  200. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/{[...subfieldNames]-c0d2bfd465df20e0.js → [...subfieldNames]-24f87303adb9428e.js} +0 -0
  201. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/{[collectionName]-28280a8a39a6e37c.js → [collectionName]-41aa4e51bd73d13e.js} +0 -0
  202. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{[datasetId]-006b695e5af5ef24.js → [datasetId]-b296323ed0922fd5.js} +0 -0
  203. /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{new-82fb246d87e58ebd.js → new-cbb409d1bc89d10b.js} +0 -0
  204. /fides/ui-build/static/admin/_next/static/chunks/pages/{dataset-20165c31ab1bc7cf.js → dataset-07df9c4896721e9f.js} +0 -0
  205. /fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-b4a6bcc87d126840.js → [id]-0a60fe49678a3ece.js} +0 -0
  206. /fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{new-f95d7b0bbfc58f5a.js → new-c903ab62611245d8.js} +0 -0
  207. /fides/ui-build/static/admin/_next/static/chunks/pages/{datastore-connection-c391c6fad56eec48.js → datastore-connection-33b01e2ab2d5ce7e.js} +0 -0
  208. /fides/ui-build/static/admin/_next/static/chunks/pages/{index-1919aab9e5834b51.js → index-ca1c9d4de5abec7a.js} +0 -0
  209. /fides/ui-build/static/admin/_next/static/chunks/pages/integrations/{[id]-eacabe4a80cb8813.js → [id]-2f9efedecc4b9273.js} +0 -0
  210. /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{[id]-53fecfb9dd6a1e0c.js → [id]-8b20a058d18181b9.js} +0 -0
  211. /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{add-template-76b01cec5fde10a9.js → add-template-c7054125338aee6c.js} +0 -0
  212. /fides/ui-build/static/admin/_next/static/chunks/pages/{messaging-5094ffea13f32ed9.js → messaging-a4fdfa9047bb5770.js} +0 -0
  213. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{ant-components-5c08e8447c45ce44.js → ant-components-64a322d01aae5ca7.js} +0 -0
  214. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{AntForm-06ad5f34585480aa.js → AntForm-8bca16a7726e7eb2.js} +0 -0
  215. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikAntFormItem-6f071c2bc9446cb0.js → FormikAntFormItem-b0f246fc3b67ebf7.js} +0 -0
  216. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikControlled-efcc38c58991ac9e.js → FormikControlled-1a0852b090bfc392.js} +0 -0
  217. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/form-experiments/{FormikField-430ba5c979abfb7c.js → FormikField-11f3de1b45e36583.js} +0 -0
  218. /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{forms-5c561880bf131afb.js → forms-1b73a1c2b6c6285f.js} +0 -0
  219. /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{messaging-10ce53ea356f8bad.js → messaging-08a6d60bb9230d5f.js} +0 -0
  220. /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{storage-5501bbb129fee9c4.js → storage-29d47f1bdd08c45d.js} +0 -0
  221. /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{configure-d888a69a3bbe040e.js → configure-2806f3458fe85897.js} +0 -0
  222. /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{[id]-d3d8e3d7583ec635.js → [id]-568620fc7e239e7b.js} +0 -0
  223. /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{add-property-1af10ed303815d46.js → add-property-394043fd823d69f5.js} +0 -0
  224. /fides/ui-build/static/admin/_next/static/chunks/pages/{properties-cebc0dc186be499a.js → properties-e959378bb32b6b73.js} +0 -0
  225. /fides/ui-build/static/admin/_next/static/chunks/pages/reporting/{datamap-e130c0197362e8f3.js → datamap-2a98bd257edd8f47.js} +0 -0
  226. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/{alpha-5e1322de868d615e.js → alpha-92d7f12a95a1adc5.js} +0 -0
  227. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{about-241f95e372b65d0f.js → about-70fb68b7519d5222.js} +0 -0
  228. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/{[purpose_id]-fc201657f4a782c7.js → [purpose_id]-668d74c041d74650.js} +0 -0
  229. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{consent-c2d39cba8396ef3a.js → consent-7c8bda96813441c5.js} +0 -0
  230. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{custom-fields-d992103cc55901ae.js → custom-fields-b773691516488006.js} +0 -0
  231. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-41242f805599feda.js → domain-records-21f14d9141a5ad8a.js} +0 -0
  232. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domains-2e885f74c92f669c.js → domains-3022368d5438dfb7.js} +0 -0
  233. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{email-templates-ff112655ad5f41e5.js → email-templates-89903d6f579b1aeb.js} +0 -0
  234. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-023e1895552817de.js → locations-14621f32ad4f9120.js} +0 -0
  235. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-ac403c0886b20e20.js → organization-5106ff9a9403ee92.js} +0 -0
  236. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{regulations-86062a18e081a52a.js → regulations-0927740524f79b02.js} +0 -0
  237. /fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-7a3396ac819c7904.js → test-datasets-47284d49803f8b3b.js} +0 -0
  238. /fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-8314a819837f5b2a.js → [id]-7bc2d6b23d5bb71f.js} +0 -0
  239. /fides/ui-build/static/admin/_next/static/chunks/pages/{systems-21f423a7c417aa9d.js → systems-c3a536901d6b445d.js} +0 -0
  240. /fides/ui-build/static/admin/_next/static/chunks/pages/{taxonomy-6387fcc8cce872eb.js → taxonomy-ab4f4d260c01163b.js} +0 -0
  241. /fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/{[id]-ff5738706da07801.js → [id]-79fe68f1eb3add49.js} +0 -0
  242. /fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-173ac3a1ed2b05a6.js → user-management-6111bb65a2607dc5.js} +0 -0
  243. /fides/ui-build/static/admin/_next/static/chunks/{webpack-ff0cd6bff75588da.js → webpack-ff4c22c9f0840531.js} +0 -0
@@ -1,13 +1,8 @@
1
- from typing import List, Optional
1
+ from typing import Any, Dict, List, Optional
2
2
 
3
3
  from loguru import logger
4
4
  from sqlalchemy import text
5
- from sqlalchemy.engine import ( # type: ignore
6
- Connection,
7
- Engine,
8
- LegacyCursorResult,
9
- create_engine,
10
- )
5
+ from sqlalchemy.engine import Connection, Engine, create_engine # type: ignore
11
6
  from sqlalchemy.orm import Session
12
7
  from sqlalchemy.sql import Executable # type: ignore
13
8
  from sqlalchemy.sql.elements import TextClause
@@ -17,6 +12,7 @@ from fides.api.graph.execution import ExecutionNode
17
12
  from fides.api.models.connectionconfig import ConnectionTestStatus
18
13
  from fides.api.models.policy import Policy
19
14
  from fides.api.models.privacy_request import PrivacyRequest, RequestTask
15
+ from fides.api.schemas.application_config import SqlDryRunMode
20
16
  from fides.api.schemas.connection_configuration.connection_secrets_bigquery import (
21
17
  BigQuerySchema,
22
18
  )
@@ -99,6 +95,16 @@ class BigQueryConnector(SQLConnector):
99
95
  logger.info(
100
96
  f"Executing {len(partition_clauses)} partition queries for node '{query_config.node.address}' in DSR execution"
101
97
  )
98
+
99
+ if self.should_dry_run(SqlDryRunMode.access):
100
+ for partition_clause in partition_clauses:
101
+ existing_bind_params = stmt.compile().params
102
+ partitioned_stmt = text(
103
+ f"{stmt} AND ({text(partition_clause)})"
104
+ ).params(existing_bind_params)
105
+ logger.warning(f"SQL DRY RUN - Would execute SQL: {partitioned_stmt}")
106
+ return []
107
+
102
108
  rows = []
103
109
  for partition_clause in partition_clauses:
104
110
  logger.debug(
@@ -135,6 +141,39 @@ class BigQueryConnector(SQLConnector):
135
141
  logger.exception(f"Error testing connection to remote BigQuery {str(e)}")
136
142
  raise ConnectionException(f"Connection error: {e}")
137
143
 
144
+ def _execute_statements_with_sql_dry_run(
145
+ self,
146
+ statements: List[Executable],
147
+ sql_dry_run_enabled: bool,
148
+ client: Engine,
149
+ ) -> int:
150
+ """
151
+ Execute SQL statements with sql_dry_run support.
152
+
153
+ Args:
154
+ statements: List of SQL statements to execute
155
+ sql_dry_run_enabled: Whether sql_dry_run mode is enabled
156
+ client: Database engine
157
+
158
+ Returns:
159
+ int: Number of affected rows (0 in sql_dry_run mode)
160
+ """
161
+ if not statements:
162
+ return 0
163
+
164
+ if sql_dry_run_enabled:
165
+ for stmt in statements:
166
+ logger.warning(f"SQL DRY RUN - Would execute SQL: {stmt}")
167
+ return 0
168
+
169
+ row_count = 0
170
+ with client.connect() as connection:
171
+ for stmt in statements:
172
+ results = connection.execute(stmt)
173
+ logger.debug(f"Affected {results.rowcount} rows")
174
+ row_count += results.rowcount
175
+ return row_count
176
+
138
177
  def mask_data(
139
178
  self,
140
179
  node: ExecutionNode,
@@ -142,22 +181,31 @@ class BigQueryConnector(SQLConnector):
142
181
  privacy_request: PrivacyRequest,
143
182
  request_task: RequestTask,
144
183
  rows: List[Row],
184
+ input_data: Optional[Dict[str, List[Any]]] = None,
145
185
  ) -> int:
146
186
  """Execute a masking request. Returns the number of records updated or deleted"""
187
+
147
188
  query_config = self.query_config(node)
148
189
  update_or_delete_ct = 0
149
190
  client = self.client()
150
- for row in rows:
151
- update_or_delete_stmts: List[Executable] = (
152
- query_config.generate_masking_stmt(
153
- node, row, policy, privacy_request, client
154
- )
191
+
192
+ if query_config.uses_delete_masking_strategy():
193
+ delete_stmts = query_config.generate_delete(client, input_data or {})
194
+ logger.debug(f"Generated {len(delete_stmts)} DELETE statements")
195
+ update_or_delete_ct += self._execute_statements_with_sql_dry_run(
196
+ delete_stmts, self.should_dry_run(SqlDryRunMode.erasure), client
155
197
  )
156
- if update_or_delete_stmts:
157
- with client.connect() as connection:
158
- for update_or_delete_stmt in update_or_delete_stmts:
159
- results: LegacyCursorResult = connection.execute(
160
- update_or_delete_stmt
161
- )
162
- update_or_delete_ct = update_or_delete_ct + results.rowcount
198
+ else:
199
+ for row in rows:
200
+ update_or_delete_stmts: List[Executable] = query_config.generate_update(
201
+ row, policy, privacy_request, client
202
+ )
203
+ logger.debug(
204
+ f"Generated {len(update_or_delete_stmts)} UPDATE statements"
205
+ )
206
+ update_or_delete_ct += self._execute_statements_with_sql_dry_run(
207
+ update_or_delete_stmts,
208
+ self.should_dry_run(SqlDryRunMode.erasure),
209
+ client,
210
+ )
163
211
  return update_or_delete_ct
@@ -140,8 +140,9 @@ class DynamoDBConnector(BaseConnector[Any]): # type: ignore
140
140
  privacy_request: PrivacyRequest,
141
141
  request_task: RequestTask,
142
142
  rows: List[Row],
143
+ input_data: Optional[Dict[str, List[Any]]] = None,
143
144
  ) -> int:
144
- """Execute a masking requestfor DynamoDB"""
145
+ """Execute a masking request for DynamoDB"""
145
146
 
146
147
  query_config = self.query_config(node)
147
148
  collection_name = node.address.collection
@@ -142,6 +142,7 @@ class FidesConnector(BaseConnector[FidesClient]):
142
142
  privacy_request: PrivacyRequest,
143
143
  request_task: RequestTask,
144
144
  rows: List[Row],
145
+ input_data: Optional[Dict[str, List[Any]]] = None,
145
146
  ) -> int:
146
147
  """Execute an erasure request on remote fides"""
147
148
  identity_data = {
@@ -90,6 +90,7 @@ class HTTPSConnector(BaseConnector[None]):
90
90
  privacy_request: PrivacyRequest,
91
91
  request_task: RequestTask,
92
92
  rows: List[Row],
93
+ input_data: Optional[Dict[str, List[Any]]] = None,
93
94
  ) -> int:
94
95
  """Currently not supported as webhooks are not called at the collection level"""
95
96
  raise NotImplementedError(
@@ -80,6 +80,7 @@ class ManualTaskConnector(BaseConnector):
80
80
  privacy_request: PrivacyRequest,
81
81
  request_task: RequestTask,
82
82
  rows: List[Row],
83
+ input_data: Optional[Dict[str, List[Any]]] = None,
83
84
  ) -> int:
84
85
  """
85
86
  Manual tasks don't support erasure operations.
@@ -1,4 +1,4 @@
1
- from typing import Any, Dict, List
1
+ from typing import Any, Dict, List, Optional
2
2
 
3
3
  from fides.api.graph.execution import ExecutionNode
4
4
  from fides.api.models.connectionconfig import ConnectionConfig, ConnectionTestStatus
@@ -53,6 +53,7 @@ class ManualWebhookConnector(BaseConnector[None]):
53
53
  privacy_request: PrivacyRequest,
54
54
  request_task: RequestTask,
55
55
  rows: List[Row],
56
+ input_data: Optional[List[List[Row]]] = None,
56
57
  ) -> None:
57
58
  """
58
59
  Not applicable for a manual webhook. Manual webhooks are not called as part of the traversal.
@@ -126,6 +126,7 @@ class MongoDBConnector(BaseConnector[MongoClient]):
126
126
  privacy_request: PrivacyRequest,
127
127
  request_task: RequestTask,
128
128
  rows: List[Row],
129
+ input_data: Optional[Dict[str, List[Any]]] = None,
129
130
  ) -> int:
130
131
  """Execute a masking request"""
131
132
  query_config = self.query_config(node)
@@ -82,6 +82,7 @@ class OktaConnector(BaseConnector):
82
82
  privacy_request: PrivacyRequest,
83
83
  request_task: RequestTask,
84
84
  rows: List[Row],
85
+ input_data: Optional[Dict[str, List[Any]]] = None,
85
86
  ) -> int:
86
87
  """DSR execution not supported for Okta connector"""
87
88
  return 0
@@ -3,7 +3,7 @@ from typing import Any, Dict, List, Optional, Union, cast
3
3
  import pydash
4
4
  from fideslang.models import MaskingStrategies
5
5
  from loguru import logger
6
- from sqlalchemy import MetaData, Table, text
6
+ from sqlalchemy import MetaData, Table, or_, text
7
7
  from sqlalchemy.engine import Engine
8
8
  from sqlalchemy.sql import Delete, Update
9
9
  from sqlalchemy.sql.elements import ColumnElement, TextClause
@@ -125,6 +125,7 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
125
125
  policy: Policy,
126
126
  request: PrivacyRequest,
127
127
  client: Engine,
128
+ input_data: Optional[Dict[str, List[Any]]] = None,
128
129
  ) -> Union[List[Update], List[Delete]]:
129
130
  """
130
131
  Generate a masking statement for BigQuery.
@@ -137,7 +138,7 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
137
138
  logger.info(
138
139
  f"Masking override detected for collection {node.address.value}: {masking_override.strategy.value}"
139
140
  )
140
- return self.generate_delete(row, client)
141
+ return self.generate_delete(client, input_data or {})
141
142
  return self.generate_update(row, policy, request, client)
142
143
 
143
144
  def generate_update(
@@ -218,27 +219,30 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
218
219
 
219
220
  return [table.update().where(*where_clauses).values(**final_update_map)]
220
221
 
221
- def generate_delete(self, row: Row, client: Engine) -> List[Delete]:
222
- """Returns a List of SQLAlchemy DELETE statements for BigQuery. Does not actually execute the delete statement.
222
+ def generate_delete(
223
+ self,
224
+ client: Engine,
225
+ input_data: Optional[Dict[str, List[Any]]] = None,
226
+ ) -> List[Delete]:
227
+ """
228
+ Returns a List of SQLAlchemy DELETE statements for BigQuery. Does not actually execute the delete statement.
223
229
 
224
230
  Used when a collection-level masking override is present and the masking strategy is DELETE.
225
231
 
226
232
  A List of multiple DELETE statements are returned for partitioned tables; for a non-partitioned table,
227
233
  a single DELETE statement is returned in a List for consistent typing.
228
-
229
- TODO: DRY up this method and `generate_update` a bit
230
234
  """
231
235
 
232
- non_empty_reference_field_keys: Dict[str, Field] = filter_nonempty_values(
233
- {
234
- fpath.string_path: fld.cast(row[fpath.string_path])
235
- for fpath, fld in self.reference_field_paths.items()
236
- if fpath.string_path in row
237
- }
238
- )
236
+ if not input_data:
237
+ logger.warning(
238
+ "No input data provided for node '{}', skipping DELETE statement generation",
239
+ self.node.address,
240
+ )
241
+ return []
239
242
 
240
- valid = len(non_empty_reference_field_keys) > 0
241
- if not valid:
243
+ filtered_data = self.node.typed_filtered_values(input_data)
244
+
245
+ if not filtered_data:
242
246
  logger.warning(
243
247
  "There is not enough data to generate a valid DELETE statement for {}",
244
248
  self.node.address,
@@ -246,9 +250,17 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
246
250
  return []
247
251
 
248
252
  table = Table(self._generate_table_name(), MetaData(bind=client), autoload=True)
249
- where_clauses: List[ColumnElement] = [
250
- getattr(table.c, k) == v for k, v in non_empty_reference_field_keys.items()
251
- ]
253
+
254
+ # Build individual reference clauses
255
+ where_clauses: List[ColumnElement] = []
256
+ for column_name, values in filtered_data.items():
257
+ if len(values) == 1:
258
+ where_clauses.append(getattr(table.c, column_name) == values[0])
259
+ else:
260
+ where_clauses.append(getattr(table.c, column_name).in_(values))
261
+
262
+ # Combine reference clauses with OR instead of AND
263
+ combined_reference_clause = or_(*where_clauses)
252
264
 
253
265
  if self.partitioning:
254
266
  partition_clauses = self.get_partition_clauses()
@@ -259,12 +271,25 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
259
271
 
260
272
  for partition_clause in partition_clauses:
261
273
  partitioned_queries.append(
262
- table.delete().where(*(where_clauses + [text(partition_clause)]))
274
+ table.delete()
275
+ .where(combined_reference_clause)
276
+ .where(text(partition_clause))
263
277
  )
264
278
 
265
279
  return partitioned_queries
266
280
 
267
- return [table.delete().where(*where_clauses)]
281
+ return [table.delete().where(combined_reference_clause)]
282
+
283
+ def uses_delete_masking_strategy(self) -> bool:
284
+ """Check if this collection uses DELETE masking strategy.
285
+
286
+ Returns True if masking override is present and strategy is DELETE.
287
+ """
288
+ masking_override = self.node.collection.masking_strategy_override
289
+ return (
290
+ masking_override is not None
291
+ and masking_override.strategy == MaskingStrategies.DELETE
292
+ )
268
293
 
269
294
  def format_fields_for_query(
270
295
  self,
@@ -158,6 +158,7 @@ class RDSMySQLConnector(RDSConnectorMixin, SQLConnector):
158
158
  privacy_request: PrivacyRequest,
159
159
  request_task: RequestTask,
160
160
  rows: List[Row],
161
+ input_data: Optional[Dict[str, List[Any]]] = None,
161
162
  ) -> int:
162
163
  """DSR execution not yet supported for RDS MySQL"""
163
164
  return 0
@@ -147,6 +147,7 @@ class RDSPostgresConnector(RDSConnectorMixin, SQLConnector):
147
147
  privacy_request: PrivacyRequest,
148
148
  request_task: RequestTask,
149
149
  rows: List[Row],
150
+ input_data: Optional[Dict[str, List[Any]]] = None,
150
151
  ) -> int:
151
152
  """DSR execution not yet supported for RDS Postgres"""
152
153
  return 0
@@ -66,6 +66,7 @@ class S3Connector(BaseConnector):
66
66
  privacy_request: PrivacyRequest,
67
67
  request_task: RequestTask,
68
68
  rows: List[Row],
69
+ input_data: Optional[Dict[str, List[Any]]] = None,
69
70
  ) -> int:
70
71
  """DSR execution not yet supported for S3"""
71
72
  return 0
@@ -517,6 +517,7 @@ class SaaSConnector(BaseConnector[AuthenticatedClient], Contextualizable):
517
517
  privacy_request: PrivacyRequest,
518
518
  request_task: RequestTask,
519
519
  rows: List[Row],
520
+ input_data: Optional[Dict[str, List[Any]]] = None,
520
521
  ) -> int:
521
522
  """Execute a masking request. Return the number of rows that have been updated."""
522
523
  self.set_privacy_request_state(privacy_request, node, request_task)
@@ -150,6 +150,7 @@ class ScyllaConnector(BaseConnector[Cluster]):
150
150
  privacy_request: PrivacyRequest,
151
151
  request_task: RequestTask,
152
152
  rows: List[Row],
153
+ input_data: Optional[Dict[str, List[Any]]] = None,
153
154
  ) -> int:
154
155
  """Execute a masking request"""
155
156
  query_config = self.query_config(node)
@@ -27,11 +27,13 @@ from fides.api.graph.execution import ExecutionNode
27
27
  from fides.api.models.connectionconfig import ConnectionConfig, ConnectionTestStatus
28
28
  from fides.api.models.policy import Policy
29
29
  from fides.api.models.privacy_request import PrivacyRequest, RequestTask
30
+ from fides.api.schemas.application_config import SqlDryRunMode
30
31
  from fides.api.schemas.connection_configuration import ConnectionConfigSecretsSchema
31
32
  from fides.api.service.connectors.base_connector import BaseConnector
32
33
  from fides.api.service.connectors.query_configs.query_config import SQLQueryConfig
33
34
  from fides.api.util.collection_util import Row
34
35
  from fides.config import get_config
36
+ from fides.config.config_proxy import ConfigProxy
35
37
 
36
38
  from fides.api.models.sql_models import ( # type: ignore[attr-defined] # isort: skip
37
39
  Dataset as CtlDataset,
@@ -58,6 +60,23 @@ class SQLConnector(BaseConnector[Engine]):
58
60
  )
59
61
  self.ssh_server: sshtunnel._ForwardServer = None
60
62
 
63
+ def should_dry_run(self, mode_to_check: SqlDryRunMode) -> bool:
64
+ """
65
+ Check if SQL dry run is enabled for the specified mode.
66
+
67
+ Args:
68
+ mode_to_check: The SqlDryRunMode to check for
69
+
70
+ Returns:
71
+ bool: True if the current mode matches the mode to check
72
+ """
73
+ from fides.api.api.deps import get_autoclose_db_session as get_db
74
+
75
+ with get_db() as db:
76
+ config_proxy = ConfigProxy(db)
77
+ current_mode = getattr(config_proxy.execution, "sql_dry_run", None)
78
+ return current_mode == mode_to_check
79
+
61
80
  @staticmethod
62
81
  def cursor_result_to_rows(results: CursorResult) -> List[Row]:
63
82
  """Convert SQLAlchemy results to a list of dictionaries"""
@@ -140,6 +159,10 @@ class SQLConnector(BaseConnector[Engine]):
140
159
  if query is None:
141
160
  return []
142
161
 
162
+ if self.should_dry_run(SqlDryRunMode.access):
163
+ logger.warning(f"SQL DRY RUN - Would execute SQL: {query}")
164
+ return []
165
+
143
166
  with client.connect() as connection:
144
167
  self.set_schema(connection)
145
168
  results = connection.execute(query)
@@ -160,6 +183,10 @@ class SQLConnector(BaseConnector[Engine]):
160
183
  if stmt is None:
161
184
  return []
162
185
 
186
+ if self.should_dry_run(SqlDryRunMode.access):
187
+ logger.warning(f"SQL DRY RUN - Would execute SQL: {stmt}")
188
+ return []
189
+
163
190
  logger.info("Starting data retrieval for {}", node.address)
164
191
  with client.connect() as connection:
165
192
  self.set_schema(connection)
@@ -178,20 +205,25 @@ class SQLConnector(BaseConnector[Engine]):
178
205
  privacy_request: PrivacyRequest,
179
206
  request_task: RequestTask,
180
207
  rows: List[Row],
208
+ input_data: Optional[Dict[str, List[Any]]] = None,
181
209
  ) -> int:
182
210
  """Execute a masking request. Returns the number of records masked"""
183
211
  query_config = self.query_config(node)
184
212
  update_ct = 0
185
213
  client = self.client()
214
+
186
215
  for row in rows:
187
216
  update_stmt: Optional[TextClause] = query_config.generate_update_stmt(
188
217
  row, policy, privacy_request
189
218
  )
190
219
  if update_stmt is not None:
191
- with client.connect() as connection:
192
- self.set_schema(connection)
193
- results: LegacyCursorResult = connection.execute(update_stmt)
194
- update_ct = update_ct + results.rowcount
220
+ if self.should_dry_run(SqlDryRunMode.erasure):
221
+ logger.warning(f"SQL DRY RUN - Would execute SQL: {update_stmt}")
222
+ else:
223
+ with client.connect() as connection:
224
+ self.set_schema(connection)
225
+ results: LegacyCursorResult = connection.execute(update_stmt)
226
+ update_ct = update_ct + results.rowcount
195
227
  return update_ct
196
228
 
197
229
  def close(self) -> None:
@@ -75,6 +75,7 @@ class WebsiteConnector(BaseConnector):
75
75
  privacy_request: PrivacyRequest,
76
76
  request_task: RequestTask,
77
77
  rows: List[Row],
78
+ input_data: Optional[Dict[str, List[Any]]] = None,
78
79
  ) -> int:
79
80
  """DSR execution not supported for website connector"""
80
81
  return 0
File without changes
@@ -0,0 +1,109 @@
1
+ import operator as py_operator
2
+ from typing import Any, Union
3
+
4
+ from loguru import logger
5
+ from sqlalchemy.orm import Session
6
+
7
+ from fides.api.graph.config import FieldPath
8
+ from fides.api.task.conditional_dependencies.schemas import (
9
+ Condition,
10
+ ConditionGroup,
11
+ ConditionLeaf,
12
+ GroupOperator,
13
+ Operator,
14
+ )
15
+
16
+ operator_methods = {
17
+ Operator.exists: lambda a, _: a is not None,
18
+ Operator.not_exists: lambda a, _: a is None,
19
+ Operator.eq: py_operator.eq,
20
+ Operator.neq: py_operator.ne,
21
+ Operator.lt: lambda a, b: a < b if a is not None else False,
22
+ Operator.lte: lambda a, b: a <= b if a is not None else False,
23
+ Operator.gt: lambda a, b: a > b if a is not None else False,
24
+ Operator.gte: lambda a, b: a >= b if a is not None else False,
25
+ Operator.list_contains: lambda a, b: b in a if isinstance(a, list) else False,
26
+ Operator.not_in_list: lambda a, b: a not in b if isinstance(b, list) else True,
27
+ }
28
+
29
+
30
+ class ConditionEvaluator:
31
+ """Evaluates nested conditions for manual task creation"""
32
+
33
+ def __init__(self, db: Session):
34
+ self.db = db
35
+
36
+ def evaluate_rule(self, rule: Condition, data: Union[dict, Any]) -> bool:
37
+ """Evaluate a nested condition rule against input data"""
38
+ if isinstance(rule, ConditionLeaf):
39
+ return self._evaluate_leaf_condition(rule, data)
40
+ # ConditionGroup
41
+ return self._evaluate_group_condition(rule, data)
42
+
43
+ def _evaluate_leaf_condition(
44
+ self, condition: ConditionLeaf, data: Union[dict, Any]
45
+ ) -> bool:
46
+ """Evaluate a leaf condition against input data"""
47
+ actual_value = self._get_nested_value(data, condition.field_address.split("."))
48
+ # Apply operator and return result
49
+ return self._apply_operator(actual_value, condition.operator, condition.value)
50
+
51
+ def _evaluate_group_condition(
52
+ self, group: ConditionGroup, data: Union[dict, Any]
53
+ ) -> bool:
54
+ """Evaluate a group condition against input data"""
55
+ results = [
56
+ self.evaluate_rule(condition, data) for condition in group.conditions
57
+ ]
58
+
59
+ logical_operators = {GroupOperator.and_: all, GroupOperator.or_: any}
60
+ operator_func = logical_operators.get(group.logical_operator)
61
+
62
+ if operator_func is None:
63
+ logger.warning(f"Unknown logical operator: {group.logical_operator}")
64
+ return False
65
+
66
+ return operator_func(results)
67
+
68
+ def _get_nested_value(self, data: Union[dict, Any], keys: list[str]) -> Any:
69
+ """Get nested value from data using dot notation
70
+
71
+ Supports both simple dictionary access and Fides reference structures:
72
+ - Simple dict: data["user"]["name"]
73
+ - Fides FieldAddress: data.get_field_value(FieldAddress("dataset", "collection", "field_address"))
74
+ - Fides Collection: data.get_field_value(FieldPath("field_address", "subfield"))
75
+ """
76
+ if not keys:
77
+ return data
78
+
79
+ current = data
80
+
81
+ # Try Fides reference structures first
82
+ if hasattr(current, "get_field_value"):
83
+ try:
84
+ field_path = FieldPath(*keys) if len(keys) > 1 else FieldPath(keys[0])
85
+ return current.get_field_value(field_path)
86
+ except (AttributeError, ValueError):
87
+ pass
88
+
89
+ # Fall back to dictionary access
90
+ for key in keys:
91
+ if not isinstance(current, dict):
92
+ current = current.get(key, {}) if hasattr(current, "get") else None
93
+ else:
94
+ current = current.get(key, {})
95
+
96
+ return current if current != {} else None
97
+
98
+ def _apply_operator(
99
+ self, actual_value: Any, operator: Operator, expected_value: Any
100
+ ) -> bool:
101
+ """Apply operator to actual and expected values"""
102
+
103
+ # Get the method for the operator and execute it
104
+ operator_method = operator_methods.get(operator)
105
+ if operator_method is None:
106
+ logger.warning(f"Unknown operator: {operator}")
107
+ return False
108
+
109
+ return operator_method(actual_value, expected_value)
@@ -0,0 +1,54 @@
1
+ from enum import Enum
2
+ from typing import List, Optional, Union
3
+
4
+ from pydantic import BaseModel, Field, model_validator
5
+
6
+
7
+ class Operator(str, Enum):
8
+ eq = "eq"
9
+ neq = "neq"
10
+ lt = "lt"
11
+ lte = "lte"
12
+ gt = "gt"
13
+ gte = "gte"
14
+ exists = "exists"
15
+ not_exists = "not_exists"
16
+ list_contains = "list_contains"
17
+ not_in_list = "not_in_list"
18
+
19
+
20
+ class GroupOperator(str, Enum):
21
+ and_ = "and"
22
+ or_ = "or"
23
+
24
+
25
+ # Leaf condition (e.g., "user.name exists", "user.created_at >= 2024-01-01")
26
+ class ConditionLeaf(BaseModel):
27
+ field_address: str = Field(
28
+ description="Field path to check (e.g., 'user.name', 'billing.subscription.status')"
29
+ )
30
+ operator: Operator = Field(description="Operator to apply")
31
+ value: Optional[
32
+ Union[str, int, float, bool, List[Union[str, int, float, bool]]]
33
+ ] = Field(default=None, description="Expected value for comparison")
34
+
35
+
36
+ # Nested logical condition (AND, OR)
37
+ class ConditionGroup(BaseModel):
38
+ logical_operator: GroupOperator = Field(
39
+ description="Logical operator: 'and' or 'or'"
40
+ )
41
+ conditions: list["Condition"] = Field(
42
+ description="List of conditions or nested groups"
43
+ )
44
+
45
+ @model_validator(mode="after")
46
+ def validate_conditions(self) -> "ConditionGroup":
47
+ if not self.conditions:
48
+ raise ValueError("conditions list cannot be empty")
49
+ return self
50
+
51
+
52
+ # Use model_rebuild to allow recursive nesting
53
+ Condition = Union[ConditionLeaf, ConditionGroup]
54
+ ConditionGroup.model_rebuild()