ethyca-fides 2.66.3b0__py2.py3-none-any.whl → 2.67.0__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 (246) hide show
  1. {ethyca_fides-2.66.3b0.dist-info → ethyca_fides-2.67.0.dist-info}/METADATA +1 -1
  2. {ethyca_fides-2.66.3b0.dist-info → ethyca_fides-2.67.0.dist-info}/RECORD +214 -210
  3. fides/_version.py +3 -3
  4. fides/api/common_exceptions.py +4 -0
  5. fides/api/graph/execution.py +16 -0
  6. fides/api/graph/graph.py +13 -2
  7. fides/api/graph/traversal.py +125 -38
  8. fides/api/models/privacy_request/privacy_request.py +33 -13
  9. fides/api/schemas/application_config.py +1 -0
  10. fides/api/schemas/connection_configuration/connection_secrets_datahub.py +10 -1
  11. fides/api/service/connectors/base_connector.py +14 -0
  12. fides/api/service/connectors/bigquery_connector.py +5 -0
  13. fides/api/service/connectors/query_configs/bigquery_query_config.py +4 -4
  14. fides/api/service/connectors/query_configs/snowflake_query_config.py +3 -3
  15. fides/api/service/connectors/snowflake_connector.py +55 -2
  16. fides/api/service/connectors/sql_connector.py +126 -12
  17. fides/api/service/privacy_request/dsr_package/templates/welcome.html +2 -2
  18. fides/api/service/privacy_request/request_runner_service.py +3 -2
  19. fides/api/service/privacy_request/request_service.py +173 -32
  20. fides/api/task/execute_request_tasks.py +4 -0
  21. fides/api/task/graph_task.py +48 -2
  22. fides/api/util/aws_util.py +5 -1
  23. fides/api/util/cache.py +56 -0
  24. fides/api/util/memory_watchdog.py +286 -0
  25. fides/config/execution_settings.py +8 -0
  26. fides/config/utils.py +1 -0
  27. fides/ui-build/static/admin/404.html +1 -1
  28. fides/ui-build/static/admin/_next/static/MNlh9olWjgbqAHKyQY3LF/_buildManifest.js +1 -0
  29. fides/ui-build/static/admin/_next/static/chunks/1316-2606e19807c08aa5.js +1 -0
  30. fides/ui-build/static/admin/_next/static/chunks/1467-8808ec8836e033f9.js +1 -0
  31. fides/ui-build/static/admin/_next/static/chunks/1817-b78b58ae3b75d75a.js +1 -0
  32. fides/ui-build/static/admin/_next/static/chunks/1975.7d4634a0e823a02b.js +1 -0
  33. fides/ui-build/static/admin/_next/static/chunks/{203-4e777c324a01dbec.js → 203-cd78ea279cecba60.js} +1 -1
  34. fides/ui-build/static/admin/_next/static/chunks/2150-930ffaf2c4718edc.js +1 -0
  35. fides/ui-build/static/admin/_next/static/chunks/255-1bc0cbef7a59cdc6.js +1 -0
  36. fides/ui-build/static/admin/_next/static/chunks/{3450-1cc2bb07ed142203.js → 3450-69f4e16978971bb8.js} +1 -1
  37. fides/ui-build/static/admin/_next/static/chunks/346-aa3b88efb85f2e28.js +1 -0
  38. fides/ui-build/static/admin/_next/static/chunks/3550-d04125c828d591a1.js +1 -0
  39. fides/ui-build/static/admin/_next/static/chunks/{3855-e2fa6db53d32c3de.js → 3855-509ca7ac99b5eada.js} +1 -1
  40. fides/ui-build/static/admin/_next/static/chunks/{3872-84b7e380b88b4454.js → 3872-0a0f0032ca39a93f.js} +1 -1
  41. fides/ui-build/static/admin/_next/static/chunks/409-ea70638a59296659.js +1 -0
  42. fides/ui-build/static/admin/_next/static/chunks/{4121-f50675521dfee6eb.js → 4121-877e19d3fa078c7b.js} +1 -1
  43. fides/ui-build/static/admin/_next/static/chunks/{4230-840c287045c88b34.js → 4230-114e31621c19ea69.js} +1 -1
  44. fides/ui-build/static/admin/_next/static/chunks/4259.d1507e0db19cbed7.js +1 -0
  45. fides/ui-build/static/admin/_next/static/chunks/{431-ade3e312fac3430b.js → 431-1db919f6569a4021.js} +1 -1
  46. fides/ui-build/static/admin/_next/static/chunks/{4608-a8e3100e2806dbff.js → 4608-f16f281f2d05d963.js} +1 -1
  47. fides/ui-build/static/admin/_next/static/chunks/5163-e682273cd76a7d07.js +1 -0
  48. fides/ui-build/static/admin/_next/static/chunks/5309-d9a488457898263b.js +1 -0
  49. fides/ui-build/static/admin/_next/static/chunks/{905-ffdbd0b14167e8bd.js → 5596-4378b2927dae65b2.js} +3 -3
  50. fides/ui-build/static/admin/_next/static/chunks/5619-9b50cec521203989.js +1 -0
  51. fides/ui-build/static/admin/_next/static/chunks/{6084-5d7598b7bcb548cf.js → 6084-ddbad3149364725d.js} +1 -1
  52. fides/ui-build/static/admin/_next/static/chunks/6148-59a59d5c5925344f.js +1 -0
  53. fides/ui-build/static/admin/_next/static/chunks/6419-d0c00d661b01f8fa.js +1 -0
  54. fides/ui-build/static/admin/_next/static/chunks/{6662-efb2cf74641647f2.js → 6662-a9e54ead3dc53644.js} +1 -1
  55. fides/ui-build/static/admin/_next/static/chunks/{6780-a00c87739acc004d.js → 6780-b42a27e72707936d.js} +1 -1
  56. fides/ui-build/static/admin/_next/static/chunks/{6853-4f8bf6558f8c6a46.js → 6853-d0190d2cca9dbde2.js} +1 -1
  57. fides/ui-build/static/admin/_next/static/chunks/{6882-586b84aeb02d5830.js → 6882-59ea739e3616ce83.js} +1 -1
  58. fides/ui-build/static/admin/_next/static/chunks/6954-ba98e778a5b45ebf.js +1 -0
  59. fides/ui-build/static/admin/_next/static/chunks/{7476-d206c11823c91088.js → 7476-cc0d9a94ed7aad53.js} +1 -1
  60. fides/ui-build/static/admin/_next/static/chunks/7725-539d3a906f627531.js +1 -0
  61. fides/ui-build/static/admin/_next/static/chunks/{787-cbe2d0bfb513d90a.js → 787-c57185ad89c4e288.js} +1 -1
  62. fides/ui-build/static/admin/_next/static/chunks/{79-3db1941d274f40c7.js → 79-2aab56be75e16187.js} +1 -1
  63. fides/ui-build/static/admin/_next/static/chunks/8735-40caf91800a3610c.js +1 -0
  64. fides/ui-build/static/admin/_next/static/chunks/8765-f622a35b40a7ec63.js +1 -0
  65. fides/ui-build/static/admin/_next/static/chunks/9046-7085a401297c5520.js +1 -0
  66. fides/ui-build/static/admin/_next/static/chunks/9187-7438242f0d380bb0.js +1 -0
  67. fides/ui-build/static/admin/_next/static/chunks/{9226-72ad691ca57b83ef.js → 9226-2dcac54ab3fb94be.js} +1 -1
  68. fides/ui-build/static/admin/_next/static/chunks/9278-08cc704317fe535e.js +1 -0
  69. fides/ui-build/static/admin/_next/static/chunks/{9951-595d0f1588215081.js → 9951-7c6639e5d062779e.js} +1 -1
  70. fides/ui-build/static/admin/_next/static/chunks/pages/{_app-f2c3d287bac00395.js → _app-750d6bd16c971bb9.js} +2 -2
  71. fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-acb59f8b5e97512a.js → manual-2a655ff3a97f2492.js} +1 -1
  72. fides/ui-build/static/admin/_next/static/chunks/pages/{add-systems-0943633a8e422695.js → add-systems-0902f0bb4080643e.js} +1 -1
  73. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{[id]-fc3a011154a2e1de.js → [id]-f22ddd9b48a5c418.js} +1 -1
  74. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{new-06bb3b0bf097fcdb.js → new-e74cb5ea87f15b40.js} +1 -1
  75. fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-experience-09d4408014bcfe1c.js → privacy-experience-21f997c69fc3b4c2.js} +1 -1
  76. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-d67542783ef5ddac.js → [id]-da4124b7600a2a1d.js} +1 -1
  77. fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-3f20e8a316bb3d5b.js → new-a57d251c88ce68ae.js} +1 -1
  78. fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-notices-23e9dcd4590312d2.js → privacy-notices-ad105181bc91209b.js} +1 -1
  79. fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-b0c4235fe6d0b0c8.js +1 -0
  80. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/{[resourceUrn]-99c9092d65c94807.js → [resourceUrn]-aad6047a4604b945.js} +1 -1
  81. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{projects-774fecea22ba8852.js → projects-29784a11fe0fbd0a.js} +1 -1
  82. fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/{[resourceUrn]-f6bd6aff389cb9fe.js → [resourceUrn]-b6b98cea25dd94fa.js} +1 -1
  83. fides/ui-build/static/admin/_next/static/chunks/pages/{data-catalog-8a7f9285da66b965.js → data-catalog-7770a8dc34bd0fc0.js} +1 -1
  84. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-3e5725cd06d7fe6c.js +1 -0
  85. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-92b0bd97d8e79340.js +1 -0
  86. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-ee3c0a103346fc06.js +1 -0
  87. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{activity-5af9381f02b2aff6.js → activity-ad6a84a6276f914c.js} +1 -1
  88. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-6421ce247549c5d6.js → [resourceUrn]-f98dd251babb7e28.js} +1 -1
  89. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-3eac407ac5181a3c.js → discovery-56eb4c014f0d96a3.js} +1 -1
  90. fides/ui-build/static/admin/_next/static/chunks/pages/datamap-d23b3ae139f0428b.js +1 -0
  91. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/{[...subfieldNames]-1c98bd0959d9570a.js → [...subfieldNames]-15301bd6bf7cf718.js} +1 -1
  92. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/{[collectionName]-e548cabda7da32c9.js → [collectionName]-0fa72873e464f581.js} +1 -1
  93. fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{new-513c862c3a707735.js → new-0d50084fbdf9b84c.js} +1 -1
  94. fides/ui-build/static/admin/_next/static/chunks/pages/{dataset-747b7a13289f1cd7.js → dataset-f3348d0a92543bab.js} +1 -1
  95. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-3d22525b3c327b2e.js → [id]-7d6027570d05c57f.js} +1 -1
  96. fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{new-d2cad97495e86adb.js → new-c6614583b14dc9f2.js} +1 -1
  97. fides/ui-build/static/admin/_next/static/chunks/pages/{fides-js-docs-5d8fd1af75f19e2f.js → fides-js-docs-1f4335dca5c09860.js} +1 -1
  98. fides/ui-build/static/admin/_next/static/chunks/pages/{index-12ac3e317fc86f21.js → index-fbf9b845bb901238.js} +1 -1
  99. fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-8e346fb36e8034d2.js +1 -0
  100. fides/ui-build/static/admin/_next/static/chunks/pages/{integrations-f10a7dcf7541c865.js → integrations-7f15cd8538cdc24d.js} +1 -1
  101. fides/ui-build/static/admin/_next/static/chunks/pages/messaging-1bae386d8c190348.js +1 -0
  102. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{[id]-c73497fc333c324d.js → [id]-79f1576b1126975c.js} +1 -1
  103. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{messaging-7498d1d5974a78b0.js → messaging-f1d818242d8550f8.js} +1 -1
  104. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{storage-2d3a2d967767a131.js → storage-d40a26bddb126c5c.js} +1 -1
  105. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-96a08c4431b5462c.js +1 -0
  106. fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-9d1840f8309b706e.js +1 -0
  107. fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent/[configuration_id]/{[purpose_id]-668d74c041d74650.js → [purpose_id]-e891d01ece59669e.js} +1 -1
  108. fides/ui-build/static/admin/_next/static/chunks/pages/settings/{consent-a989532a12c40dcf.js → consent-f61b87e79367865b.js} +1 -1
  109. fides/ui-build/static/admin/_next/static/chunks/pages/settings/{custom-fields-45bea76ff7eda3cb.js → custom-fields-f8eea5d508c60c64.js} +1 -1
  110. fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-6946e78a5d43e654.js → locations-ed6a140b362c5baa.js} +1 -1
  111. fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-55a10e01dffc8039.js → organization-ff9a34264d48c35f.js} +1 -1
  112. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-f91f22cf96566ed4.js → test-datasets-a4b6d41ca679298b.js} +1 -1
  113. fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-d4a57ea18935dd70.js → [id]-c8f5fbaa83dd9945.js} +1 -1
  114. fides/ui-build/static/admin/_next/static/chunks/pages/{systems-648a0ff4920579ce.js → systems-c05b49ddec1a1b4f.js} +1 -1
  115. fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-df0d88716578e295.js +1 -0
  116. fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/{[id]-fe58cebba358119d.js → [id]-da68efc31998dc66.js} +1 -1
  117. fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-45bfa04e45a7d13f.js → user-management-e98dfc7d4f2a4e16.js} +1 -1
  118. fides/ui-build/static/admin/_next/static/chunks/webpack-90e8ec1fc5c6455b.js +1 -0
  119. fides/ui-build/static/admin/_next/static/css/{79e296c724c1568c.css → d9924caa849931b3.css} +1 -1
  120. fides/ui-build/static/admin/_next/static/css/dbcf63488933a4d5.css +29 -0
  121. fides/ui-build/static/admin/add-systems/manual.html +1 -1
  122. fides/ui-build/static/admin/add-systems/multiple.html +1 -1
  123. fides/ui-build/static/admin/add-systems.html +1 -1
  124. fides/ui-build/static/admin/consent/configure/add-vendors.html +1 -1
  125. fides/ui-build/static/admin/consent/configure.html +1 -1
  126. fides/ui-build/static/admin/consent/privacy-experience/[id].html +1 -1
  127. fides/ui-build/static/admin/consent/privacy-experience/new.html +1 -1
  128. fides/ui-build/static/admin/consent/privacy-experience.html +1 -1
  129. fides/ui-build/static/admin/consent/privacy-notices/[id].html +1 -1
  130. fides/ui-build/static/admin/consent/privacy-notices/new.html +1 -1
  131. fides/ui-build/static/admin/consent/privacy-notices.html +1 -1
  132. fides/ui-build/static/admin/consent/properties.html +1 -1
  133. fides/ui-build/static/admin/consent/reporting.html +1 -1
  134. fides/ui-build/static/admin/consent.html +1 -1
  135. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn].html +1 -1
  136. fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn].html +1 -1
  137. fides/ui-build/static/admin/data-catalog/[systemId]/projects.html +1 -1
  138. fides/ui-build/static/admin/data-catalog/[systemId]/resources/[resourceUrn].html +1 -1
  139. fides/ui-build/static/admin/data-catalog/[systemId]/resources.html +1 -1
  140. fides/ui-build/static/admin/data-catalog.html +1 -1
  141. fides/ui-build/static/admin/data-discovery/action-center/[monitorId]/[systemId].html +1 -1
  142. fides/ui-build/static/admin/data-discovery/action-center/[monitorId].html +1 -1
  143. fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
  144. fides/ui-build/static/admin/data-discovery/activity.html +1 -1
  145. fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
  146. fides/ui-build/static/admin/data-discovery/detection.html +1 -1
  147. fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
  148. fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
  149. fides/ui-build/static/admin/datamap.html +1 -1
  150. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
  151. fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
  152. fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
  153. fides/ui-build/static/admin/dataset/new.html +1 -1
  154. fides/ui-build/static/admin/dataset.html +1 -1
  155. fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
  156. fides/ui-build/static/admin/datastore-connection/new.html +1 -1
  157. fides/ui-build/static/admin/datastore-connection.html +1 -1
  158. fides/ui-build/static/admin/index.html +1 -1
  159. fides/ui-build/static/admin/integrations/[id].html +1 -1
  160. fides/ui-build/static/admin/integrations.html +1 -1
  161. fides/ui-build/static/admin/lib/fides-ext-gpp.js +1 -1
  162. fides/ui-build/static/admin/lib/fides-headless.js +1 -1
  163. fides/ui-build/static/admin/lib/fides-preview.js +1 -1
  164. fides/ui-build/static/admin/lib/fides-tcf.js +4 -4
  165. fides/ui-build/static/admin/lib/fides.js +4 -4
  166. fides/ui-build/static/admin/login/[provider].html +1 -1
  167. fides/ui-build/static/admin/login.html +1 -1
  168. fides/ui-build/static/admin/messaging/[id].html +1 -1
  169. fides/ui-build/static/admin/messaging/add-template.html +1 -1
  170. fides/ui-build/static/admin/messaging.html +1 -1
  171. fides/ui-build/static/admin/poc/ant-components.html +1 -1
  172. fides/ui-build/static/admin/poc/form-experiments/AntForm.html +1 -1
  173. fides/ui-build/static/admin/poc/form-experiments/FormikAntFormItem.html +1 -1
  174. fides/ui-build/static/admin/poc/form-experiments/FormikControlled.html +1 -1
  175. fides/ui-build/static/admin/poc/form-experiments/FormikField.html +1 -1
  176. fides/ui-build/static/admin/poc/form-experiments/FormikSpreadField.html +1 -1
  177. fides/ui-build/static/admin/poc/forms.html +1 -1
  178. fides/ui-build/static/admin/poc/table-migration.html +1 -1
  179. fides/ui-build/static/admin/privacy-requests/[id].html +1 -1
  180. fides/ui-build/static/admin/privacy-requests/configure/messaging.html +1 -1
  181. fides/ui-build/static/admin/privacy-requests/configure/storage.html +1 -1
  182. fides/ui-build/static/admin/privacy-requests/configure.html +1 -1
  183. fides/ui-build/static/admin/privacy-requests.html +1 -1
  184. fides/ui-build/static/admin/properties/[id].html +1 -1
  185. fides/ui-build/static/admin/properties/add-property.html +1 -1
  186. fides/ui-build/static/admin/properties.html +1 -1
  187. fides/ui-build/static/admin/reporting/datamap.html +1 -1
  188. fides/ui-build/static/admin/settings/about/alpha.html +1 -1
  189. fides/ui-build/static/admin/settings/about.html +1 -1
  190. fides/ui-build/static/admin/settings/consent/[configuration_id]/[purpose_id].html +1 -1
  191. fides/ui-build/static/admin/settings/consent.html +1 -1
  192. fides/ui-build/static/admin/settings/custom-fields.html +1 -1
  193. fides/ui-build/static/admin/settings/domain-records.html +1 -1
  194. fides/ui-build/static/admin/settings/domains.html +1 -1
  195. fides/ui-build/static/admin/settings/email-templates.html +1 -1
  196. fides/ui-build/static/admin/settings/locations.html +1 -1
  197. fides/ui-build/static/admin/settings/organization.html +1 -1
  198. fides/ui-build/static/admin/settings/regulations.html +1 -1
  199. fides/ui-build/static/admin/systems/configure/[id]/test-datasets.html +1 -1
  200. fides/ui-build/static/admin/systems/configure/[id].html +1 -1
  201. fides/ui-build/static/admin/systems.html +1 -1
  202. fides/ui-build/static/admin/taxonomy.html +1 -1
  203. fides/ui-build/static/admin/user-management/new.html +1 -1
  204. fides/ui-build/static/admin/user-management/profile/[id].html +1 -1
  205. fides/ui-build/static/admin/user-management.html +1 -1
  206. fides/ui-build/static/admin/_next/static/chunks/1316-6cc72a45ebf7ff81.js +0 -1
  207. fides/ui-build/static/admin/_next/static/chunks/1807-3beab149351d5ded.js +0 -1
  208. fides/ui-build/static/admin/_next/static/chunks/1817-6f35f58cd08b04ae.js +0 -1
  209. fides/ui-build/static/admin/_next/static/chunks/255-7db55b0e3a0f9dea.js +0 -1
  210. fides/ui-build/static/admin/_next/static/chunks/2599-6c4d22e75028d8b6.js +0 -1
  211. fides/ui-build/static/admin/_next/static/chunks/2858-0b44609b6be7850b.js +0 -1
  212. fides/ui-build/static/admin/_next/static/chunks/2866-a73888c17a195cbe.js +0 -1
  213. fides/ui-build/static/admin/_next/static/chunks/3615-5e2d062d684b8fa1.js +0 -1
  214. fides/ui-build/static/admin/_next/static/chunks/409-45a125437261580c.js +0 -1
  215. fides/ui-build/static/admin/_next/static/chunks/5309-1172322bf91b5d57.js +0 -1
  216. fides/ui-build/static/admin/_next/static/chunks/570-c99f07161bd339cd.js +0 -1
  217. fides/ui-build/static/admin/_next/static/chunks/6954-34e062e4bffc7e71.js +0 -1
  218. fides/ui-build/static/admin/_next/static/chunks/7062.fda15dcb7df85675.js +0 -1
  219. fides/ui-build/static/admin/_next/static/chunks/7c79804f.7a7112aece470725.js +0 -1
  220. fides/ui-build/static/admin/_next/static/chunks/8237-55049f8f5fd5e058.js +0 -1
  221. fides/ui-build/static/admin/_next/static/chunks/9014-eeae6f581158e645.js +0 -1
  222. fides/ui-build/static/admin/_next/static/chunks/9046-c8233981762585b4.js +0 -1
  223. fides/ui-build/static/admin/_next/static/chunks/9278-9b1b5970f0702668.js +0 -1
  224. fides/ui-build/static/admin/_next/static/chunks/9392.f4520f66206d347d.js +0 -1
  225. fides/ui-build/static/admin/_next/static/chunks/ea076c0a.84423f606aef37cd.js +0 -1
  226. fides/ui-build/static/admin/_next/static/chunks/pages/consent/reporting-8f891957c8944137.js +0 -1
  227. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-71579a199158952e.js +0 -1
  228. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-7d3115059503b904.js +0 -1
  229. fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-85e140788e251272.js +0 -1
  230. fides/ui-build/static/admin/_next/static/chunks/pages/datamap-d2b275d83089820d.js +0 -1
  231. fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-f9c0eac932188593.js +0 -1
  232. fides/ui-build/static/admin/_next/static/chunks/pages/messaging-c1bd3e7adbe8d2d3.js +0 -1
  233. fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests-6a9068df48bdee05.js +0 -1
  234. fides/ui-build/static/admin/_next/static/chunks/pages/reporting/datamap-2a98bd257edd8f47.js +0 -1
  235. fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-0b9d1a24188f65a9.js +0 -1
  236. fides/ui-build/static/admin/_next/static/chunks/webpack-63a0c45b150a1037.js +0 -1
  237. fides/ui-build/static/admin/_next/static/hKDMNIRKgB86FSDpiOjHn/_buildManifest.js +0 -1
  238. {ethyca_fides-2.66.3b0.dist-info → ethyca_fides-2.67.0.dist-info}/WHEEL +0 -0
  239. {ethyca_fides-2.66.3b0.dist-info → ethyca_fides-2.67.0.dist-info}/entry_points.txt +0 -0
  240. {ethyca_fides-2.66.3b0.dist-info → ethyca_fides-2.67.0.dist-info}/licenses/LICENSE +0 -0
  241. {ethyca_fides-2.66.3b0.dist-info → ethyca_fides-2.67.0.dist-info}/top_level.txt +0 -0
  242. /fides/ui-build/static/admin/_next/static/{hKDMNIRKgB86FSDpiOjHn → MNlh9olWjgbqAHKyQY3LF}/_ssgManifest.js +0 -0
  243. /fides/ui-build/static/admin/_next/static/chunks/pages/{datastore-connection-0f29b47402292070.js → datastore-connection-3bd77864da523d41.js} +0 -0
  244. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/about/{alpha-8f98a4895e74725e.js → alpha-1066f0c202ef744c.js} +0 -0
  245. /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{about-8155a35a62fdb5ae.js → about-37ba24a72a06862e.js} +0 -0
  246. /fides/ui-build/static/admin/_next/static/chunks/pages/user-management/{new-bc4eb541906781e6.js → new-de8cb3739ab99c09.js} +0 -0
fides/_version.py CHANGED
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2025-07-29T12:14:04-0400",
11
+ "date": "2025-08-01T17:34:13-0700",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "9e0a314647173d6c1527fa8e2567d8058075bd15",
15
- "version": "2.66.3b0"
14
+ "full-revisionid": "fd2fea83c304d1b8f1d9fa0c01172ccead8d45a3",
15
+ "version": "2.67.0"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -151,6 +151,10 @@ class NotSupportedForCollection(BaseException):
151
151
  """The given action is not supported for this type of collection"""
152
152
 
153
153
 
154
+ class TableNotFound(BaseException):
155
+ """Table or collection does not exist in the database/system"""
156
+
157
+
154
158
  class PrivacyRequestExit(BaseException):
155
159
  """Privacy request exiting processing waiting on subtasks to complete"""
156
160
 
@@ -4,6 +4,7 @@ from fideslang.validation import FidesKey
4
4
  from loguru import logger
5
5
 
6
6
  from fides.api.graph.config import (
7
+ TERMINATOR_ADDRESS,
7
8
  Collection,
8
9
  CollectionAddress,
9
10
  Field,
@@ -157,3 +158,18 @@ class ExecutionNode(Contextualizable): # pylint: disable=too-many-instance-attr
157
158
  )
158
159
 
159
160
  return out
161
+
162
+ def has_outgoing_dependencies(self) -> bool:
163
+ """
164
+ Check if this node has outgoing edges to collections other than the terminal node.
165
+
166
+ Returns:
167
+ bool: True if the node has dependencies on other collections, False otherwise
168
+ """
169
+
170
+ for edge in self.outgoing_edges:
171
+ # Check if the outgoing edge points to a collection other than the terminal node
172
+ target_collection = edge.f2.collection_address()
173
+ if target_collection != TERMINATOR_ADDRESS:
174
+ return True
175
+ return False
fides/api/graph/graph.py CHANGED
@@ -180,8 +180,19 @@ class DatasetGraph:
180
180
  this is a destructive operation on the input datasets, as it
181
181
  will alter references within them"""
182
182
 
183
- # build nodes
184
- nodes = [Node(dr, ds) for dr in datasets for ds in dr.collections]
183
+ # build nodes, filtering out collections with skip_processing=True
184
+ nodes = []
185
+ for dr in datasets:
186
+ for ds in dr.collections:
187
+ if ds.skip_processing:
188
+ logger.debug(
189
+ "Skipping collection {} on dataset {} marked with skip_processing",
190
+ ds.name,
191
+ dr.name,
192
+ )
193
+ continue
194
+ nodes.append(Node(dr, ds))
195
+
185
196
  self.nodes: dict[CollectionAddress, Node] = {
186
197
  node.address: node for node in nodes
187
198
  }
@@ -1,6 +1,8 @@
1
+ # pylint: disable=too-many-instance-attributes, too-many-branches, too-many-statements
1
2
  from __future__ import annotations
2
3
 
3
4
  import json
5
+ from collections import defaultdict
4
6
  from itertools import chain
5
7
  from typing import Any, Callable, Dict, List, Optional, Set, Tuple, cast
6
8
 
@@ -99,20 +101,51 @@ class BaseTraversal:
99
101
  self.traversal_node_dict = {k: TraversalNode(v) for k, v in graph.nodes.items()}
100
102
  self.edges: Set[Edge] = graph.edges.copy()
101
103
  self.root_node = artificial_traversal_node(ROOT_COLLECTION_ADDRESS)
104
+
105
+ # Pre-index edges by node address for O(1) lookup
106
+ self.edges_by_node: Dict[CollectionAddress, List[Edge]] = defaultdict(list)
107
+ for edge in self.edges:
108
+ self.edges_by_node[edge.f1.collection_address()].append(edge)
109
+ self.edges_by_node[edge.f2.collection_address()].append(edge)
110
+
111
+ # Pre-compute string versions of node dependencies
112
+ # This avoids expensive hash operations during traversal
113
+ self.node_after_str: Dict[str, Set[str]] = {}
114
+ self.dataset_after_str: Dict[str, Set[str]] = {}
115
+
116
+ for addr, traversal_node in self.traversal_node_dict.items():
117
+ # Collection-level after dependencies
118
+ self.node_after_str[addr.value] = {
119
+ dep.value for dep in traversal_node.node.collection.after
120
+ }
121
+ # Dataset-level after dependencies (need to find all collections in those datasets)
122
+ dataset_deps = set()
123
+ for dataset_name in traversal_node.node.dataset.after:
124
+ for other_addr in self.traversal_node_dict.keys():
125
+ if other_addr.dataset == dataset_name:
126
+ dataset_deps.add(other_addr.value)
127
+ self.dataset_after_str[addr.value] = dataset_deps
128
+
129
+ # Add root node to the pre-computed dependencies (it has no dependencies)
130
+ self.node_after_str[ROOT_COLLECTION_ADDRESS.value] = set()
131
+ self.dataset_after_str[ROOT_COLLECTION_ADDRESS.value] = set()
132
+
102
133
  for (
103
134
  start_field_address,
104
135
  seed_key,
105
136
  ) in self.extract_seed_field_addresses().items():
106
- self.edges.add(
107
- Edge(
108
- FieldAddress(
109
- ROOT_COLLECTION_ADDRESS.dataset,
110
- ROOT_COLLECTION_ADDRESS.collection,
111
- seed_key,
112
- ),
113
- start_field_address,
114
- )
137
+ edge = Edge(
138
+ FieldAddress(
139
+ ROOT_COLLECTION_ADDRESS.dataset,
140
+ ROOT_COLLECTION_ADDRESS.collection,
141
+ seed_key,
142
+ ),
143
+ start_field_address,
115
144
  )
145
+ self.edges.add(edge)
146
+ # Add to edge index
147
+ self.edges_by_node[ROOT_COLLECTION_ADDRESS].append(edge)
148
+ self.edges_by_node[start_field_address.collection_address()].append(edge)
116
149
 
117
150
  # Ensure manual_task collections execute right after ROOT
118
151
  from fides.api.task.manual.manual_task_address import ManualTaskAddress
@@ -120,16 +153,18 @@ class BaseTraversal:
120
153
  for addr in self.traversal_node_dict.keys():
121
154
  if ManualTaskAddress.is_manual_task_address(addr):
122
155
  # Add a simple synthetic edge ROOT.id -> manual_data.id
123
- self.edges.add(
124
- Edge(
125
- FieldAddress(
126
- ROOT_COLLECTION_ADDRESS.dataset,
127
- ROOT_COLLECTION_ADDRESS.collection,
128
- "id",
129
- ),
130
- addr.field_address(FieldPath("id")),
131
- )
156
+ edge = Edge(
157
+ FieldAddress(
158
+ ROOT_COLLECTION_ADDRESS.dataset,
159
+ ROOT_COLLECTION_ADDRESS.collection,
160
+ "id",
161
+ ),
162
+ addr.field_address(FieldPath("id")),
132
163
  )
164
+ self.edges.add(edge)
165
+ # Add to edge index
166
+ self.edges_by_node[ROOT_COLLECTION_ADDRESS].append(edge)
167
+ self.edges_by_node[addr].append(edge)
133
168
 
134
169
  self._verify_traversal()
135
170
 
@@ -204,28 +239,52 @@ class BaseTraversal:
204
239
  logger.info(
205
240
  "Starting traversal",
206
241
  )
207
- remaining_node_keys: Set[CollectionAddress] = set(
208
- self.traversal_node_dict.keys()
209
- )
242
+
243
+ # Use string sets instead of CollectionAddress sets
244
+ # This avoids expensive hash operations
245
+ remaining_node_keys_str: Set[str] = {
246
+ addr.value for addr in self.traversal_node_dict.keys()
247
+ }
210
248
  finished_nodes: dict[CollectionAddress, TraversalNode] = {}
211
249
  running_node_queue: MatchingQueue[TraversalNode] = MatchingQueue(self.root_node)
212
250
 
213
- remaining_edges: Set[Edge] = self.edges.copy()
251
+ # Instead of copying entire edge set, use a more efficient approach
252
+ # We'll simulate Edge.delete_edges behavior without the expensive set operations
253
+ deleted_edges_tracker: Dict[Edge, bool] = {}
254
+
214
255
  while not running_node_queue.is_empty():
215
256
  # this is to support the "run traversal_node A AFTER traversal_node B functionality:"
216
257
  n = running_node_queue.pop_first_match(
217
- lambda x: x.can_run_given(remaining_node_keys)
258
+ lambda x: x.can_run_given_str(
259
+ remaining_node_keys_str, self.node_after_str, self.dataset_after_str
260
+ )
218
261
  )
219
262
 
220
263
  if n:
221
264
  node_run_fn(n, environment)
222
265
  # delete all edges between the traversal_node that's just run and any completed nodes
223
266
  for finished_node_address, finished_node in finished_nodes.items():
224
- completed_edges: Set[Edge] = Edge.delete_edges(
225
- remaining_edges,
226
- finished_node_address,
227
- cast(TraversalNode, n).address, # type: ignore[redundant-cast]
267
+ # Find edges to delete manually instead of using Edge.delete_edges
268
+ completed_edges: Set[Edge] = set()
269
+
270
+ # Only check edges connected to the relevant nodes
271
+ relevant_edges = set()
272
+ relevant_edges.update(
273
+ self.edges_by_node.get(finished_node_address, [])
228
274
  )
275
+ relevant_edges.update(self.edges_by_node.get(n.address, []))
276
+
277
+ for edge in relevant_edges:
278
+ # Skip if already deleted
279
+ if deleted_edges_tracker.get(edge, False):
280
+ continue
281
+
282
+ # Check if this edge spans between the two nodes (bidirectional check)
283
+ if edge.spans(
284
+ finished_node_address, cast(TraversalNode, n).address # type: ignore[redundant-cast]
285
+ ):
286
+ completed_edges.add(edge)
287
+ deleted_edges_tracker[edge] = True
229
288
 
230
289
  def edge_ends_with_collection(_edge: Edge) -> bool:
231
290
  # append edges that end in this traversal_node
@@ -236,14 +295,16 @@ class BaseTraversal:
236
295
  for edge in filter(edge_ends_with_collection, completed_edges):
237
296
  # note, this will not work for self-reference
238
297
  finished_node.add_child(n, edge)
298
+
239
299
  # next edges = take all edges including n that are _not_ in edges_from_completed_nodes
240
300
  # in the form (field_address_this, field_address_foreign)
241
301
 
302
+ # Use pre-indexed edges instead of iterating through all edges
242
303
  edges_to_children = pydash.collections.filter_(
243
304
  [
244
305
  e.split_by_address(cast(TraversalNode, n).address) # type: ignore[redundant-cast]
245
- for e in remaining_edges
246
- if e.contains(n.address)
306
+ for e in self.edges_by_node[n.address]
307
+ if not deleted_edges_tracker.get(e, False)
247
308
  ]
248
309
  )
249
310
  if not edges_to_children:
@@ -259,7 +320,7 @@ class BaseTraversal:
259
320
  self.traversal_node_dict[nxt_address]
260
321
  )
261
322
  finished_nodes[n.address] = n
262
- remaining_node_keys.difference_update({n.address})
323
+ remaining_node_keys_str.discard(n.address.value) # Use string value
263
324
  else:
264
325
  # traversal traversal_node dict diff finished nodes
265
326
  logger.error(
@@ -271,12 +332,17 @@ class BaseTraversal:
271
332
  [{', '.join([str(tn.address) for tn in running_node_queue.data])}]""",
272
333
  )
273
334
 
335
+ # Convert back to CollectionAddress set for filtering
274
336
  remaining_node_keys = {
275
337
  key
276
- for key in remaining_node_keys
277
- if not self.should_exclude_node(self.traversal_node_dict[key])
338
+ for key in self.traversal_node_dict.keys()
339
+ if key.value in remaining_node_keys_str
340
+ and not self.should_exclude_node(self.traversal_node_dict[key])
278
341
  }
279
342
 
343
+ # Update string set after filtering
344
+ remaining_node_keys_str = {key.value for key in remaining_node_keys}
345
+
280
346
  # error if there are nodes that have not been visited
281
347
  if remaining_node_keys:
282
348
  logger.error(
@@ -289,12 +355,16 @@ class BaseTraversal:
289
355
  )
290
356
 
291
357
  # filter out remaining_edges if the nodes they link are allowed to remain unreachable
292
- remaining_edges = {
293
- edge
294
- for edge in remaining_edges
295
- if edge.f1.collection_address() in remaining_node_keys
296
- and edge.f2.collection_address() in remaining_node_keys
297
- }
358
+ remaining_edges = set()
359
+ for node_key in remaining_node_keys:
360
+ for edge in self.edges_by_node.get(node_key, []):
361
+ if not deleted_edges_tracker.get(edge, False):
362
+ # Check if both ends of the edge are in remaining nodes
363
+ if (
364
+ edge.f1.collection_address() in remaining_node_keys
365
+ and edge.f2.collection_address() in remaining_node_keys
366
+ ):
367
+ remaining_edges.add(edge)
298
368
 
299
369
  # error if there are edges that have not been visited
300
370
  if remaining_edges:
@@ -478,6 +548,23 @@ class TraversalNode(Contextualizable):
478
548
  return False
479
549
  return True
480
550
 
551
+ def can_run_given_str(
552
+ self,
553
+ remaining_node_keys_str: Set[str],
554
+ node_after_str: Dict[str, Set[str]],
555
+ dataset_after_str: Dict[str, Set[str]],
556
+ ) -> bool:
557
+ """Optimized version using pre-computed string sets to avoid expensive hash operations."""
558
+ # Check collection-level dependencies
559
+ node_deps = node_after_str.get(self.address.value, set())
560
+ if node_deps & remaining_node_keys_str:
561
+ return False
562
+ # Check dataset-level dependencies
563
+ dataset_deps = dataset_after_str.get(self.address.value, set())
564
+ if dataset_deps & remaining_node_keys_str:
565
+ return False
566
+ return True
567
+
481
568
  def is_root_node(self) -> bool:
482
569
  """This traversal_node is the defined traversal start"""
483
570
  return self.address == ROOT_COLLECTION_ADDRESS
@@ -1009,6 +1009,38 @@ class PrivacyRequest(
1009
1009
  request_task_celery_ids.append(request_task_id)
1010
1010
  return request_task_celery_ids
1011
1011
 
1012
+ def cancel_celery_tasks(self) -> None:
1013
+ """Cancel all Celery tasks associated with this privacy request.
1014
+
1015
+ This includes both the main privacy request task and any sub-tasks (Request Tasks).
1016
+ """
1017
+ task_ids: List[str] = []
1018
+
1019
+ # Add the main privacy request task ID
1020
+ parent_task_id = self.get_cached_task_id()
1021
+ if parent_task_id:
1022
+ task_ids.append(parent_task_id)
1023
+
1024
+ # Add all request task IDs
1025
+ request_task_celery_ids = self.get_request_task_celery_task_ids()
1026
+ task_ids.extend(request_task_celery_ids)
1027
+
1028
+ if not task_ids:
1029
+ return
1030
+
1031
+ # Revoke all Celery tasks in batch
1032
+ logger.info(f"Revoking {len(task_ids)} tasks for privacy request {self.id}")
1033
+ try:
1034
+ # Use terminate=False to allow graceful shutdown if already running
1035
+ celery_app.control.revoke(task_ids, terminate=False)
1036
+ logger.info(
1037
+ f"Successfully revoked {len(task_ids)} tasks for privacy request {self.id}"
1038
+ )
1039
+ except Exception as exc:
1040
+ logger.warning(
1041
+ f"Failed to revoke {len(task_ids)} tasks for privacy request {self.id}: {exc}"
1042
+ )
1043
+
1012
1044
  def cancel_processing(self, db: Session, cancel_reason: Optional[str]) -> None:
1013
1045
  """Cancels a privacy request. Currently should only cancel 'pending' tasks
1014
1046
 
@@ -1021,19 +1053,7 @@ class PrivacyRequest(
1021
1053
  self.canceled_at = datetime.utcnow()
1022
1054
  self.save(db)
1023
1055
 
1024
- task_ids: List[str] = (
1025
- self.get_request_task_celery_task_ids()
1026
- ) # Celery tasks for sub tasks (DSR 3.0 Request Tasks)
1027
- parent_task_id = (
1028
- self.get_cached_task_id()
1029
- ) # Celery task for current Privacy Request
1030
- if parent_task_id:
1031
- task_ids.append(parent_task_id)
1032
-
1033
- for celery_task_id in task_ids:
1034
- logger.info("Revoking task {} for request {}", celery_task_id, self.id)
1035
- # Only revokes if execution is not already in progress.
1036
- celery_app.control.revoke(celery_task_id, terminate=False)
1056
+ self.cancel_celery_tasks()
1037
1057
 
1038
1058
  def error_processing(self, db: Session) -> None:
1039
1059
  """Mark privacy request as errored, and note time processing was finished"""
@@ -70,6 +70,7 @@ class ExecutionApplicationConfig(FidesSchema):
70
70
  subject_identity_verification_required: Optional[bool] = None
71
71
  disable_consent_identity_verification: Optional[bool] = None
72
72
  require_manual_request_approval: Optional[bool] = None
73
+ memory_watchdog_enabled: Optional[bool] = None
73
74
  sql_dry_run: Optional[SqlDryRunMode] = None
74
75
 
75
76
  model_config = ConfigDict(use_enum_values=True, extra="forbid")
@@ -16,6 +16,15 @@ class PeriodicIntegrationFrequency(Enum):
16
16
  daily = "daily"
17
17
  weekly = "weekly"
18
18
  monthly = "monthly"
19
+ not_scheduled = "not scheduled"
20
+
21
+
22
+ PERIODIC_INTEGRATION_FREQUENCY_TO_DAYS = {
23
+ PeriodicIntegrationFrequency.daily.value: 1,
24
+ PeriodicIntegrationFrequency.weekly.value: 7,
25
+ PeriodicIntegrationFrequency.monthly.value: 30,
26
+ PeriodicIntegrationFrequency.not_scheduled.value: -1, # negative value to indicate that the integration is not scheduled
27
+ }
19
28
 
20
29
 
21
30
  class DatahubSchema(ConnectionConfigSecretsSchema):
@@ -30,7 +39,7 @@ class DatahubSchema(ConnectionConfigSecretsSchema):
30
39
  )
31
40
  frequency: PeriodicIntegrationFrequency = Field(
32
41
  title="Frequency",
33
- description="The frequency at which the integration should run. Available options are daily, weekly, and monthly.",
42
+ description="The frequency at which the integration should run. Available options are daily, weekly, monthly, and not scheduled.",
34
43
  )
35
44
  glossary_node: str = Field(
36
45
  title="Glossary node",
@@ -144,3 +144,17 @@ class BaseConnector(Generic[DB_CONNECTOR_TYPE], ABC):
144
144
  # Defaulting to true for now so we can keep the default behavior and
145
145
  # incrementally determine the need for primary keys across all connectors
146
146
  return True
147
+
148
+ def get_qualified_table_name(self, node: ExecutionNode) -> str:
149
+ """
150
+ Get the fully qualified table name for the given execution node.
151
+ """
152
+ raise NotImplementedError(
153
+ "get_qualified_table_name is not implemented by this connector"
154
+ )
155
+
156
+ def table_exists(self, qualified_table_name: str) -> bool:
157
+ """
158
+ Check if a table exists in the datastore.
159
+ """
160
+ raise NotImplementedError("table_exists is not implemented by this connector")
@@ -70,6 +70,11 @@ class BigQueryConnector(SQLConnector):
70
70
  node, SQLConnector.get_namespace_meta(db, node.address.dataset)
71
71
  )
72
72
 
73
+ def get_qualified_table_name(self, node: ExecutionNode) -> str:
74
+ """Get fully qualified BigQuery table name using existing query config logic"""
75
+ query_config = self.query_config(node)
76
+ return query_config.generate_table_name()
77
+
73
78
  def partitioned_retrieval(
74
79
  self,
75
80
  query_config: SQLQueryConfig,
@@ -93,7 +93,7 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
93
93
 
94
94
  return where_clauses
95
95
 
96
- def _generate_table_name(self) -> str:
96
+ def generate_table_name(self) -> str:
97
97
  """
98
98
  Prepends the dataset ID and project ID to the base table name
99
99
  if the BigQuery namespace meta is provided.
@@ -116,7 +116,7 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
116
116
  Returns a query string with backtick formatting for tables that have the same names as
117
117
  BigQuery reserved words.
118
118
  """
119
- return f'SELECT {field_list} FROM `{self._generate_table_name()}` WHERE ({" OR ".join(clauses)})'
119
+ return f'SELECT {field_list} FROM `{self.generate_table_name()}` WHERE ({" OR ".join(clauses)})'
120
120
 
121
121
  def generate_masking_stmt(
122
122
  self,
@@ -197,7 +197,7 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
197
197
  )
198
198
  return []
199
199
 
200
- table = Table(self._generate_table_name(), MetaData(bind=client), autoload=True)
200
+ table = Table(self.generate_table_name(), MetaData(bind=client), autoload=True)
201
201
  where_clauses: List[ColumnElement] = [
202
202
  table.c[k] == v for k, v in non_empty_reference_field_keys.items()
203
203
  ]
@@ -256,7 +256,7 @@ class BigQueryQueryConfig(QueryStringWithoutTuplesOverrideQueryConfig):
256
256
  )
257
257
  return []
258
258
 
259
- table = Table(self._generate_table_name(), MetaData(bind=client), autoload=True)
259
+ table = Table(self.generate_table_name(), MetaData(bind=client), autoload=True)
260
260
 
261
261
  # Build individual reference clauses
262
262
  where_clauses: List[ColumnElement] = []
@@ -30,7 +30,7 @@ class SnowflakeQueryConfig(SQLQueryConfig):
30
30
  """Returns field names in clauses surrounded by quotation marks as required by Snowflake syntax."""
31
31
  return f'"{string_path}" {operator} (:{operand})'
32
32
 
33
- def _generate_table_name(self) -> str:
33
+ def generate_table_name(self) -> str:
34
34
  """
35
35
  Prepends the dataset name and schema to the base table name
36
36
  if the Snowflake namespace meta is provided.
@@ -57,7 +57,7 @@ class SnowflakeQueryConfig(SQLQueryConfig):
57
57
  clauses: List[str],
58
58
  ) -> str:
59
59
  """Returns a query string with double quotation mark formatting as required by Snowflake syntax."""
60
- return f'SELECT {field_list} FROM {self._generate_table_name()} WHERE ({" OR ".join(clauses)})'
60
+ return f'SELECT {field_list} FROM {self.generate_table_name()} WHERE ({" OR ".join(clauses)})'
61
61
 
62
62
  def format_key_map_for_update_stmt(self, param_map: Dict[str, Any]) -> List[str]:
63
63
  """Adds the appropriate formatting for update statements in this datastore."""
@@ -69,4 +69,4 @@ class SnowflakeQueryConfig(SQLQueryConfig):
69
69
  where_clauses: List[str],
70
70
  ) -> str:
71
71
  """Returns a parameterized update statement in Snowflake dialect."""
72
- return f'UPDATE {self._generate_table_name()} SET {", ".join(update_clauses)} WHERE {" AND ".join(where_clauses)}'
72
+ return f'UPDATE {self.generate_table_name()} SET {", ".join(update_clauses)} WHERE {" AND ".join(where_clauses)}'
@@ -3,11 +3,11 @@ from typing import Any, Dict, Union
3
3
  from cryptography.hazmat.backends import default_backend
4
4
  from cryptography.hazmat.primitives import serialization
5
5
  from snowflake.sqlalchemy import URL as Snowflake_URL
6
+ from sqlalchemy import text
6
7
  from sqlalchemy.orm import Session
7
8
 
8
9
  from fides.api.graph.execution import ExecutionNode
9
10
  from fides.api.schemas.connection_configuration import SnowflakeSchema
10
- from fides.api.service.connectors.query_configs.query_config import SQLQueryConfig
11
11
  from fides.api.service.connectors.query_configs.snowflake_query_config import (
12
12
  SnowflakeQueryConfig,
13
13
  )
@@ -69,10 +69,63 @@ class SnowflakeConnector(SQLConnector):
69
69
  connect_args["private_key"] = private_key
70
70
  return connect_args
71
71
 
72
- def query_config(self, node: ExecutionNode) -> SQLQueryConfig:
72
+ def query_config(self, node: ExecutionNode) -> SnowflakeQueryConfig:
73
73
  """Query wrapper corresponding to the input execution_node."""
74
74
 
75
75
  db: Session = Session.object_session(self.configuration)
76
76
  return SnowflakeQueryConfig(
77
77
  node, SQLConnector.get_namespace_meta(db, node.address.dataset)
78
78
  )
79
+
80
+ def get_qualified_table_name(self, node: ExecutionNode) -> str:
81
+ """Get fully qualified Snowflake table name using existing query config logic"""
82
+ query_config = self.query_config(node)
83
+ return query_config.generate_table_name()
84
+
85
+ def table_exists(self, qualified_table_name: str) -> bool:
86
+ """
87
+ Check if table exists in Snowflake using the proper three-part naming convention.
88
+
89
+ Snowflake supports database.schema.table naming, and the generic SQLConnector
90
+ table_exists method doesn't handle quoted identifiers properly.
91
+ """
92
+ try:
93
+ client = self.create_client()
94
+ with client.connect() as connection:
95
+ # Remove quotes and split the parts
96
+ clean_name = qualified_table_name.replace('"', "")
97
+ parts = clean_name.split(".")
98
+
99
+ if len(parts) == 1:
100
+ # Simple table name - use current schema
101
+ table_name = parts[0]
102
+ result = connection.execute(text(f'DESC TABLE "{table_name}"'))
103
+ elif len(parts) == 2:
104
+ # schema.table format
105
+ schema_name, table_name = parts
106
+ result = connection.execute(
107
+ text(f'DESC TABLE "{schema_name}"."{table_name}"')
108
+ )
109
+ elif len(parts) >= 3:
110
+ # database.schema.table format
111
+ database_name, schema_name, table_name = (
112
+ parts[-3],
113
+ parts[-2],
114
+ parts[-1],
115
+ )
116
+ # Use the database.schema.table format
117
+ result = connection.execute(
118
+ text(
119
+ f'DESC TABLE "{database_name}"."{schema_name}"."{table_name}"'
120
+ )
121
+ )
122
+ else:
123
+ return False
124
+
125
+ # If we get here without an exception, the table exists
126
+ result.close()
127
+ return True
128
+
129
+ except Exception:
130
+ # Table doesn't exist or other error
131
+ return False