ethyca-fides 2.70.1rc3__py2.py3-none-any.whl → 2.70.3b0__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.
- {ethyca_fides-2.70.1rc3.dist-info → ethyca_fides-2.70.3b0.dist-info}/METADATA +3 -3
- {ethyca_fides-2.70.1rc3.dist-info → ethyca_fides-2.70.3b0.dist-info}/RECORD +206 -200
- fides/_version.py +3 -3
- fides/api/alembic/migrations/versions/a8e0c016afd_add_classification_benchmark_table.py +126 -0
- fides/api/alembic/migrations/versions/cd8649be3a2b_add_classifications_and_user_assigned_.py +74 -0
- fides/api/db/crud.py +30 -2
- fides/api/db/database.py +1 -1
- fides/api/db/safe_crud.py +377 -0
- fides/api/migrations/post_upgrade_index_creation.py +10 -0
- fides/api/models/conditional_dependency/__init__.py +0 -0
- fides/api/models/conditional_dependency/conditional_dependency_base.py +82 -0
- fides/api/models/detection_discovery/__init__.py +2 -0
- fides/api/models/detection_discovery/classification_benchmark.py +140 -0
- fides/api/models/detection_discovery/core.py +11 -0
- fides/api/models/manual_task/conditional_dependency.py +21 -77
- fides/api/models/policy.py +24 -0
- fides/api/models/privacy_notice.py +1 -1
- fides/api/models/privacy_request/privacy_request.py +68 -0
- fides/api/models/privacy_request/webhook.py +3 -1
- fides/api/oauth/roles.py +2 -0
- fides/api/schemas/connection_configuration/connection_secrets_mongodb.py +1 -1
- fides/api/schemas/privacy_request.py +4 -2
- fides/api/schemas/user.py +2 -2
- fides/api/service/privacy_request/dsr_package/dsr_report_builder.py +2 -2
- fides/api/service/storage/streaming/smart_open_streaming_storage.py +36 -28
- fides/api/service/storage/util.py +11 -102
- fides/api/task/manual/manual_task_utils.py +5 -6
- fides/ui-build/static/admin/404.html +1 -1
- fides/ui-build/static/admin/_next/static/chunks/155-047c3806cc41295e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/4608-a9941d0c236ebca1.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/6416-0ccadfefcdad00cc.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{7476-0b6e114658b15eaa.js → 7476-d055aa931da47ac0.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{7725-dd6736855807936a.js → 7725-fdc4298dfbea6f80.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{_app-a4e3c999afb28ee7.js → _app-de41f80e35acbde0.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-3d03cd31cd99fa07.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/{[id]-8b13bb5f7bee61c6.js → [id]-9f6c82e14ff2cad2.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{integrations-6f109ef64304ef59.js → integrations-ebc9c90fe99ee68d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{[id]-8566e3b7b2a632fa.js → [id]-f8e3f63bea43db3b.js} +1 -1
- fides/ui-build/static/admin/_next/static/{PaOA8PWIbtkQn-5P5HoNz → uJYQK3qHPl-9P-RICNMQA}/_buildManifest.js +1 -1
- fides/ui-build/static/admin/add-systems/manual.html +1 -1
- fides/ui-build/static/admin/add-systems/multiple.html +1 -1
- fides/ui-build/static/admin/add-systems.html +1 -1
- fides/ui-build/static/admin/consent/configure/add-vendors.html +1 -1
- fides/ui-build/static/admin/consent/configure.html +1 -1
- fides/ui-build/static/admin/consent/privacy-experience/[id].html +1 -1
- fides/ui-build/static/admin/consent/privacy-experience/new.html +1 -1
- fides/ui-build/static/admin/consent/privacy-experience.html +1 -1
- fides/ui-build/static/admin/consent/privacy-notices/[id].html +1 -1
- fides/ui-build/static/admin/consent/privacy-notices/new.html +1 -1
- fides/ui-build/static/admin/consent/privacy-notices.html +1 -1
- fides/ui-build/static/admin/consent/properties.html +1 -1
- fides/ui-build/static/admin/consent/reporting.html +1 -1
- fides/ui-build/static/admin/consent.html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn]/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/projects/[projectUrn].html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/projects.html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/resources/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-catalog/[systemId]/resources.html +1 -1
- fides/ui-build/static/admin/data-catalog.html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center/[monitorId]/[systemId].html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center/[monitorId].html +1 -1
- fides/ui-build/static/admin/data-discovery/action-center.html +1 -1
- fides/ui-build/static/admin/data-discovery/activity.html +1 -1
- fides/ui-build/static/admin/data-discovery/detection/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-discovery/detection.html +1 -1
- fides/ui-build/static/admin/data-discovery/discovery/[resourceUrn].html +1 -1
- fides/ui-build/static/admin/data-discovery/discovery.html +1 -1
- fides/ui-build/static/admin/datamap.html +1 -1
- fides/ui-build/static/admin/dataset/[datasetId]/[collectionName]/[...subfieldNames].html +1 -1
- fides/ui-build/static/admin/dataset/[datasetId]/[collectionName].html +1 -1
- fides/ui-build/static/admin/dataset/[datasetId].html +1 -1
- fides/ui-build/static/admin/dataset/new.html +1 -1
- fides/ui-build/static/admin/dataset.html +1 -1
- fides/ui-build/static/admin/datastore-connection/[id].html +1 -1
- fides/ui-build/static/admin/datastore-connection/new.html +1 -1
- fides/ui-build/static/admin/datastore-connection.html +1 -1
- fides/ui-build/static/admin/index.html +1 -1
- fides/ui-build/static/admin/integrations/[id].html +1 -1
- fides/ui-build/static/admin/integrations.html +1 -1
- fides/ui-build/static/admin/login/[provider].html +1 -1
- fides/ui-build/static/admin/login.html +1 -1
- fides/ui-build/static/admin/messaging/[id].html +1 -1
- fides/ui-build/static/admin/messaging/add-template.html +1 -1
- fides/ui-build/static/admin/messaging.html +1 -1
- fides/ui-build/static/admin/poc/ant-components.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/AntForm.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikAntFormItem.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikControlled.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikField.html +1 -1
- fides/ui-build/static/admin/poc/form-experiments/FormikSpreadField.html +1 -1
- fides/ui-build/static/admin/poc/forms.html +1 -1
- fides/ui-build/static/admin/poc/table-migration.html +1 -1
- fides/ui-build/static/admin/privacy-requests/[id].html +1 -1
- fides/ui-build/static/admin/privacy-requests/configure/storage.html +1 -1
- fides/ui-build/static/admin/privacy-requests/configure.html +1 -1
- fides/ui-build/static/admin/privacy-requests.html +1 -1
- fides/ui-build/static/admin/properties/[id].html +1 -1
- fides/ui-build/static/admin/properties/add-property.html +1 -1
- fides/ui-build/static/admin/properties.html +1 -1
- fides/ui-build/static/admin/reporting/datamap.html +1 -1
- fides/ui-build/static/admin/settings/about/alpha.html +1 -1
- fides/ui-build/static/admin/settings/about.html +1 -1
- fides/ui-build/static/admin/settings/consent/[configuration_id]/[purpose_id].html +1 -1
- fides/ui-build/static/admin/settings/consent.html +1 -1
- fides/ui-build/static/admin/settings/custom-fields.html +1 -1
- fides/ui-build/static/admin/settings/domain-records.html +1 -1
- fides/ui-build/static/admin/settings/domains.html +1 -1
- fides/ui-build/static/admin/settings/email-templates.html +1 -1
- fides/ui-build/static/admin/settings/locations.html +1 -1
- fides/ui-build/static/admin/settings/messaging-providers/[key].html +1 -1
- fides/ui-build/static/admin/settings/messaging-providers/new.html +1 -1
- fides/ui-build/static/admin/settings/messaging-providers.html +1 -1
- fides/ui-build/static/admin/settings/organization.html +1 -1
- fides/ui-build/static/admin/settings/privacy-requests.html +1 -1
- fides/ui-build/static/admin/settings/regulations.html +1 -1
- fides/ui-build/static/admin/systems/configure/[id]/test-datasets.html +1 -1
- fides/ui-build/static/admin/systems/configure/[id].html +1 -1
- fides/ui-build/static/admin/systems.html +1 -1
- fides/ui-build/static/admin/taxonomy.html +1 -1
- fides/ui-build/static/admin/user-management/new.html +1 -1
- fides/ui-build/static/admin/user-management/profile/[id].html +1 -1
- fides/ui-build/static/admin/user-management.html +1 -1
- fides/ui-build/static/admin/_next/static/chunks/155-88303b05c6e115a5.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4608-96f480766541124b.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/6416-18d438ff85d807f8.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-b0e3f1886de28d66.js +0 -1
- {ethyca_fides-2.70.1rc3.dist-info → ethyca_fides-2.70.3b0.dist-info}/WHEEL +0 -0
- {ethyca_fides-2.70.1rc3.dist-info → ethyca_fides-2.70.3b0.dist-info}/entry_points.txt +0 -0
- {ethyca_fides-2.70.1rc3.dist-info → ethyca_fides-2.70.3b0.dist-info}/licenses/LICENSE +0 -0
- {ethyca_fides-2.70.1rc3.dist-info → ethyca_fides-2.70.3b0.dist-info}/top_level.txt +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{1115-7888473b3dc28cda.js → 1115-90baef2a89f361ad.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{1817-ee9e29a6b8c4af50.js → 1817-dbde9966025d7970.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{2040-ab6212a3074f34f9.js → 2040-fdecc41a18e40bdc.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{3696-c25dc8d1b0e1aee1.js → 3696-90c8b336bbc46782.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{3700-dc3f05d21e2a5ff6.js → 3700-865408b36fbee782.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{3872-3514b712afd683c0.js → 3872-04d3afbfa41a7782.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{502-a40d39e615f7b664.js → 502-d3ecae97b67befbd.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{5185-33f50cf9ae17b42e.js → 5185-51eaa78e3ed6bfb7.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{5574-b8c4cba5a6938c00.js → 5574-c31ea831371610d5.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{5783-8de76df87af55e98.js → 5783-8f5713517ebc35f3.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{6084-2cd165179c428a6f.js → 6084-dc473a58c3e2889b.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{6882-447f15e87b8c48a5.js → 6882-10296485ec326e6b.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{6954-599f2de2c902e9b2.js → 6954-4b24e1731c1cc3b3.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{7158-835ba42fd881d8dd.js → 7158-04745cc8d684b2e7.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{7630-e7ea13be69c118a1.js → 7630-d0d3a0fe3f95e971.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{796-e07ac2c543f569e3.js → 796-876998c86754da97.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{8002-25fd174aec9b077b.js → 8002-ed832921ad190832.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{9226-2f960b7ca530642a.js → 9226-4a7027057f55ca2a.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{9826-c02be5882205bbbc.js → 9826-ccedc28e978ca9e1.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-939253f8daf349b2.js → manual-16ecb33f09224fbb.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{multiple-83ed7da0bb90b0a5.js → multiple-2ca59996860a33c5.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{add-systems-f90aa48500c1cbde.js → add-systems-caff552fce501f82.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/{add-vendors-46b88bda3d7b15c5.js → add-vendors-7a258b7ecd6da4b8.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{configure-c0db068d1863222f.js → configure-259ad2e10fe6f413.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-experience-92a337ee96845af2.js → privacy-experience-7e78616b7b048978.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-1fd2cda8707314f6.js → [id]-9c23fbe813c997d0.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-2987e397445713c5.js → new-0e5e38bbcfe59fd2.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{privacy-notices-59a8aff5935482ec.js → privacy-notices-3ff2fd2570f02f1c.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{consent-e5a33654a2dfaf35.js → consent-d2bf72508c3cad55.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/[projectUrn]/{[resourceUrn]-293c1f2d9aefb447.js → [resourceUrn]-06a08970907ed3f0.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/{[projectUrn]-6a4b0d49dcbf17a8.js → [projectUrn]-4a1af12d2d7cd660.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{projects-be7b385073f22414.js → projects-99573a1ee3ef8f4c.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/resources/{[resourceUrn]-18501152fa1e4f40.js → [resourceUrn]-4c84d952bb1db690.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{resources-af80fdca3bbdc82f.js → resources-6e429b7511028d60.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{data-catalog-351caadeef03876a.js → data-catalog-56fd0f3e465e52b6.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/{[monitorId]-e8ec4080d9a3e22f.js → [monitorId]-b74dfaf4f4126e57.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{action-center-1554afcb8b9da2ab.js → action-center-040813022f0890c9.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-0497f3ffdb632516.js → [resourceUrn]-844a8de0d1b506e2.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-a780390da99f3e43.js → detection-11b07cf2d91b17ef.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-1901c26cdde820da.js → [resourceUrn]-65dc7f5a2ce3eae7.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-0cea22af5929c81f.js → discovery-4d378516817cf00b.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{datamap-619f27c745188adb.js → datamap-4a05303416dcb657.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/{[...subfieldNames]-ef71f387fbbab425.js → [...subfieldNames]-d4031e438c363fff.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/{[collectionName]-d790553662caf5c3.js → [collectionName]-9463af37079762d0.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{[datasetId]-cb63db8594fe8dc1.js → [datasetId]-60a4a9eb4aab4c11.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{new-78e052c8f95110c4.js → new-910e28bce6a98f3d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{dataset-d6d7ee8bd8858a8a.js → dataset-108630926347724e.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-cc149157d290a94d.js → [id]-5119e6602507157f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{new-4c9fb068a5561658.js → new-cda4e7b5b888e17c.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{datastore-connection-a832084ce294f8af.js → datastore-connection-223c2d1ded51bfb1.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{index-269b8f81546dad66.js → index-b74d1e8608ae5b5d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{[id]-d8c5c03fb2f31d65.js → [id]-e8d2140787045acd.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{add-template-0e0c06e3c8aabe02.js → add-template-e3f93462a08251bf.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{messaging-3f15804cf9625f01.js → messaging-b5f7d6afdecd013d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/poc/{table-migration-e551fccfcaae76e7.js → table-migration-329333a88f3826eb.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/{storage-1b38b656807ed0cd.js → storage-648d775d0fce49dc.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{configure-b00532a0ad4f6927.js → configure-8f577df28ebca869.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{privacy-requests-2c82cf73d20416f2.js → privacy-requests-1eeb320867dbebf4.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{[id]-322a01e2bceab3fb.js → [id]-57a75c7e9659271a.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{add-property-777ed2d73812043d.js → add-property-8964c2300206bc89.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{consent-55e36839d219a503.js → consent-1ae1257f5b388924.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{custom-fields-26ce8fc493993765.js → custom-fields-c2f1376aca192114.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-7ddf9d992fe714a6.js → domain-records-586505df9d853f1f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domains-7c78ae51f0dd7102.js → domains-a3275554ffe8e640.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{email-templates-05ffbda19ab894b9.js → email-templates-604790638c656fbd.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-77fb85bdd0be42b5.js → locations-be2a885150adc133.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/messaging-providers/{[key]-b0d93bf478bf63ee.js → [key]-8df31428446a6a96.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/messaging-providers/{new-084f9756b9987285.js → new-af1471bf4571d5d3.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{messaging-providers-6c51ffd46bb598e7.js → messaging-providers-8d92be437793c96f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-44456bfe54ac4ad5.js → organization-3c86162afe9759df.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{privacy-requests-fbe7e8030d837aed.js → privacy-requests-8cbdfd08e0fa88fb.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{regulations-2866ac99faa5a542.js → regulations-4fe3b90747d885e5.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-a86bafe1b4e1205f.js → test-datasets-2deb6becece69d46.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-1895c6a13b543436.js → [id]-589952aa1a31c33d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{systems-d266cc062b56beb2.js → systems-916238dcc0596957.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{taxonomy-07848c232d960f6a.js → taxonomy-616f5a7cbb99e46d.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/user-management/{new-6304dad2c5fab694.js → new-629c88e90699369b.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/{[id]-ff4711db191099cd.js → [id]-98f737e735eaa0f0.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-82fcf1151e2fe2ba.js → user-management-562624e5461083ec.js} +0 -0
- /fides/ui-build/static/admin/_next/static/{PaOA8PWIbtkQn-5P5HoNz → uJYQK3qHPl-9P-RICNMQA}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,377 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains "safe" versions of CRUD operations that do NOT
|
|
3
|
+
manually manage database transactions. Transaction management is left
|
|
4
|
+
to the calling code (endpoints, services) to handle at the appropriate
|
|
5
|
+
boundary.
|
|
6
|
+
|
|
7
|
+
Use functions from this module instead of fides.api.db.crud
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from collections import defaultdict
|
|
11
|
+
from typing import Any, Dict, List, Tuple, Type, TypeVar
|
|
12
|
+
|
|
13
|
+
from fastapi import HTTPException
|
|
14
|
+
from loguru import logger as log
|
|
15
|
+
from sqlalchemy import and_, column
|
|
16
|
+
from sqlalchemy import delete as _delete
|
|
17
|
+
from sqlalchemy import or_
|
|
18
|
+
from sqlalchemy import update as _update
|
|
19
|
+
from sqlalchemy.dialects.postgresql import Insert as _insert
|
|
20
|
+
from sqlalchemy.exc import IntegrityError, SQLAlchemyError
|
|
21
|
+
from sqlalchemy.ext.asyncio import AsyncSession
|
|
22
|
+
from sqlalchemy.future import select
|
|
23
|
+
from sqlalchemy.sql import Select
|
|
24
|
+
from starlette.status import HTTP_422_UNPROCESSABLE_ENTITY
|
|
25
|
+
|
|
26
|
+
from fides.api.models.sql_models import ( # type: ignore[attr-defined]
|
|
27
|
+
CustomField,
|
|
28
|
+
CustomFieldDefinition,
|
|
29
|
+
FidesBase,
|
|
30
|
+
ResourceTypes,
|
|
31
|
+
)
|
|
32
|
+
from fides.api.util import errors
|
|
33
|
+
|
|
34
|
+
# Helps return type be linked to the type of the parameter
|
|
35
|
+
T = TypeVar("T", bound="FidesBase")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# CRUD Functions
|
|
39
|
+
async def create_resource(
|
|
40
|
+
sql_model: Type[T], resource_dict: Dict, async_session: AsyncSession
|
|
41
|
+
) -> T:
|
|
42
|
+
"""
|
|
43
|
+
Create a resource in the database.
|
|
44
|
+
|
|
45
|
+
This version does NOT manually manage transactions - transaction management
|
|
46
|
+
is left to the calling code.
|
|
47
|
+
"""
|
|
48
|
+
with log.contextualize(
|
|
49
|
+
sql_model=sql_model.__name__, fides_key=resource_dict["fides_key"]
|
|
50
|
+
):
|
|
51
|
+
|
|
52
|
+
existing_resource = await get_resource(
|
|
53
|
+
sql_model, resource_dict["fides_key"], async_session, raise_not_found=False
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
if existing_resource is not None:
|
|
57
|
+
already_exists_error = errors.AlreadyExistsError(
|
|
58
|
+
sql_model.__name__, resource_dict["fides_key"]
|
|
59
|
+
)
|
|
60
|
+
log.bind(error=already_exists_error.detail["error"]).info( # type: ignore[index]
|
|
61
|
+
"Failed to insert resource"
|
|
62
|
+
)
|
|
63
|
+
raise already_exists_error
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
log.debug("Creating resource")
|
|
67
|
+
query = _insert(sql_model.__table__).values(resource_dict)
|
|
68
|
+
await async_session.execute(query)
|
|
69
|
+
except SQLAlchemyError as e:
|
|
70
|
+
log.exception(f"Failed to create resource with error: '{e}'")
|
|
71
|
+
sa_error = errors.QueryError()
|
|
72
|
+
raise sa_error
|
|
73
|
+
|
|
74
|
+
return await get_resource(sql_model, resource_dict["fides_key"], async_session)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
async def get_custom_fields_filtered(
|
|
78
|
+
async_session: AsyncSession,
|
|
79
|
+
resource_types_to_ids: Dict[ResourceTypes, List[str]] = defaultdict(list),
|
|
80
|
+
) -> FidesBase:
|
|
81
|
+
"""
|
|
82
|
+
Utility function to construct a filtered query for custom field values based on provided mapping of
|
|
83
|
+
resource types to resource IDs.
|
|
84
|
+
|
|
85
|
+
Only custom fields with an "active" CustomFieldDefinition are returned.
|
|
86
|
+
|
|
87
|
+
This is for use in bulk querying of custom fields, to avoid multiple round trips to the db.
|
|
88
|
+
"""
|
|
89
|
+
with log.contextualize(model=CustomField):
|
|
90
|
+
try:
|
|
91
|
+
log.debug("Fetching resource")
|
|
92
|
+
query = select(
|
|
93
|
+
CustomField.resource_id,
|
|
94
|
+
CustomField.value,
|
|
95
|
+
CustomFieldDefinition.resource_type,
|
|
96
|
+
CustomFieldDefinition.name,
|
|
97
|
+
CustomFieldDefinition.field_type,
|
|
98
|
+
).join(
|
|
99
|
+
CustomFieldDefinition,
|
|
100
|
+
CustomFieldDefinition.id == CustomField.custom_field_definition_id,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
criteria = [
|
|
104
|
+
and_(
|
|
105
|
+
CustomFieldDefinition.resource_type == resource_type.value,
|
|
106
|
+
CustomField.resource_id.in_(resource_ids),
|
|
107
|
+
# pylint: disable=singleton-comparison
|
|
108
|
+
CustomFieldDefinition.active == True,
|
|
109
|
+
)
|
|
110
|
+
for resource_type, resource_ids in resource_types_to_ids.items()
|
|
111
|
+
]
|
|
112
|
+
query = query.where(or_(False, *criteria))
|
|
113
|
+
result = await async_session.execute(query)
|
|
114
|
+
return result.mappings().all()
|
|
115
|
+
except SQLAlchemyError as e:
|
|
116
|
+
sa_error = errors.QueryError()
|
|
117
|
+
log.exception(f"Failed to fetch custom fields with error: '{e}'")
|
|
118
|
+
raise sa_error
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
async def get_resource(
|
|
122
|
+
sql_model: Type[T],
|
|
123
|
+
fides_key: str,
|
|
124
|
+
async_session: AsyncSession,
|
|
125
|
+
raise_not_found: bool = True,
|
|
126
|
+
) -> T:
|
|
127
|
+
"""
|
|
128
|
+
Get a resource from the database by its FidesKey.
|
|
129
|
+
|
|
130
|
+
Returns a SQLAlchemy model of that resource.
|
|
131
|
+
|
|
132
|
+
This version does NOT manually manage transactions - transaction management
|
|
133
|
+
is left to the calling code.
|
|
134
|
+
"""
|
|
135
|
+
with log.contextualize(sql_model=sql_model.__name__, fides_key=fides_key):
|
|
136
|
+
try:
|
|
137
|
+
log.debug("Fetching resource")
|
|
138
|
+
query = select(sql_model).where(sql_model.fides_key == fides_key)
|
|
139
|
+
result = await async_session.execute(query)
|
|
140
|
+
except SQLAlchemyError as e:
|
|
141
|
+
sa_error = errors.QueryError()
|
|
142
|
+
log.exception(f"Failed to fetch resource with error: '{e}'")
|
|
143
|
+
raise sa_error
|
|
144
|
+
|
|
145
|
+
sql_resource = result.scalars().first()
|
|
146
|
+
if sql_resource is None and raise_not_found:
|
|
147
|
+
not_found_error = errors.NotFoundError(sql_model.__name__, fides_key)
|
|
148
|
+
log.bind(error=not_found_error.detail["error"]).info("Resource not found") # type: ignore[index]
|
|
149
|
+
raise not_found_error
|
|
150
|
+
|
|
151
|
+
return sql_resource
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
async def get_resource_with_custom_fields(
|
|
155
|
+
sql_model: Type[T], fides_key: str, async_session: AsyncSession
|
|
156
|
+
) -> Dict[str, Any]:
|
|
157
|
+
"""Get a resource from the database by its FidesKey including it's custom fields.
|
|
158
|
+
|
|
159
|
+
Returns a dictionary of that resource.
|
|
160
|
+
|
|
161
|
+
This version does NOT manually manage transactions - transaction management
|
|
162
|
+
is left to the calling code.
|
|
163
|
+
"""
|
|
164
|
+
resource: T = await get_resource(sql_model, fides_key, async_session)
|
|
165
|
+
resource_dict = resource.__dict__
|
|
166
|
+
resource_dict.pop("_sa_instance_state", None)
|
|
167
|
+
|
|
168
|
+
with log.contextualize(sql_model=sql_model.__name__, fides_key=fides_key):
|
|
169
|
+
try:
|
|
170
|
+
log.debug("Fetching custom fields for resource")
|
|
171
|
+
query = (
|
|
172
|
+
select(CustomFieldDefinition.name, CustomField.value)
|
|
173
|
+
.join(
|
|
174
|
+
CustomField,
|
|
175
|
+
CustomField.custom_field_definition_id == CustomFieldDefinition.id,
|
|
176
|
+
)
|
|
177
|
+
.where(
|
|
178
|
+
(CustomField.resource_id == resource.fides_key)
|
|
179
|
+
& ( # pylint: disable=singleton-comparison
|
|
180
|
+
CustomFieldDefinition.active == True
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
result = await async_session.execute(query)
|
|
185
|
+
except SQLAlchemyError as e:
|
|
186
|
+
sa_error = errors.QueryError()
|
|
187
|
+
log.exception(f"Failed to fetch custom fields with error: '{e}'")
|
|
188
|
+
raise sa_error
|
|
189
|
+
|
|
190
|
+
custom_fields = result.mappings().all()
|
|
191
|
+
|
|
192
|
+
if not custom_fields:
|
|
193
|
+
return resource_dict
|
|
194
|
+
|
|
195
|
+
for field in custom_fields:
|
|
196
|
+
if field["name"] in resource_dict:
|
|
197
|
+
resource_dict[field["name"]] = (
|
|
198
|
+
f"{resource_dict[field['name']]}, {', '.join(field['value'])}"
|
|
199
|
+
)
|
|
200
|
+
else:
|
|
201
|
+
resource_dict[field["name"]] = ", ".join(field["value"])
|
|
202
|
+
|
|
203
|
+
return resource_dict
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
async def list_resource(sql_model: Type[T], async_session: AsyncSession) -> List[T]:
|
|
207
|
+
"""
|
|
208
|
+
Get a list of all of the resources of this type from the database.
|
|
209
|
+
|
|
210
|
+
Returns a list of SQLAlchemy models of that resource type.
|
|
211
|
+
|
|
212
|
+
This version does NOT manually manage transactions - transaction management
|
|
213
|
+
is left to the calling code.
|
|
214
|
+
"""
|
|
215
|
+
query = select(sql_model)
|
|
216
|
+
return await list_resource_query(async_session, query, sql_model)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
async def list_resource_query(
|
|
220
|
+
async_session: AsyncSession, query: Select, sql_model: Type[T]
|
|
221
|
+
) -> List[T]:
|
|
222
|
+
"""
|
|
223
|
+
Utility function to wrap a select query in generic "list_resource" execution handling.
|
|
224
|
+
Wrapping includes execution against the DB session, logging and error handling.
|
|
225
|
+
|
|
226
|
+
This version does NOT manually manage transactions - transaction management
|
|
227
|
+
is left to the calling code.
|
|
228
|
+
"""
|
|
229
|
+
|
|
230
|
+
with log.contextualize(sql_model=sql_model.__name__):
|
|
231
|
+
try:
|
|
232
|
+
log.debug("Fetching resources")
|
|
233
|
+
result = await async_session.execute(query)
|
|
234
|
+
sql_resources = result.scalars().all()
|
|
235
|
+
except SQLAlchemyError as e:
|
|
236
|
+
log.exception(f"Failed to fetch resources with error: '{e}'")
|
|
237
|
+
sa_error = errors.QueryError()
|
|
238
|
+
raise sa_error
|
|
239
|
+
|
|
240
|
+
return sql_resources
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
async def update_resource(
|
|
244
|
+
sql_model: Type[T], resource_dict: Dict, async_session: AsyncSession
|
|
245
|
+
) -> Dict:
|
|
246
|
+
"""
|
|
247
|
+
Update a resource in the database by its fides_key.
|
|
248
|
+
|
|
249
|
+
This version does NOT manually manage transactions - transaction management
|
|
250
|
+
is left to the calling code.
|
|
251
|
+
"""
|
|
252
|
+
|
|
253
|
+
with log.contextualize(
|
|
254
|
+
sql_model=sql_model.__name__, fides_key=resource_dict["fides_key"]
|
|
255
|
+
):
|
|
256
|
+
await get_resource(sql_model, resource_dict["fides_key"], async_session)
|
|
257
|
+
|
|
258
|
+
try:
|
|
259
|
+
log.debug("Updating resource")
|
|
260
|
+
await async_session.execute(
|
|
261
|
+
_update(sql_model.__table__)
|
|
262
|
+
.where(sql_model.fides_key == resource_dict["fides_key"])
|
|
263
|
+
.values(resource_dict)
|
|
264
|
+
)
|
|
265
|
+
except SQLAlchemyError as e:
|
|
266
|
+
log.exception(f"Failed to update resource with error: '{e}'")
|
|
267
|
+
sa_error = errors.QueryError()
|
|
268
|
+
raise sa_error
|
|
269
|
+
|
|
270
|
+
return await get_resource(sql_model, resource_dict["fides_key"], async_session)
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
async def upsert_resources(
|
|
274
|
+
sql_model: Type[T], resource_dicts: List[Dict], async_session: AsyncSession
|
|
275
|
+
) -> Tuple[int, int]:
|
|
276
|
+
"""
|
|
277
|
+
Insert new resources into the database. If a resource already exists,
|
|
278
|
+
update it by it's fides_key.
|
|
279
|
+
|
|
280
|
+
Returns a Tuple containing the counts of inserted and updated rows.
|
|
281
|
+
|
|
282
|
+
This version does NOT manually manage transactions - transaction management
|
|
283
|
+
is left to the calling code.
|
|
284
|
+
"""
|
|
285
|
+
|
|
286
|
+
with log.contextualize(
|
|
287
|
+
sql_model=sql_model.__name__,
|
|
288
|
+
fides_keys=[resource["fides_key"] for resource in resource_dicts],
|
|
289
|
+
):
|
|
290
|
+
try:
|
|
291
|
+
log.debug("Upserting resources")
|
|
292
|
+
insert_stmt = (
|
|
293
|
+
_insert(sql_model.__table__)
|
|
294
|
+
.values(resource_dicts)
|
|
295
|
+
.returning(
|
|
296
|
+
(column("xmax") == 0), # Row was inserted
|
|
297
|
+
(column("xmax") != 0), # Row was updated
|
|
298
|
+
)
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
excluded = dict(insert_stmt.excluded.items()) # type: ignore[attr-defined]
|
|
302
|
+
excluded.pop("id", None) # If updating, don't update the "id"
|
|
303
|
+
|
|
304
|
+
result = await async_session.execute(
|
|
305
|
+
insert_stmt.on_conflict_do_update(
|
|
306
|
+
index_elements=["fides_key"],
|
|
307
|
+
set_=excluded,
|
|
308
|
+
)
|
|
309
|
+
)
|
|
310
|
+
|
|
311
|
+
inserts, updates = 0, 0
|
|
312
|
+
for xmax in result:
|
|
313
|
+
inserts += 1 if xmax[0] else 0
|
|
314
|
+
updates += 1 if xmax[1] else 0
|
|
315
|
+
|
|
316
|
+
return (inserts, updates)
|
|
317
|
+
|
|
318
|
+
except SQLAlchemyError as e:
|
|
319
|
+
log.exception(f"Failed to upsert resources with error: '{e}'")
|
|
320
|
+
sa_error = errors.QueryError()
|
|
321
|
+
raise sa_error
|
|
322
|
+
|
|
323
|
+
|
|
324
|
+
async def delete_resource(
|
|
325
|
+
sql_model: Type[T], fides_key: str, async_session: AsyncSession
|
|
326
|
+
) -> T:
|
|
327
|
+
"""
|
|
328
|
+
Delete a resource by its fides_key.
|
|
329
|
+
|
|
330
|
+
If the resource has child keys referring to it, also delete those.
|
|
331
|
+
|
|
332
|
+
This version does NOT manually manage transactions - transaction management
|
|
333
|
+
is left to the calling code.
|
|
334
|
+
"""
|
|
335
|
+
|
|
336
|
+
with log.contextualize(sql_model=sql_model.__name__, fides_key=fides_key):
|
|
337
|
+
sql_resource = await get_resource(sql_model, fides_key, async_session)
|
|
338
|
+
|
|
339
|
+
try:
|
|
340
|
+
# Automatically delete related resources if they are CTL objects
|
|
341
|
+
if hasattr(sql_model, "parent_key"):
|
|
342
|
+
log.debug("Deleting resource and its children")
|
|
343
|
+
query = (
|
|
344
|
+
_delete(sql_model.__table__)
|
|
345
|
+
.where(
|
|
346
|
+
or_(
|
|
347
|
+
sql_model.fides_key == fides_key,
|
|
348
|
+
sql_model.fides_key.like(f"{fides_key}.%"),
|
|
349
|
+
)
|
|
350
|
+
)
|
|
351
|
+
.execution_options(synchronize_session=False)
|
|
352
|
+
)
|
|
353
|
+
else:
|
|
354
|
+
log.debug("Deleting resource")
|
|
355
|
+
query = _delete(sql_model.__table__).where(
|
|
356
|
+
sql_model.fides_key == fides_key
|
|
357
|
+
)
|
|
358
|
+
await async_session.execute(query)
|
|
359
|
+
except IntegrityError as err:
|
|
360
|
+
raw_error_text: str = err.orig.args[0]
|
|
361
|
+
|
|
362
|
+
if "violates foreign key constraint" in raw_error_text:
|
|
363
|
+
error_message = "Failed to delete resource! Foreign key constraint found, try deleting related resources first."
|
|
364
|
+
else:
|
|
365
|
+
error_message = "Failed to delete resource!"
|
|
366
|
+
|
|
367
|
+
log.bind(error="SQL Query integrity error!").error(raw_error_text)
|
|
368
|
+
raise HTTPException(
|
|
369
|
+
status_code=HTTP_422_UNPROCESSABLE_ENTITY,
|
|
370
|
+
detail=error_message,
|
|
371
|
+
)
|
|
372
|
+
except SQLAlchemyError as e:
|
|
373
|
+
log.exception(f"Failed to delete resource with error: '{e}'")
|
|
374
|
+
sa_error = errors.QueryError()
|
|
375
|
+
raise sa_error
|
|
376
|
+
|
|
377
|
+
return sql_resource
|
|
@@ -153,6 +153,16 @@ TABLE_OBJECT_MAP: Dict[str, List[Dict[str, str]]] = {
|
|
|
153
153
|
"statement": "CREATE INDEX CONCURRENTLY ix_stagedresource_system_vendor_consent ON stagedresource (system_id, vendor_id, (meta->>'consent_aggregated'))",
|
|
154
154
|
"type": "index",
|
|
155
155
|
},
|
|
156
|
+
{
|
|
157
|
+
"name": "idx_stagedresource_user_categories_gin",
|
|
158
|
+
"statement": "CREATE INDEX CONCURRENTLY idx_stagedresource_user_categories_gin ON stagedresource USING GIN (user_assigned_data_categories)",
|
|
159
|
+
"type": "index",
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"name": "idx_stagedresource_classifications_gin",
|
|
163
|
+
"statement": "CREATE INDEX CONCURRENTLY idx_stagedresource_classifications_gin ON stagedresource USING GIN (classifications)",
|
|
164
|
+
"type": "index",
|
|
165
|
+
},
|
|
156
166
|
],
|
|
157
167
|
}
|
|
158
168
|
|
|
File without changes
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Any, Optional
|
|
3
|
+
|
|
4
|
+
from sqlalchemy import Column, Integer, String
|
|
5
|
+
from sqlalchemy.dialects.postgresql import JSONB
|
|
6
|
+
from sqlalchemy.orm import Session
|
|
7
|
+
|
|
8
|
+
from fides.api.db.base_class import Base
|
|
9
|
+
from fides.api.db.util import EnumColumn
|
|
10
|
+
from fides.api.task.conditional_dependencies.schemas import (
|
|
11
|
+
Condition,
|
|
12
|
+
ConditionGroup,
|
|
13
|
+
ConditionLeaf,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ConditionalDependencyType(str, Enum):
|
|
18
|
+
"""Shared enum for conditional dependency node types."""
|
|
19
|
+
|
|
20
|
+
leaf = "leaf"
|
|
21
|
+
group = "group"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class ConditionalDependencyBase(Base):
|
|
25
|
+
"""Abstract base class for all conditional dependency models."""
|
|
26
|
+
|
|
27
|
+
__abstract__ = True
|
|
28
|
+
|
|
29
|
+
# Tree structure - parent_id defined in concrete classes for proper foreign keys
|
|
30
|
+
condition_type = Column(EnumColumn(ConditionalDependencyType), nullable=False)
|
|
31
|
+
|
|
32
|
+
# Condition details (for leaf nodes)
|
|
33
|
+
field_address = Column(String(255), nullable=True) # For leaf conditions
|
|
34
|
+
operator = Column(String, nullable=True) # For leaf conditions
|
|
35
|
+
value = Column(JSONB, nullable=True) # For leaf conditions
|
|
36
|
+
logical_operator = Column(String, nullable=True) # 'and' or 'or' for groups
|
|
37
|
+
|
|
38
|
+
# Ordering
|
|
39
|
+
sort_order = Column(Integer, nullable=False, default=0)
|
|
40
|
+
|
|
41
|
+
def to_condition_leaf(self) -> ConditionLeaf:
|
|
42
|
+
"""Convert to ConditionLeaf if this is a leaf condition"""
|
|
43
|
+
if self.condition_type != "leaf":
|
|
44
|
+
raise ValueError("Cannot convert group condition to leaf")
|
|
45
|
+
|
|
46
|
+
return ConditionLeaf(
|
|
47
|
+
field_address=self.field_address, operator=self.operator, value=self.value
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
def to_condition_group(self) -> ConditionGroup:
|
|
51
|
+
"""Convert to ConditionGroup if this is a group condition"""
|
|
52
|
+
if self.condition_type != "group":
|
|
53
|
+
raise ValueError("Cannot convert leaf condition to group")
|
|
54
|
+
|
|
55
|
+
# Recursively build children
|
|
56
|
+
child_conditions = []
|
|
57
|
+
children_list = [child for child in self.children] # type: ignore[attr-defined]
|
|
58
|
+
for child in sorted(children_list, key=lambda x: x.sort_order):
|
|
59
|
+
if child.condition_type == "leaf":
|
|
60
|
+
child_conditions.append(child.to_condition_leaf())
|
|
61
|
+
else:
|
|
62
|
+
child_conditions.append(child.to_condition_group())
|
|
63
|
+
|
|
64
|
+
return ConditionGroup(
|
|
65
|
+
logical_operator=self.logical_operator, conditions=child_conditions
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def get_root_condition(
|
|
70
|
+
cls, db: Session, *args: Any, **kwargs: Any
|
|
71
|
+
) -> Optional[Condition]:
|
|
72
|
+
"""Get the root condition for a parent entity - implemented by subclasses
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
db: Database session
|
|
76
|
+
*args: Additional positional arguments specific to each implementation
|
|
77
|
+
**kwargs: Additional keyword arguments specific to each implementation
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
Optional[Condition]: Root condition or None if not found
|
|
81
|
+
"""
|
|
82
|
+
raise NotImplementedError("Subclasses must implement get_root_condition")
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from .classification_benchmark import ClassificationBenchmark
|
|
1
2
|
from .core import (
|
|
2
3
|
DiffStatus,
|
|
3
4
|
MonitorConfig,
|
|
@@ -18,6 +19,7 @@ from .monitor_task import (
|
|
|
18
19
|
)
|
|
19
20
|
|
|
20
21
|
__all__ = [
|
|
22
|
+
"ClassificationBenchmark",
|
|
21
23
|
"DiffStatus",
|
|
22
24
|
"MonitorConfig",
|
|
23
25
|
"MonitorExecution",
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Database model for classification benchmark results.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from datetime import datetime
|
|
8
|
+
from typing import Optional
|
|
9
|
+
|
|
10
|
+
from sqlalchemy import ARRAY, Column, ForeignKey, String
|
|
11
|
+
from sqlalchemy.dialects.postgresql import JSONB
|
|
12
|
+
from sqlalchemy.ext.declarative import declared_attr
|
|
13
|
+
from sqlalchemy.ext.mutable import MutableDict
|
|
14
|
+
from sqlalchemy.orm import Query, RelationshipProperty, Session, relationship
|
|
15
|
+
|
|
16
|
+
from fides.api.db.base_class import Base, FidesBase
|
|
17
|
+
from fides.api.models.detection_discovery.core import MonitorConfig
|
|
18
|
+
from fides.api.models.sql_models import Dataset # type: ignore[attr-defined]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ClassificationBenchmark(Base, FidesBase):
|
|
22
|
+
"""
|
|
23
|
+
Database model for storing classification benchmark results.
|
|
24
|
+
|
|
25
|
+
This model stores the results of classification accuracy benchmarks that compare
|
|
26
|
+
system-assigned classifications on staged resources with ground truth data from datasets.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
@declared_attr
|
|
30
|
+
def __tablename__(self) -> str:
|
|
31
|
+
return "classification_benchmark"
|
|
32
|
+
|
|
33
|
+
# Foreign key relationships
|
|
34
|
+
monitor_config_key = Column(
|
|
35
|
+
String,
|
|
36
|
+
ForeignKey(MonitorConfig.key, ondelete="CASCADE"),
|
|
37
|
+
nullable=False,
|
|
38
|
+
index=True,
|
|
39
|
+
)
|
|
40
|
+
dataset_fides_key = Column(
|
|
41
|
+
String,
|
|
42
|
+
ForeignKey(Dataset.fides_key, ondelete="CASCADE"),
|
|
43
|
+
nullable=False,
|
|
44
|
+
index=True,
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# Resource URNs (array of strings)
|
|
48
|
+
resource_urns = Column(
|
|
49
|
+
ARRAY(String),
|
|
50
|
+
nullable=False,
|
|
51
|
+
server_default="{}",
|
|
52
|
+
default=list,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
# Overall accuracy metrics stored as JSONB
|
|
56
|
+
overall_metrics = Column(
|
|
57
|
+
MutableDict.as_mutable(JSONB),
|
|
58
|
+
nullable=True,
|
|
59
|
+
server_default=None,
|
|
60
|
+
default=None,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# Field-level accuracy details stored as JSONB array
|
|
64
|
+
field_accuracy_details = Column(
|
|
65
|
+
ARRAY(JSONB),
|
|
66
|
+
nullable=False,
|
|
67
|
+
server_default="{}",
|
|
68
|
+
default=list,
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# Status and messages for tracking benchmark execution
|
|
72
|
+
status = Column(
|
|
73
|
+
String,
|
|
74
|
+
nullable=False,
|
|
75
|
+
server_default="'pending'",
|
|
76
|
+
default="pending",
|
|
77
|
+
index=True,
|
|
78
|
+
)
|
|
79
|
+
messages = Column(
|
|
80
|
+
ARRAY(String),
|
|
81
|
+
nullable=False,
|
|
82
|
+
server_default="{}",
|
|
83
|
+
default=list,
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
# Relationships
|
|
87
|
+
monitor_config: RelationshipProperty[MonitorConfig] = relationship(
|
|
88
|
+
MonitorConfig,
|
|
89
|
+
foreign_keys=[monitor_config_key],
|
|
90
|
+
primaryjoin="ClassificationBenchmark.monitor_config_key == MonitorConfig.key",
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
dataset: RelationshipProperty[Dataset] = relationship(
|
|
94
|
+
Dataset,
|
|
95
|
+
foreign_keys=[dataset_fides_key],
|
|
96
|
+
primaryjoin="ClassificationBenchmark.dataset_fides_key == Dataset.fides_key",
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
@classmethod
|
|
100
|
+
def list_benchmarks(
|
|
101
|
+
cls,
|
|
102
|
+
db: Session,
|
|
103
|
+
monitor_config_key: Optional[str] = None,
|
|
104
|
+
dataset_fides_key: Optional[str] = None,
|
|
105
|
+
created_after: Optional[datetime] = None,
|
|
106
|
+
created_before: Optional[datetime] = None,
|
|
107
|
+
status: Optional[str] = None,
|
|
108
|
+
) -> Query:
|
|
109
|
+
"""
|
|
110
|
+
Get a filtered query for benchmarks.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
db: Database session
|
|
114
|
+
monitor_config_key: Filter by monitor config key
|
|
115
|
+
dataset_fides_key: Filter by dataset fides key
|
|
116
|
+
created_after: Filter by creation date (inclusive)
|
|
117
|
+
created_before: Filter by creation date (exclusive)
|
|
118
|
+
status: Filter by status
|
|
119
|
+
Returns:
|
|
120
|
+
Filtered query (not executed)
|
|
121
|
+
"""
|
|
122
|
+
# Build the query with filters
|
|
123
|
+
query = db.query(cls)
|
|
124
|
+
|
|
125
|
+
# Apply filters
|
|
126
|
+
if monitor_config_key:
|
|
127
|
+
query = query.filter(cls.monitor_config_key == monitor_config_key)
|
|
128
|
+
if dataset_fides_key:
|
|
129
|
+
query = query.filter(cls.dataset_fides_key == dataset_fides_key)
|
|
130
|
+
if created_after:
|
|
131
|
+
query = query.filter(cls.created_at >= created_after)
|
|
132
|
+
if created_before:
|
|
133
|
+
query = query.filter(cls.created_at < created_before)
|
|
134
|
+
if status:
|
|
135
|
+
query = query.filter(cls.status == status)
|
|
136
|
+
|
|
137
|
+
# Apply sorting
|
|
138
|
+
query = query.order_by(cls.created_at.desc())
|
|
139
|
+
|
|
140
|
+
return query
|
|
@@ -599,6 +599,17 @@ class StagedResource(Base):
|
|
|
599
599
|
"vendor_id",
|
|
600
600
|
text("(meta->>'consent_aggregated')"),
|
|
601
601
|
),
|
|
602
|
+
# GIN indices for array operations (&&, @>, <@)
|
|
603
|
+
Index(
|
|
604
|
+
"idx_stagedresource_classifications_gin",
|
|
605
|
+
"classifications",
|
|
606
|
+
postgresql_using="gin",
|
|
607
|
+
),
|
|
608
|
+
Index(
|
|
609
|
+
"idx_stagedresource_user_categories_gin",
|
|
610
|
+
"user_assigned_data_categories",
|
|
611
|
+
postgresql_using="gin",
|
|
612
|
+
),
|
|
602
613
|
)
|
|
603
614
|
|
|
604
615
|
@classmethod
|