ethyca-fides 2.58.2rc0__py2.py3-none-any.whl → 2.58.3b1__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.
- {ethyca_fides-2.58.2rc0.dist-info → ethyca_fides-2.58.3b1.dist-info}/METADATA +20 -11
- {ethyca_fides-2.58.2rc0.dist-info → ethyca_fides-2.58.3b1.dist-info}/RECORD +225 -216
- {ethyca_fides-2.58.2rc0.dist-info → ethyca_fides-2.58.3b1.dist-info}/WHEEL +1 -1
- {ethyca_fides-2.58.2rc0.dist-info → ethyca_fides-2.58.3b1.dist-info}/entry_points.txt +0 -1
- fides/_version.py +3 -3
- fides/api/alembic/migrations/versions/67d01c4e124e_add_reject_all_mechanism_to_privacy_.py +56 -0
- fides/api/alembic/migrations/versions/6e565c16dae1_add_tcf_publisher_restrictions.py +107 -0
- fides/api/alembic/migrations/versions/9288f729cac4_add_tcf_configuration_fk_to_experience_.py +62 -0
- fides/api/alembic/migrations/versions/99c603c1b8f9_add_password_login_enabled_and_totp_secret_to_fidesuser.py +45 -0
- fides/api/api/deps.py +38 -2
- fides/api/api/v1/endpoints/user_endpoints.py +8 -12
- fides/api/cryptography/identity_salt.py +12 -13
- fides/api/custom_types.py +6 -1
- fides/api/db/base.py +5 -0
- fides/api/db/system.py +1 -3
- fides/api/migrations/hash_migration_job.py +2 -2
- fides/api/models/attachment.py +80 -11
- fides/api/models/comment.py +45 -15
- fides/api/models/detection_discovery.py +31 -0
- fides/api/models/fides_user.py +26 -9
- fides/api/models/fides_user_invite.py +2 -0
- fides/api/models/privacy_experience.py +68 -0
- fides/api/models/privacy_request/privacy_request.py +23 -6
- fides/api/models/tcf_publisher_restrictions.py +465 -0
- fides/api/schemas/connection_configuration/connection_config.py +30 -16
- fides/api/schemas/user.py +5 -1
- fides/api/service/deps.py +9 -0
- fides/api/service/storage/s3.py +14 -1
- fides/api/task/graph_task.py +1 -1
- fides/api/util/collection_util.py +48 -9
- fides/cli/commands/pull.py +77 -13
- fides/config/database_settings.py +46 -0
- fides/core/api.py +2 -1
- fides/core/pull.py +38 -7
- fides/service/error_handling/__init__.py +0 -0
- fides/service/error_handling/error_handler.py +202 -0
- fides/service/user/__init__.py +0 -0
- fides/service/user/user_service.py +140 -0
- fides/ui-build/static/admin/404.html +1 -1
- fides/ui-build/static/admin/_next/static/Phr0wJQrVglnj5svYDeUY/_buildManifest.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/1376-f2e68d1cfdacfd48.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/146-9567a3d2f9e21b83.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/1817-5c32a7592d18a859.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/1904-689b67a43153d56c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/2479-bf5586191c72fb2b.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/3244-c728351365b77871.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/3702-70f90912a76ecef3.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{3855-b6b7865dedd7bc2a.js → 3855-c02445526594fc1f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{3872-4e053c20d546f027.js → 3872-0b61e674a790491b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/401-839481005c1ba95e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{4060-8d165e1236ea521a.js → 4060-fd2f97afa5ba80d4.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/4121-28beb1c0ce3330b6.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/4481-865277e9623e6014.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/5102-626b9ff42e904276.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{5480-f49696df5e8ae500.js → 5480-ff3e05a015ee2799.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{5487-3ad50d21cdbc9209.js → 5487-f281d138cb89b5c9.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/5973-28d2af853d8498d7.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{6372-ca9c12ac8902365b.js → 6372-8479ec83d73af02b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/6395-4224d6d26d1e8bb7.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{6853-8941824350c3c1a8.js → 6853-3562089cc16a6799.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{6954-3b887fb444f9228c.js → 6954-23438f7f9729748b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{7751-a8f31c062d4cb09d.js → 7751-95349028f1ee3fb5.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{79-f9b948ebb186900f.js → 79-488979db197d250c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/8934-ffa2b0509bc7a845.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/9572-82484a4dd5ebc57e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/9767-d1d54cb9b74c0693.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/9951-8425f24ce61496bd.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{main-24f422f93845a596.js → main-090643377c8254e6.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{main-app-94a0711202e08b15.js → main-app-59156a9331ac7bce.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{_app-fc89ce7bed454c84.js → _app-fadef5a6a65d3ec4.js} +8 -8
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/manual-58e9256e86916ecd.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{multiple-ba975134a85588f8.js → multiple-2392e3a101fae073.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{add-systems-d258f0c25fa020bf.js → add-systems-661ff00f91fe62df.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/configure/{add-vendors-b34e5324461d0c87.js → add-vendors-7dfe37b9f0ff9a3c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{configure-723cc3d4f5740ea6.js → configure-dba7848b760ba227.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/[id]-78de4bde88e18b0f.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{new-a9d9402c219d13e5.js → new-c02b14c50b19bd91.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience-7dcbcd6f74029d90.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-c27810fc7d8ad4c0.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{reporting-788cf0e34829af46.js → reporting-71c8a8557a0fb316.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{data-catalog-900004e402c31797.js → data-catalog-6dae602b509b00b5.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-f149a0df946c05db.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-c5b3283cddb68c19.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-38476c697da53480.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-36ce3e322670e082.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/detection/{[resourceUrn]-054ca46a782e99a5.js → [resourceUrn]-1bcaa606739ea1c5.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{detection-b75dd3e4306ac18e.js → detection-22f55dc12354b4c6.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/discovery/{[resourceUrn]-1da20aeb6fc995e4.js → [resourceUrn]-86111c25dc30ef2b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/{discovery-cad50b0cc6d1050c.js → discovery-853be75f08b9ab5c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{datamap-8cb714cdd44ac40e.js → datamap-423172d31de8e9c6.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{new-803c1b577ab17ae3.js → new-afb5d98731bc1bb1.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{dataset-fa743ddc7f89d76b.js → dataset-13e06b0a4a8c114c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-bbe1ca2793798e6b.js → [id]-d796cf4c25d0b988.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{new-abc17fef69cd951b.js → new-814849a549132ffa.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-be295129568a929c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{index-bfaacdb55a5a6c9f.js → index-b0926c4083d4ac88.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-dadebdf2d6fbcc64.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/messaging-eef56c95b08aa24c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-b1d39bb680cfd6d2.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-ea8c82f36520e542.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-2a56dc41a292a468.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{privacy-requests-d85c0d16ba09ba35.js → privacy-requests-f394d59981a4f50c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/reporting/{datamap-afedc48ef4e7f858.js → datamap-4e04234aa5dff9f8.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-b359f061d3b9d455.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-dcf102352d4d4d98.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/domains-2f03e981234c40ad.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-a08693d0d1e10bc8.js → organization-b208f9fd45ebb829.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-151571cff4e85894.js → test-datasets-37c8930711ca2b8e.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]-dd053a3bf2a9ca6c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{systems-abd68fc5ddde5482.js → systems-4f07a39a7def714a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-ec35b1f86d536b75.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/{new-be690621a944bfe2.js → new-082c3156175f9267.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-963b0dbbf93b9e49.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-6c9ad62479a7d03e.js → user-management-cb40808c1509f191.js} +1 -1
- fides/ui-build/static/admin/_next/static/css/687135955af5b7e1.css +1 -0
- fides/ui-build/static/admin/_next/static/css/b89fc4b36b501cf6.css +1 -0
- 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/ant-poc.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/images/connector-logos/website.svg +10 -0
- 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/lib/fides-ext-gpp.js +1 -1
- fides/ui-build/static/admin/lib/fides-headless.js +1 -1
- fides/ui-build/static/admin/lib/fides-tcf.js +4 -4
- fides/ui-build/static/admin/lib/fides.js +3 -3
- 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/privacy-requests/[id].html +1 -1
- fides/ui-build/static/admin/privacy-requests/configure/messaging.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.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/organization.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/api/service/user/fides_user_service.py +0 -128
- fides/ui-build/static/admin/_next/static/chunks/1150-035a721a04f4451e.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/1376-5cea5ef9362215e8.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/2397-ee53235fb21b5e97.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/3412-7ec8751b8182e1bf.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/355-8a77c9a1cd027f2e.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4450-36234280bee624ff.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4481-aab99ff80f707473.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4723-0a3c5e2ce143a7d0.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/5258-0658dc2274df6832.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/5973-52aee296edc44f7e.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/6315-fa1519cdf080f42d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7453-39761c38da31257e.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/7980-4bd08957448dea32.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/8499-43606100edf42fdf.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9046-04bd7becea207cb1.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9767-1a23925d2cb27b51.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9999-637e0e5341f15f4a.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/manual-9acaab973dfe86e2.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/[id]-fb75fa0aea77678d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience-c946b33b0322b8ad.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-ea57f9d6ad17e957.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/[systemId]-b66831fdafcdf67c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-5f9ef1f99818117c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-d001337d1bb73bd1.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-11b3ce9f61d9bfe9.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-a78a73b65929853a.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-d4329043219fed9b.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/messaging-1e60754abec1ee6b.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/[id]-fe765154315782cf.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-f5f7a8069909ef24.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-9f7eaad05e5b9292.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-89524101b7279f6e.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/custom-fields-52d030b1db2ca1b9.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/domains-24cba38685dc872c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]-4f5a28226575c976.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/taxonomy-16b4d75c49276add.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-78eaf933f755bfe8.js +0 -1
- fides/ui-build/static/admin/_next/static/css/113d823fe71f6af0.css +0 -1
- fides/ui-build/static/admin/_next/static/css/15fb7d4837a1de34.css +0 -1
- fides/ui-build/static/admin/_next/static/o0mKeH0cB6eAYV6qOlVD0/_buildManifest.js +0 -1
- {ethyca_fides-2.58.2rc0.dist-info → ethyca_fides-2.58.3b1.dist-info/licenses}/LICENSE +0 -0
- {ethyca_fides-2.58.2rc0.dist-info → ethyca_fides-2.58.3b1.dist-info}/top_level.txt +0 -0
- /fides/ui-build/static/admin/_next/static/{o0mKeH0cB6eAYV6qOlVD0 → Phr0wJQrVglnj5svYDeUY}/_ssgManifest.js +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{1100-053fc6b76c65a00f.js → 1100-45c0634b4f51d10c.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{3086-b5054ec2c75700b9.js → 3086-d1ba90bc6ac9174b.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{5826-e5dcb4e68cfe6289.js → 5826-1f4f74bf3b5348e4.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{8433-b3008ecaf9834e7f.js → 8433-c4c765833ab9cc07.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{9282-2bfbdca45e84e810.js → 9282-1a48b10b114d01f4.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{9327-2cba327d10586683.js → 9327-4970d356f7000c0b.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{404-b202c0d8f6fc75c3.js → 404-29560aa2e6a60963.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{ant-poc-b9932971a479f3a7.js → ant-poc-404f3c9018952800.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-72251b48e2e03a1e.js → [id]-75c2ed6ba3de48cd.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{new-9611dd42856d6062.js → new-487ae57dc7e2ded2.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{properties-e5748812ba055a56.js → properties-f67fda6a71d0a46b.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{consent-39d65f13cc8f1cf8.js → consent-75395dfc224cf44b.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/projects/{[projectUrn]-a29850536c85d4b8.js → [projectUrn]-fa65c67ec8c4b5e6.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/data-catalog/[systemId]/{projects-c44ce244122e96d5.js → projects-e76a07ee6ee8d55f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/[collectionName]/{[...subfieldNames]-d79551d4c64c398c.js → [...subfieldNames]-704553f5329fb9d4.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/[datasetId]/{[collectionName]-7e5df4a0de7540bb.js → [collectionName]-06c19dca941edb14.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{[datasetId]-6ba18f92ba561114.js → [datasetId]-eac517f43d5f53a8.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{integrations-ef8000d7388dc915.js → integrations-373fb3a596099fc9.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{[id]-ad02e019b2467958.js → [id]-66f5fbadd8455805.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/messaging/{add-template-f9693cb6a0b7ded8.js → add-template-d441abb1f045940f.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{configure-2987edc77388e85a.js → configure-b8c94b10ab90b061.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{[id]-94e2faa73dd6a3e6.js → [id]-f4fb941df069b7bf.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/properties/{add-property-630a6a3dd6502ba6.js → add-property-bccb6ffab25aa214.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/{properties-20ca2f963906674b.js → properties-9a7ac623370b7c00.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{about-a49d0f84cf0cf05e.js → about-4412a7b468b6d4bf.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-fa42d8f18df44927.js → domain-records-d9088f5cd9fb2822.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{email-templates-6fd6071e2009b8f2.js → email-templates-cf09ad896c7396a6.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{locations-66c757325cb58467.js → locations-759564ca0ae62840.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{regulations-c6c239996cfa6ae8.js → regulations-2dce4501fca920b3.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{webpack-2c7ccac5843c4d8e.js → webpack-03e375f6d6b2c71b.js} +0 -0
@@ -122,6 +122,7 @@ def extract_key_for_address(
|
|
122
122
|
return f"{dataset}:{collection}"
|
123
123
|
|
124
124
|
|
125
|
+
# pylint: disable=too-many-branches
|
125
126
|
def unflatten_dict(flat_dict: Dict[str, Any], separator: str = ".") -> Dict[str, Any]:
|
126
127
|
"""
|
127
128
|
Converts a dictionary of paths/values into a nested dictionary
|
@@ -149,17 +150,29 @@ def unflatten_dict(flat_dict: Dict[str, Any], separator: str = ".") -> Dict[str,
|
|
149
150
|
for i, current_key in enumerate(keys[:-1]):
|
150
151
|
next_key = keys[i + 1]
|
151
152
|
if next_key.isdigit():
|
152
|
-
target
|
153
|
+
if isinstance(target, dict): # Only call setdefault on dictionaries
|
154
|
+
target = target.setdefault(current_key, [])
|
155
|
+
elif isinstance(
|
156
|
+
target, list
|
157
|
+
): # If target is a list, handle differently
|
158
|
+
idx = int(current_key)
|
159
|
+
while len(target) <= idx:
|
160
|
+
target.append([]) # Add a list since next_key is a digit
|
161
|
+
target = target[idx]
|
153
162
|
else:
|
154
163
|
if isinstance(target, dict):
|
155
164
|
target = target.setdefault(current_key, {})
|
156
165
|
elif isinstance(target, list):
|
157
|
-
|
166
|
+
idx = int(current_key)
|
167
|
+
while len(target) <= idx:
|
158
168
|
target.append({})
|
159
|
-
target = target[
|
169
|
+
target = target[idx]
|
160
170
|
try:
|
161
171
|
if isinstance(target, list):
|
162
|
-
|
172
|
+
idx = int(keys[-1]) if keys[-1].isdigit() else len(target)
|
173
|
+
while len(target) <= idx:
|
174
|
+
target.append(None)
|
175
|
+
target[idx] = value
|
163
176
|
else:
|
164
177
|
# If the value is a dictionary, add its components to the queue for processing
|
165
178
|
if isinstance(value, dict):
|
@@ -176,10 +189,12 @@ def unflatten_dict(flat_dict: Dict[str, Any], separator: str = ".") -> Dict[str,
|
|
176
189
|
return output
|
177
190
|
|
178
191
|
|
192
|
+
# pylint: disable=too-many-branches
|
179
193
|
def flatten_dict(data: Any, prefix: str = "", separator: str = ".") -> Dict[str, Any]:
|
180
194
|
"""
|
181
195
|
Recursively flatten a dictionary or list into a flat dictionary with dot-notation keys.
|
182
196
|
Handles nested dictionaries and arrays with proper indices.
|
197
|
+
Preserves empty lists and dictionaries.
|
183
198
|
|
184
199
|
example:
|
185
200
|
|
@@ -191,7 +206,9 @@ def flatten_dict(data: Any, prefix: str = "", separator: str = ".") -> Dict[str,
|
|
191
206
|
"D": [
|
192
207
|
{"E": "3"},
|
193
208
|
{"E": "4"}
|
194
|
-
]
|
209
|
+
],
|
210
|
+
"E": [],
|
211
|
+
"F": {}
|
195
212
|
}
|
196
213
|
|
197
214
|
becomes
|
@@ -200,7 +217,9 @@ def flatten_dict(data: Any, prefix: str = "", separator: str = ".") -> Dict[str,
|
|
200
217
|
"A.B": "1",
|
201
218
|
"A.C": "2",
|
202
219
|
"D.0.E": "3",
|
203
|
-
"D.1.E": "4"
|
220
|
+
"D.1.E": "4",
|
221
|
+
"E": [],
|
222
|
+
"F": {}
|
204
223
|
}
|
205
224
|
|
206
225
|
Args:
|
@@ -211,20 +230,40 @@ def flatten_dict(data: Any, prefix: str = "", separator: str = ".") -> Dict[str,
|
|
211
230
|
Returns:
|
212
231
|
A flattened dictionary with dot-notation keys
|
213
232
|
"""
|
214
|
-
items = {}
|
233
|
+
items: Dict[str, Any] = {}
|
215
234
|
|
216
235
|
if isinstance(data, dict):
|
236
|
+
# Handle top-level empty dictionary case
|
237
|
+
if not data and not prefix:
|
238
|
+
return {}
|
239
|
+
|
240
|
+
# If the dictionary is empty but has a prefix, store it as is
|
241
|
+
if not data:
|
242
|
+
items[prefix] = {}
|
243
|
+
return items
|
244
|
+
|
217
245
|
for k, v in data.items():
|
218
246
|
new_key = f"{prefix}{separator}{k}" if prefix else k
|
219
247
|
if isinstance(v, (dict, list)):
|
220
|
-
|
248
|
+
if not v: # Handle empty dict or list
|
249
|
+
items[new_key] = v
|
250
|
+
else:
|
251
|
+
items.update(flatten_dict(v, new_key, separator))
|
221
252
|
else:
|
222
253
|
items[new_key] = v
|
223
254
|
elif isinstance(data, list):
|
255
|
+
# If the list is empty, store it as is
|
256
|
+
if not data:
|
257
|
+
items[prefix] = []
|
258
|
+
return items
|
259
|
+
|
224
260
|
for i, v in enumerate(data):
|
225
261
|
new_key = f"{prefix}{separator}{i}"
|
226
262
|
if isinstance(v, (dict, list)):
|
227
|
-
|
263
|
+
if not v: # Handle empty dict or list
|
264
|
+
items[new_key] = v
|
265
|
+
else:
|
266
|
+
items.update(flatten_dict(v, new_key, separator))
|
228
267
|
else:
|
229
268
|
items[new_key] = v
|
230
269
|
else:
|
fides/cli/commands/pull.py
CHANGED
@@ -5,9 +5,11 @@ from click_default_group import DefaultGroup
|
|
5
5
|
|
6
6
|
from fides.cli.options import fides_key_argument, manifests_dir_argument
|
7
7
|
from fides.cli.utils import with_analytics, with_server_health_check
|
8
|
-
from fides.common.utils import echo_red
|
8
|
+
from fides.common.utils import echo_green, echo_red
|
9
9
|
from fides.core import parse as _parse
|
10
10
|
from fides.core import pull as _pull
|
11
|
+
from fides.core.api_helpers import list_server_resources
|
12
|
+
from fides.core.pull import remove_nulls, write_manifest_file
|
11
13
|
from fides.core.utils import git_is_dirty
|
12
14
|
|
13
15
|
|
@@ -60,26 +62,88 @@ def pull_all(
|
|
60
62
|
|
61
63
|
@pull.command(name="dataset") # type: ignore
|
62
64
|
@click.pass_context
|
63
|
-
@
|
65
|
+
@click.argument("fides_key", required=False)
|
64
66
|
@manifests_dir_argument
|
65
|
-
|
67
|
+
@click.option(
|
68
|
+
"--all-resources",
|
69
|
+
"-a",
|
70
|
+
is_flag=True,
|
71
|
+
default=False,
|
72
|
+
help="Pull all datasets from the server.",
|
73
|
+
)
|
74
|
+
@click.option(
|
75
|
+
"--separate-files",
|
76
|
+
is_flag=True,
|
77
|
+
default=False,
|
78
|
+
help="Write each dataset to a separate file named after its fides_key.",
|
79
|
+
)
|
80
|
+
@with_analytics
|
81
|
+
@with_server_health_check
|
82
|
+
def pull_dataset(
|
66
83
|
ctx: click.Context,
|
67
|
-
fides_key: str,
|
84
|
+
fides_key: Optional[str],
|
68
85
|
manifests_dir: str,
|
86
|
+
all_resources: bool,
|
87
|
+
separate_files: bool,
|
69
88
|
) -> None:
|
70
89
|
"""
|
71
|
-
Retrieve
|
90
|
+
Retrieve datasets from the server and update the local manifest files.
|
91
|
+
|
92
|
+
If FIDES_KEY is provided, only that dataset will be pulled.
|
93
|
+
If --all-resources is specified, all datasets will be pulled.
|
94
|
+
If --separate-files is specified, each dataset will be written to a separate file.
|
72
95
|
"""
|
96
|
+
if not fides_key and not all_resources:
|
97
|
+
echo_red("Error: Either FIDES_KEY or --all-resources must be specified.")
|
98
|
+
raise SystemExit(1)
|
73
99
|
|
74
100
|
config = ctx.obj["CONFIG"]
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
101
|
+
|
102
|
+
# Check for unstaged git changes before proceeding
|
103
|
+
if git_is_dirty(manifests_dir):
|
104
|
+
echo_red(
|
105
|
+
f"There are unstaged changes in your manifest directory: '{manifests_dir}' \nAborting pull!"
|
106
|
+
)
|
107
|
+
raise SystemExit(1)
|
108
|
+
|
109
|
+
if fides_key:
|
110
|
+
_pull.pull(
|
111
|
+
url=config.cli.server_url,
|
112
|
+
manifests_dir=manifests_dir,
|
113
|
+
headers=config.user.auth_header,
|
114
|
+
fides_key=fides_key,
|
115
|
+
resource_type="dataset",
|
116
|
+
all_resources_file=None,
|
117
|
+
)
|
118
|
+
elif all_resources:
|
119
|
+
# Get all available datasets from server
|
120
|
+
datasets = list_server_resources(
|
121
|
+
url=config.cli.server_url,
|
122
|
+
headers=config.user.auth_header,
|
123
|
+
resource_type="dataset",
|
124
|
+
exclude_keys=[],
|
125
|
+
)
|
126
|
+
|
127
|
+
if not datasets:
|
128
|
+
echo_red("No datasets found on the server.")
|
129
|
+
return
|
130
|
+
|
131
|
+
# Remove null values
|
132
|
+
datasets = [remove_nulls(dataset) for dataset in datasets]
|
133
|
+
|
134
|
+
if separate_files:
|
135
|
+
# Write each dataset to a separate file
|
136
|
+
for dataset in datasets:
|
137
|
+
if "fides_key" in dataset:
|
138
|
+
fides_key = dataset["fides_key"]
|
139
|
+
manifest_path = f"{manifests_dir.rstrip('/')}/{fides_key}.yml"
|
140
|
+
write_manifest_file(manifest_path, {"dataset": [dataset]})
|
141
|
+
else:
|
142
|
+
# Write all datasets to a single file
|
143
|
+
all_datasets_file = f"{manifests_dir.rstrip('/')}/datasets.yml"
|
144
|
+
write_manifest_file(all_datasets_file, {"dataset": datasets})
|
145
|
+
|
146
|
+
echo_green("Pull complete.")
|
83
147
|
|
84
148
|
|
85
149
|
@pull.command(name="system") # type: ignore
|
@@ -65,6 +65,17 @@ class DatabaseSettings(FidesSettings):
|
|
65
65
|
default="default-db",
|
66
66
|
description="The hostname of the application database server.",
|
67
67
|
)
|
68
|
+
readonly_server: Optional[str] = Field(
|
69
|
+
default=None,
|
70
|
+
description="The hostname of the application read database server.",
|
71
|
+
)
|
72
|
+
|
73
|
+
# TODO (LJ-663): add optional readonly_user
|
74
|
+
# TODO (LJ-663): add optional readonly_password
|
75
|
+
# TODO (LJ-663): add optional readonly_port
|
76
|
+
# TODO (LJ-663): add optional readonly_params
|
77
|
+
# TODO (LJ-663): add optional readonly_db
|
78
|
+
|
68
79
|
task_engine_pool_size: int = Field(
|
69
80
|
default=50,
|
70
81
|
description="Number of concurrent database connections Fides will use for executing privacy request tasks, either locally or on each worker. Note that the pool begins with no connections, but as they are requested the connections are maintained and reused up to this limit.",
|
@@ -105,6 +116,11 @@ class DatabaseSettings(FidesSettings):
|
|
105
116
|
description="Programmatically created connection string for the application database.",
|
106
117
|
exclude=True,
|
107
118
|
)
|
119
|
+
sqlalchemy_readonly_database_uri: Optional[str] = Field(
|
120
|
+
default=None,
|
121
|
+
description="Programmatically created connection string for the read-only application database.",
|
122
|
+
exclude=True,
|
123
|
+
)
|
108
124
|
sqlalchemy_test_database_uri: str = Field(
|
109
125
|
default="",
|
110
126
|
description="Programmatically created connection string for the test database.",
|
@@ -218,6 +234,36 @@ class DatabaseSettings(FidesSettings):
|
|
218
234
|
)
|
219
235
|
)
|
220
236
|
|
237
|
+
@field_validator("sqlalchemy_readonly_database_uri", mode="before")
|
238
|
+
@classmethod
|
239
|
+
def assemble_readonly_db_connection(
|
240
|
+
cls, v: Optional[str], info: ValidationInfo
|
241
|
+
) -> Optional[str]:
|
242
|
+
"""Join DB connection credentials into a synchronous connection string."""
|
243
|
+
if isinstance(v, str) and v:
|
244
|
+
return v
|
245
|
+
if not info.data.get("readonly_server"):
|
246
|
+
return None
|
247
|
+
port: int = port_integer_converter(info)
|
248
|
+
return str(
|
249
|
+
# TODO: support optional readonly params for user, password, etc.
|
250
|
+
PostgresDsn.build( # pylint: disable=no-member
|
251
|
+
scheme="postgresql",
|
252
|
+
username=info.data.get("user"),
|
253
|
+
password=info.data.get("password"),
|
254
|
+
host=info.data.get("readonly_server"),
|
255
|
+
port=port,
|
256
|
+
path=f"{info.data.get('db') or ''}",
|
257
|
+
query=(
|
258
|
+
urlencode(
|
259
|
+
cast(Dict, info.data.get("params")), quote_via=quote, safe="/"
|
260
|
+
)
|
261
|
+
if info.data.get("params")
|
262
|
+
else None
|
263
|
+
),
|
264
|
+
)
|
265
|
+
)
|
266
|
+
|
221
267
|
@field_validator("sqlalchemy_test_database_uri", mode="before")
|
222
268
|
@classmethod
|
223
269
|
def assemble_test_db_connection(cls, v: Optional[str], info: ValidationInfo) -> str:
|
fides/core/api.py
CHANGED
@@ -4,7 +4,8 @@ from typing import Dict, List, Optional, Union
|
|
4
4
|
|
5
5
|
import requests
|
6
6
|
|
7
|
-
from fides.api.util.endpoint_utils
|
7
|
+
# Not using the constant value from fides.api.util.endpoint_utils to reduce the startup time for the CLI
|
8
|
+
API_PREFIX = "/api/v1"
|
8
9
|
|
9
10
|
|
10
11
|
def generate_resource_url(
|
fides/core/pull.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
"""This module handles the logic for syncing remote resource versions into their local file."""
|
2
2
|
|
3
|
-
from typing import Dict, List, Optional
|
3
|
+
from typing import Any, Dict, List, Optional
|
4
4
|
|
5
5
|
import yaml
|
6
6
|
from fideslang import model_list
|
@@ -13,6 +13,30 @@ from fides.core.utils import get_manifest_list
|
|
13
13
|
MODEL_LIST = model_list
|
14
14
|
|
15
15
|
|
16
|
+
def remove_nulls(obj: Any) -> Any:
|
17
|
+
"""
|
18
|
+
Recursively remove all null values from dictionaries and lists.
|
19
|
+
|
20
|
+
Args:
|
21
|
+
obj: Any Python object (dict, list, etc.)
|
22
|
+
|
23
|
+
Returns:
|
24
|
+
The transformed data structure with null values removed
|
25
|
+
"""
|
26
|
+
if isinstance(obj, list):
|
27
|
+
# For lists: process each item and filter out None values
|
28
|
+
return [remove_nulls(item) for item in obj if item is not None]
|
29
|
+
|
30
|
+
if isinstance(obj, dict):
|
31
|
+
# For dictionaries: process each key-value pair and filter out pairs with None values
|
32
|
+
return {
|
33
|
+
key: remove_nulls(value) for key, value in obj.items() if value is not None
|
34
|
+
}
|
35
|
+
|
36
|
+
# Return all other data types unchanged
|
37
|
+
return obj
|
38
|
+
|
39
|
+
|
16
40
|
def write_manifest_file(manifest_path: str, manifest: Dict) -> None:
|
17
41
|
"""
|
18
42
|
Write a manifest file out.
|
@@ -54,6 +78,8 @@ def pull_existing_resources(
|
|
54
78
|
)
|
55
79
|
|
56
80
|
if server_resource:
|
81
|
+
# Remove null values from the server resource
|
82
|
+
server_resource = remove_nulls(server_resource)
|
57
83
|
updated_resource_list.append(server_resource)
|
58
84
|
print(
|
59
85
|
f" - {resource_type.capitalize()} with fides_key: {fides_key} is being updated from the server..."
|
@@ -77,16 +103,17 @@ def pull_resource_by_key(
|
|
77
103
|
) -> None:
|
78
104
|
"""
|
79
105
|
Pull a resource from the server by its fides_key and update the local manifest file if it exists,
|
80
|
-
otherwise a new manifest file at {manifests_dir}/{resource_type}.
|
106
|
+
otherwise a new manifest file at {manifests_dir}/{resource_type}.yml
|
81
107
|
"""
|
82
108
|
if manifests_dir[-1] == "/":
|
83
109
|
manifests_dir = manifests_dir[:-1]
|
84
|
-
manifest_path = f"{manifests_dir}/{
|
85
|
-
print(f"Pulling {resource_type} with fides_key: {fides_key}...", end="
|
110
|
+
manifest_path = f"{manifests_dir}/{fides_key}.yml"
|
111
|
+
print(f"Pulling {resource_type} with fides_key: {fides_key}...", end="\n")
|
86
112
|
server_resource = get_server_resource(url, resource_type, fides_key, headers)
|
87
|
-
print("done.")
|
88
113
|
|
89
114
|
if server_resource:
|
115
|
+
# Remove null values from the server resource
|
116
|
+
server_resource = remove_nulls(server_resource)
|
90
117
|
try:
|
91
118
|
manifest = load_yaml_into_dict(manifest_path)
|
92
119
|
except FileNotFoundError:
|
@@ -94,10 +121,8 @@ def pull_resource_by_key(
|
|
94
121
|
f"Manifest file {manifest_path} does not already exist and will be created"
|
95
122
|
)
|
96
123
|
manifest = {}
|
97
|
-
print("Writing out resource to file...", end=" ")
|
98
124
|
manifest[resource_type] = [server_resource]
|
99
125
|
write_manifest_file(manifest_path, manifest)
|
100
|
-
print("done.")
|
101
126
|
|
102
127
|
else:
|
103
128
|
echo_red(
|
@@ -127,6 +152,12 @@ def pull_missing_resources(
|
|
127
152
|
for resource in MODEL_LIST
|
128
153
|
}
|
129
154
|
|
155
|
+
# Remove null values from all resources
|
156
|
+
for resource_type, resources in resource_manifest.items():
|
157
|
+
resource_manifest[resource_type] = [
|
158
|
+
remove_nulls(resource) for resource in (resources or [])
|
159
|
+
]
|
160
|
+
|
130
161
|
resource_manifest = {
|
131
162
|
key: value for key, value in resource_manifest.items() if value
|
132
163
|
}
|
File without changes
|
@@ -0,0 +1,202 @@
|
|
1
|
+
from functools import wraps
|
2
|
+
from typing import Any, Callable, Optional, TypeVar
|
3
|
+
|
4
|
+
from fastapi import HTTPException
|
5
|
+
from loguru import logger
|
6
|
+
from starlette.status import HTTP_400_BAD_REQUEST, HTTP_422_UNPROCESSABLE_ENTITY
|
7
|
+
|
8
|
+
T = TypeVar("T")
|
9
|
+
|
10
|
+
|
11
|
+
class ErrorHandler:
|
12
|
+
"""Utility class for handling errors consistently throughout the application.
|
13
|
+
|
14
|
+
Usage Examples:
|
15
|
+
-----------------------------------------------------------------------------
|
16
|
+
|
17
|
+
1. Basic Validation:
|
18
|
+
```python
|
19
|
+
from fastapi import FastAPI
|
20
|
+
from fides.service.error_handling.error_handler import ErrorHandler
|
21
|
+
|
22
|
+
app = FastAPI()
|
23
|
+
|
24
|
+
@app.post("/users")
|
25
|
+
def create_user(age: int):
|
26
|
+
# Simple validation
|
27
|
+
ErrorHandler.validate(age >= 18, "User must be 18 or older")
|
28
|
+
return {"message": "User created"}
|
29
|
+
|
30
|
+
@app.get("/items/{item_id}")
|
31
|
+
def get_item(item_id: str):
|
32
|
+
# Direct error raising
|
33
|
+
if not item_id:
|
34
|
+
ErrorHandler.raise_error("Item ID is required", status_code=400)
|
35
|
+
return {"item_id": item_id}
|
36
|
+
```
|
37
|
+
|
38
|
+
2. Exception Handling Decorator:
|
39
|
+
```python
|
40
|
+
@app.post("/orders")
|
41
|
+
@ErrorHandler.handle_exceptions("Failed to create order")
|
42
|
+
def create_order(order_data: dict):
|
43
|
+
if not order_data.get("items"):
|
44
|
+
raise ValueError("Order must contain items")
|
45
|
+
# Process order...
|
46
|
+
return {"message": "Order created"}
|
47
|
+
|
48
|
+
@app.get("/products/{product_id}")
|
49
|
+
@ErrorHandler.handle_exceptions("Failed to fetch product", status_code=404)
|
50
|
+
def get_product(product_id: str):
|
51
|
+
product = database.get_product(product_id)
|
52
|
+
if not product:
|
53
|
+
raise ValueError("Product not found")
|
54
|
+
return product
|
55
|
+
```
|
56
|
+
|
57
|
+
3. Complex Validation:
|
58
|
+
```python
|
59
|
+
@app.post("/payments")
|
60
|
+
@ErrorHandler.handle_exceptions("Payment processing failed")
|
61
|
+
def process_payment(payment: dict):
|
62
|
+
# Validate amount
|
63
|
+
ErrorHandler.validate(
|
64
|
+
payment.get("amount", 0) > 0,
|
65
|
+
"Payment amount must be positive",
|
66
|
+
HTTP_400_BAD_REQUEST
|
67
|
+
)
|
68
|
+
|
69
|
+
# Validate currency
|
70
|
+
ErrorHandler.validate(
|
71
|
+
payment.get("currency") in ["USD", "EUR"],
|
72
|
+
"Invalid currency",
|
73
|
+
HTTP_400_BAD_REQUEST,
|
74
|
+
"Unsupported currency provided" # Optional log message
|
75
|
+
)
|
76
|
+
|
77
|
+
# Custom error for insufficient funds
|
78
|
+
if payment.get("amount", 0) > get_balance():
|
79
|
+
ErrorHandler.raise_error(
|
80
|
+
"Insufficient funds",
|
81
|
+
status_code=HTTP_400_BAD_REQUEST,
|
82
|
+
log_message="User attempted payment exceeding balance"
|
83
|
+
)
|
84
|
+
|
85
|
+
return {"status": "payment processed"}
|
86
|
+
```
|
87
|
+
|
88
|
+
4. Error Logging:
|
89
|
+
```python
|
90
|
+
@app.post("/imports")
|
91
|
+
@ErrorHandler.handle_exceptions("Import failed")
|
92
|
+
def import_data(data: dict):
|
93
|
+
try:
|
94
|
+
process_import(data)
|
95
|
+
except Exception as e:
|
96
|
+
# Log error with custom message before raising
|
97
|
+
ErrorHandler.raise_error(
|
98
|
+
"Import failed: invalid format",
|
99
|
+
status_code=400,
|
100
|
+
log_message=f"Import failed with error: {str(e)}"
|
101
|
+
)
|
102
|
+
return {"status": "import complete"}
|
103
|
+
```
|
104
|
+
|
105
|
+
Key Features:
|
106
|
+
- Simple validation with custom error messages
|
107
|
+
- Consistent error handling across endpoints
|
108
|
+
- Built-in error logging
|
109
|
+
- HTTP status code customization
|
110
|
+
- Exception handling decorator for common patterns
|
111
|
+
"""
|
112
|
+
|
113
|
+
@staticmethod
|
114
|
+
def raise_error(
|
115
|
+
detail: str,
|
116
|
+
status_code: int = HTTP_422_UNPROCESSABLE_ENTITY,
|
117
|
+
log_message: Optional[str] = None,
|
118
|
+
) -> None:
|
119
|
+
"""Raise an HTTPException with consistent logging.
|
120
|
+
|
121
|
+
Args:
|
122
|
+
detail: Error message to include in the HTTPException
|
123
|
+
status_code: HTTP status code to use (default: 422)
|
124
|
+
log_message: Optional message to log before raising the exception
|
125
|
+
|
126
|
+
Raises:
|
127
|
+
HTTPException: Always raised with the provided details
|
128
|
+
"""
|
129
|
+
if log_message:
|
130
|
+
logger.error(log_message)
|
131
|
+
raise HTTPException(status_code=status_code, detail=detail)
|
132
|
+
|
133
|
+
@staticmethod
|
134
|
+
def validate(
|
135
|
+
condition: bool,
|
136
|
+
detail: str,
|
137
|
+
status_code: int = HTTP_400_BAD_REQUEST,
|
138
|
+
log_message: Optional[str] = None,
|
139
|
+
) -> None:
|
140
|
+
"""Validate a condition and raise an error if it's False.
|
141
|
+
|
142
|
+
Args:
|
143
|
+
condition: The condition to check
|
144
|
+
detail: Error message if condition is False
|
145
|
+
status_code: HTTP status code to use if condition is False
|
146
|
+
log_message: Optional message to log before raising the exception
|
147
|
+
|
148
|
+
Raises:
|
149
|
+
HTTPException: If the condition is False
|
150
|
+
"""
|
151
|
+
if not condition:
|
152
|
+
ErrorHandler.raise_error(detail, status_code, log_message)
|
153
|
+
|
154
|
+
@classmethod
|
155
|
+
def handle_exceptions(
|
156
|
+
cls, error_message: str, status_code: int = HTTP_422_UNPROCESSABLE_ENTITY
|
157
|
+
) -> Callable[[Callable[..., T]], Callable[..., T]]:
|
158
|
+
"""Decorator to handle exceptions consistently.
|
159
|
+
|
160
|
+
Args:
|
161
|
+
error_message: Base error message to use
|
162
|
+
status_code: HTTP status code to use for unexpected errors
|
163
|
+
|
164
|
+
Returns:
|
165
|
+
Callable: The decorated function that handles exceptions
|
166
|
+
|
167
|
+
Note:
|
168
|
+
This decorator will catch specific exceptions and convert them to HTTPExceptions.
|
169
|
+
HTTPExceptions are re-raised as is, while other exceptions are wrapped with
|
170
|
+
additional context.
|
171
|
+
"""
|
172
|
+
|
173
|
+
def decorator(func: Callable[..., T]) -> Callable[..., T]:
|
174
|
+
@wraps(func)
|
175
|
+
def wrapper(*args: Any, **kwargs: Any) -> Optional[T]:
|
176
|
+
try:
|
177
|
+
return func(*args, **kwargs)
|
178
|
+
except HTTPException:
|
179
|
+
# Re-raise HTTP exceptions without modification
|
180
|
+
raise
|
181
|
+
except (ValueError, TypeError, AttributeError, KeyError) as e:
|
182
|
+
# Handle common validation and data access errors
|
183
|
+
cls.raise_error(
|
184
|
+
f"{error_message}: {str(e)}",
|
185
|
+
status_code,
|
186
|
+
f"{error_message}: {e}",
|
187
|
+
)
|
188
|
+
except Exception as e: # pylint: disable=broad-except
|
189
|
+
# Log unexpected errors with full context but present a sanitized message
|
190
|
+
logger.error(
|
191
|
+
f"Unexpected error in {func.__name__}: {str(e)}", exc_info=True
|
192
|
+
)
|
193
|
+
cls.raise_error(
|
194
|
+
f"{error_message}: An unexpected error occurred",
|
195
|
+
status_code,
|
196
|
+
f"{error_message}: Unexpected {type(e).__name__}",
|
197
|
+
)
|
198
|
+
return None # This line is never reached but satisfies the return type checker
|
199
|
+
|
200
|
+
return wrapper # type: ignore[return-value]
|
201
|
+
|
202
|
+
return decorator
|
File without changes
|