ethyca-fides 2.58.2b5__py2.py3-none-any.whl → 2.58.2rc0__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.2b5.dist-info → ethyca_fides-2.58.2rc0.dist-info}/METADATA +11 -20
- {ethyca_fides-2.58.2b5.dist-info → ethyca_fides-2.58.2rc0.dist-info}/RECORD +178 -188
- {ethyca_fides-2.58.2b5.dist-info → ethyca_fides-2.58.2rc0.dist-info}/WHEEL +1 -1
- {ethyca_fides-2.58.2b5.dist-info → ethyca_fides-2.58.2rc0.dist-info}/entry_points.txt +1 -0
- fides/_version.py +3 -3
- fides/api/api/deps.py +2 -8
- fides/api/api/v1/endpoints/user_endpoints.py +12 -8
- fides/api/cryptography/identity_salt.py +13 -12
- fides/api/custom_types.py +1 -6
- fides/api/db/base.py +0 -5
- fides/api/db/system.py +3 -1
- fides/api/migrations/hash_migration_job.py +2 -2
- fides/api/models/attachment.py +11 -80
- fides/api/models/comment.py +15 -45
- fides/api/models/detection_discovery.py +0 -31
- fides/api/models/fides_user.py +9 -26
- fides/api/models/fides_user_invite.py +0 -2
- fides/api/models/privacy_experience.py +0 -68
- fides/api/models/privacy_request/privacy_request.py +6 -23
- fides/api/schemas/connection_configuration/connection_config.py +16 -30
- fides/api/schemas/user.py +1 -5
- fides/api/service/deps.py +0 -9
- fides/api/service/storage/s3.py +1 -14
- fides/api/service/user/fides_user_service.py +128 -0
- fides/api/task/graph_task.py +1 -1
- fides/api/util/collection_util.py +9 -48
- fides/cli/commands/pull.py +13 -77
- fides/core/api.py +1 -2
- fides/core/pull.py +7 -38
- fides/ui-build/static/admin/404.html +1 -1
- fides/ui-build/static/admin/_next/static/chunks/1150-035a721a04f4451e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/1376-5cea5ef9362215e8.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{2397-0d1c289b788fcc11.js → 2397-ee53235fb21b5e97.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/3412-7ec8751b8182e1bf.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{3855-63495367531cb776.js → 3855-b6b7865dedd7bc2a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{3872-472bb47eb34d8fdb.js → 3872-4e053c20d546f027.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{4060-53a5c6347690a8fa.js → 4060-8d165e1236ea521a.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/4450-36234280bee624ff.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/4481-aab99ff80f707473.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{5258-cf7b27ef51f38392.js → 5258-0658dc2274df6832.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{5480-52dc446be40725f5.js → 5480-f49696df5e8ae500.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{5487-8678d75ee1d1ef09.js → 5487-3ad50d21cdbc9209.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/5973-52aee296edc44f7e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/6315-fa1519cdf080f42d.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{6372-e0bb9f8d07cc3b04.js → 6372-ca9c12ac8902365b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{6853-09e831e9dff7fd3b.js → 6853-8941824350c3c1a8.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{6954-ec5276bb464d42b2.js → 6954-3b887fb444f9228c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/7453-39761c38da31257e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{7751-a70fe0e5f67f5538.js → 7751-a8f31c062d4cb09d.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{79-8e060d36d36c752c.js → 79-f9b948ebb186900f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{7980-72f745bff9fabcc9.js → 7980-4bd08957448dea32.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{9046-a69fa8f99c414570.js → 9046-04bd7becea207cb1.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/9767-1a23925d2cb27b51.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/{main-090643377c8254e6.js → main-24f422f93845a596.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/{main-app-59156a9331ac7bce.js → main-app-94a0711202e08b15.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{_app-0c1548ca3b158123.js → _app-fc89ce7bed454c84.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/add-systems/{manual-9f9500c639362aa6.js → manual-9acaab973dfe86e2.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{add-systems-fac606150b65d494.js → add-systems-d258f0c25fa020bf.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/[id]-fb75fa0aea77678d.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/{new-c02b14c50b19bd91.js → new-a9d9402c219d13e5.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience-c946b33b0322b8ad.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-ea57f9d6ad17e957.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/{reporting-100234c23a85d235.js → reporting-788cf0e34829af46.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{data-catalog-49bffaf07973fead.js → data-catalog-900004e402c31797.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]/{[systemId]-727f1f5d06be674b.js → [systemId]-b66831fdafcdf67c.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-5f9ef1f99818117c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-d001337d1bb73bd1.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-11b3ce9f61d9bfe9.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/dataset/{new-4d4a31d0186a4a5b.js → new-803c1b577ab17ae3.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{dataset-29317d18ce34adfe.js → dataset-fa743ddc7f89d76b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{[id]-14c57e5069e9cccd.js → [id]-bbe1ca2793798e6b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection/{new-f0a4e385c1ad8fee.js → new-abc17fef69cd951b.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-a78a73b65929853a.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{index-b70def65e264270e.js → index-bfaacdb55a5a6c9f.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-d4329043219fed9b.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/messaging-1e60754abec1ee6b.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/{[id]-72d64a02b7ef175e.js → [id]-fe765154315782cf.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-f5f7a8069909ef24.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-9f7eaad05e5b9292.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{privacy-requests-fd81714d811db7b3.js → privacy-requests-d85c0d16ba09ba35.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/reporting/{datamap-f035cddf17f4d898.js → datamap-afedc48ef4e7f858.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-89524101b7279f6e.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{custom-fields-dfcd7a4b6aa773bd.js → custom-fields-52d030b1db2ca1b9.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/domains-24cba38685dc872c.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/{organization-0e0aa552f520f913.js → organization-a08693d0d1e10bc8.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/[id]/{test-datasets-1d83d5178b3eb216.js → test-datasets-151571cff4e85894.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/systems/configure/{[id]-a158fa77df288523.js → [id]-4f5a28226575c976.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{systems-8490aaaee9d76a4a.js → systems-abd68fc5ddde5482.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/{taxonomy-be1ffe267b1602e1.js → taxonomy-16b4d75c49276add.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/{new-082c3156175f9267.js → new-be690621a944bfe2.js} +1 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-78eaf933f755bfe8.js +1 -0
- fides/ui-build/static/admin/_next/static/chunks/pages/{user-management-a1db56f1cbfba373.js → user-management-6c9ad62479a7d03e.js} +1 -1
- fides/ui-build/static/admin/_next/static/css/{cf2744a40308fc4a.css → 113d823fe71f6af0.css} +1 -1
- fides/ui-build/static/admin/_next/static/o0mKeH0cB6eAYV6qOlVD0/_buildManifest.js +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/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/alembic/migrations/versions/67d01c4e124e_add_reject_all_mechanism_to_privacy_.py +0 -56
- fides/api/alembic/migrations/versions/6e565c16dae1_add_tcf_publisher_restrictions.py +0 -107
- fides/api/alembic/migrations/versions/9288f729cac4_add_tcf_configuration_fk_to_experience_.py +0 -62
- fides/api/alembic/migrations/versions/99c603c1b8f9_add_password_login_enabled_and_totp_secret_to_fidesuser.py +0 -45
- fides/api/models/tcf_publisher_restrictions.py +0 -465
- fides/service/error_handling/__init__.py +0 -0
- fides/service/error_handling/error_handler.py +0 -202
- fides/service/user/__init__.py +0 -0
- fides/service/user/user_service.py +0 -140
- fides/ui-build/static/admin/_next/static/_o6WH0hDzNEhnUJyvLex7/_buildManifest.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/1376-87058e04584cff20.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/146-0ae2d30ec71fce09.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/1817-48e1c9d3504e18f0.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/3938-6a1c07d06a80cf4c.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4121-4d5273d7a354994d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4450-9c3086ccb55c66aa.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/4481-275aa9f4c10bce53.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/5973-d3d3872692c1d0fa.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/6315-24a0483ee1cab6cc.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/9767-8179ce7336727141.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience/[id]-ab3ef485f6101697.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-experience-ba5325035c71a97e.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices-ef31c181cac86c64.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center/[monitorId]-53efbed54d230f07.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/action-center-17d1525551d8904f.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/data-discovery/activity-7d2cb947eee11262.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/datastore-connection-d2f88a8fc68944db.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/integrations/[id]-b75ab4ee677f118d.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/messaging-26407674949bcbc4.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/messaging-28d4bdf060ec8cb2.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/privacy-requests/configure/storage-208e49ef43361d6f.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/consent-ff1985f72d50ef47.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/settings/domains-5a0b10ec955097d4.js +0 -1
- fides/ui-build/static/admin/_next/static/chunks/pages/user-management/profile/[id]-af83245e9373a064.js +0 -1
- fides/ui-build/static/admin/images/connector-logos/website.svg +0 -10
- {ethyca_fides-2.58.2b5.dist-info/licenses → ethyca_fides-2.58.2rc0.dist-info}/LICENSE +0 -0
- {ethyca_fides-2.58.2b5.dist-info → ethyca_fides-2.58.2rc0.dist-info}/top_level.txt +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{4723-1dd1d16f404d56a2.js → 4723-0a3c5e2ce143a7d0.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/{5826-ef0aa43ffad83acc.js → 5826-e5dcb4e68cfe6289.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/{configure-a4e636eecaba5324.js → configure-723cc3d4f5740ea6.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/consent/privacy-notices/{[id]-606457ef5bd1eb04.js → [id]-72251b48e2e03a1e.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{about-2ae030f3a28f057a.js → about-a49d0f84cf0cf05e.js} +0 -0
- /fides/ui-build/static/admin/_next/static/chunks/pages/settings/{domain-records-72ec54ee8755a503.js → domain-records-fa42d8f18df44927.js} +0 -0
- /fides/ui-build/static/admin/_next/static/{_o6WH0hDzNEhnUJyvLex7 → o0mKeH0cB6eAYV6qOlVD0}/_ssgManifest.js +0 -0
@@ -20,7 +20,6 @@ from fides.api.models import (
|
|
20
20
|
from fides.api.models.location_regulation_selections import PrivacyNoticeRegion
|
21
21
|
from fides.api.models.privacy_notice import PrivacyNotice
|
22
22
|
from fides.api.models.property import Property
|
23
|
-
from fides.api.models.tcf_publisher_restrictions import TCFConfiguration
|
24
23
|
from fides.api.schemas.language import SupportedLanguage
|
25
24
|
|
26
25
|
|
@@ -47,21 +46,6 @@ class Layer1ButtonOption(Enum):
|
|
47
46
|
|
48
47
|
ACKNOWLEDGE = "acknowledge"
|
49
48
|
OPT_IN_OPT_OUT = "opt_in_opt_out"
|
50
|
-
OPT_IN_ONLY = "opt_in_only"
|
51
|
-
|
52
|
-
|
53
|
-
class RejectAllMechanism(Enum):
|
54
|
-
"""
|
55
|
-
Reject all mechanism options - not formalized in the db.
|
56
|
-
Used to configure the behavior of the reject all button in TCF experiences
|
57
|
-
"""
|
58
|
-
|
59
|
-
# Reject both consent and legitimate interest preferences (all purposes, special features, vendors)
|
60
|
-
# This is the default behavior
|
61
|
-
REJECT_ALL = "reject_all"
|
62
|
-
# Reject only consent preferences (all purposes, special features, vendors).
|
63
|
-
# Do not reject any legitimate interest preferences.
|
64
|
-
REJECT_CONSENT_ONLY = "reject_consent_only"
|
65
49
|
|
66
50
|
|
67
51
|
# Fides JS UX Types - there should only be one of these defined per region
|
@@ -195,10 +179,6 @@ class PrivacyExperienceConfig(PrivacyExperienceConfigBase, Base):
|
|
195
179
|
The Privacy Experience Configuration model that stores shared configuration for Privacy Experiences.
|
196
180
|
|
197
181
|
- Translations, Notices, and Regions (via Privacy Experiences) are linked to this resource.
|
198
|
-
|
199
|
-
If you're adding a new PrivacyExperienceConfig, make sure to use the `create` method since it has
|
200
|
-
custom logic that ensures other resources are created/updated as needed, as well as setting the
|
201
|
-
expected values for some fields in the experience config itself.
|
202
182
|
"""
|
203
183
|
|
204
184
|
allow_language_selection = Column(
|
@@ -223,20 +203,6 @@ class PrivacyExperienceConfig(PrivacyExperienceConfigBase, Base):
|
|
223
203
|
String, ForeignKey(ExperienceConfigTemplate.id_field_path)
|
224
204
|
) # The template from which this config was created if applicable
|
225
205
|
|
226
|
-
# Mechanism to use when the reject all button is clicked in a TCF experience
|
227
|
-
# Nullable because this is not applicable for other experience types
|
228
|
-
reject_all_mechanism = Column(
|
229
|
-
EnumColumn(RejectAllMechanism),
|
230
|
-
nullable=True,
|
231
|
-
)
|
232
|
-
# Optional FK to a TCF Configuration
|
233
|
-
|
234
|
-
tcf_configuration_id = Column(
|
235
|
-
String,
|
236
|
-
ForeignKey(TCFConfiguration.id_field_path, ondelete="SET NULL"),
|
237
|
-
nullable=True,
|
238
|
-
)
|
239
|
-
|
240
206
|
# Relationships
|
241
207
|
experiences = relationship(
|
242
208
|
"PrivacyExperience",
|
@@ -266,12 +232,6 @@ class PrivacyExperienceConfig(PrivacyExperienceConfigBase, Base):
|
|
266
232
|
lazy="selectin",
|
267
233
|
)
|
268
234
|
|
269
|
-
tcf_configuration: RelationshipProperty[Optional[TCFConfiguration]] = relationship(
|
270
|
-
"TCFConfiguration",
|
271
|
-
back_populates="privacy_experience_configs",
|
272
|
-
lazy="selectin",
|
273
|
-
)
|
274
|
-
|
275
235
|
@property
|
276
236
|
def regions(self) -> List[PrivacyNoticeRegion]:
|
277
237
|
"""Return the regions using this experience config"""
|
@@ -344,15 +304,6 @@ class PrivacyExperienceConfig(PrivacyExperienceConfigBase, Base):
|
|
344
304
|
# Link Properties to this Privacy Experience config via the PrivacyExperienceConfigProperty table
|
345
305
|
link_properties_to_experience_config(db, properties, experience_config)
|
346
306
|
|
347
|
-
# If the reject all mechanism is not set and the experience config is a TCF experience,
|
348
|
-
# set the reject all mechanism to REJECT_ALL
|
349
|
-
if (
|
350
|
-
experience_config.component == ComponentType.tcf_overlay
|
351
|
-
and experience_config.reject_all_mechanism is None
|
352
|
-
):
|
353
|
-
experience_config.reject_all_mechanism = RejectAllMechanism.REJECT_ALL # type: ignore
|
354
|
-
experience_config.save(db)
|
355
|
-
|
356
307
|
return experience_config
|
357
308
|
|
358
309
|
def update(self, db: Session, *, data: dict[str, Any]) -> PrivacyExperienceConfig:
|
@@ -556,24 +507,6 @@ class PrivacyExperienceConfigHistory(
|
|
556
507
|
index=True,
|
557
508
|
) # If a translation is deleted, this is set to null, but the overall record remains in the database for reporting purposes
|
558
509
|
|
559
|
-
# Mechanism to use when the reject all button is clicked in a TCF experience
|
560
|
-
# Nullable because this is not applicable for other experience types
|
561
|
-
reject_all_mechanism = Column(
|
562
|
-
EnumColumn(RejectAllMechanism),
|
563
|
-
nullable=True,
|
564
|
-
)
|
565
|
-
# Optional FK to a TCF Configuration
|
566
|
-
tcf_configuration_id = Column(
|
567
|
-
String,
|
568
|
-
ForeignKey(TCFConfiguration.id_field_path, ondelete="SET NULL"),
|
569
|
-
nullable=True,
|
570
|
-
)
|
571
|
-
|
572
|
-
tcf_configuration: RelationshipProperty[Optional[TCFConfiguration]] = relationship(
|
573
|
-
"TCFConfiguration",
|
574
|
-
lazy="selectin",
|
575
|
-
)
|
576
|
-
|
577
510
|
version = Column(Float, nullable=False, default=1.0)
|
578
511
|
|
579
512
|
|
@@ -636,7 +569,6 @@ class PrivacyExperience(Base):
|
|
636
569
|
tcf_special_features: List = []
|
637
570
|
tcf_system_consents: List = []
|
638
571
|
tcf_system_legitimate_interests: List = []
|
639
|
-
tcf_publisher_restrictions: List = []
|
640
572
|
gvl: Optional[Dict] = {}
|
641
573
|
# TCF Developer-Friendly Meta added at runtime as the result of build_tc_data_for_mobile
|
642
574
|
meta: Dict = {}
|
@@ -39,14 +39,10 @@ from fides.api.graph.config import (
|
|
39
39
|
CollectionAddress,
|
40
40
|
)
|
41
41
|
from fides.api.migrations.hash_migration_mixin import HashMigrationMixin
|
42
|
-
from fides.api.models.attachment import
|
43
|
-
Attachment,
|
44
|
-
AttachmentReference,
|
45
|
-
AttachmentReferenceType,
|
46
|
-
)
|
42
|
+
from fides.api.models.attachment import Attachment, AttachmentReference
|
47
43
|
from fides.api.models.audit_log import AuditLog
|
48
44
|
from fides.api.models.client import ClientDetail
|
49
|
-
from fides.api.models.comment import Comment, CommentReference
|
45
|
+
from fides.api.models.comment import Comment, CommentReference
|
50
46
|
from fides.api.models.fides_user import FidesUser
|
51
47
|
from fides.api.models.manual_webhook import AccessManualWebhook
|
52
48
|
from fides.api.models.policy import (
|
@@ -173,25 +169,18 @@ class PrivacyRequest(
|
|
173
169
|
backref="privacy_requests",
|
174
170
|
)
|
175
171
|
attachments = relationship(
|
176
|
-
|
172
|
+
Attachment,
|
177
173
|
secondary="attachment_reference",
|
178
|
-
primaryjoin="
|
179
|
-
"AttachmentReference.reference_type == 'privacy_request')",
|
174
|
+
primaryjoin="PrivacyRequest.id == AttachmentReference.reference_id",
|
180
175
|
secondaryjoin="Attachment.id == AttachmentReference.attachment_id",
|
181
176
|
order_by="Attachment.created_at",
|
182
|
-
viewonly=True,
|
183
|
-
uselist=True,
|
184
177
|
)
|
185
|
-
|
186
178
|
comments = relationship(
|
187
|
-
|
179
|
+
Comment,
|
188
180
|
secondary="comment_reference",
|
189
|
-
primaryjoin="
|
190
|
-
"CommentReference.reference_type == 'privacy_request')",
|
181
|
+
primaryjoin="PrivacyRequest.id == CommentReference.reference_id",
|
191
182
|
secondaryjoin="Comment.id == CommentReference.comment_id",
|
192
183
|
order_by="Comment.created_at",
|
193
|
-
viewonly=True,
|
194
|
-
uselist=True,
|
195
184
|
)
|
196
185
|
property_id = Column(String, nullable=True)
|
197
186
|
|
@@ -334,12 +323,6 @@ class PrivacyRequest(
|
|
334
323
|
deleting this object from the database
|
335
324
|
"""
|
336
325
|
self.clear_cached_values()
|
337
|
-
Attachment.delete_attachments_for_reference_and_type(
|
338
|
-
db, self.id, AttachmentReferenceType.privacy_request
|
339
|
-
)
|
340
|
-
Comment.delete_comments_for_reference_and_type(
|
341
|
-
db, self.id, CommentReferenceType.privacy_request
|
342
|
-
)
|
343
326
|
|
344
327
|
for provided_identity in self.provided_identities: # type: ignore[attr-defined]
|
345
328
|
provided_identity.delete(db=db)
|
@@ -71,23 +71,29 @@ def mask_sensitive_fields(
|
|
71
71
|
return new_connection_secrets
|
72
72
|
|
73
73
|
|
74
|
-
class
|
74
|
+
class ConnectionConfigurationResponse(BaseModel):
|
75
75
|
"""
|
76
|
-
|
77
|
-
and handle masking of sensitive values based on `connection_type`
|
78
|
-
and (optionally) a `saas_config`.
|
76
|
+
Describes the returned schema for a ConnectionConfiguration.
|
79
77
|
"""
|
80
78
|
|
79
|
+
name: Optional[str] = None
|
80
|
+
key: FidesKey
|
81
|
+
description: Optional[str] = None
|
81
82
|
connection_type: ConnectionType
|
82
|
-
|
83
|
+
access: AccessLevel
|
84
|
+
created_at: datetime
|
85
|
+
updated_at: Optional[datetime] = None
|
86
|
+
disabled: Optional[bool] = False
|
87
|
+
last_test_timestamp: Optional[datetime] = None
|
88
|
+
last_test_succeeded: Optional[bool] = None
|
83
89
|
saas_config: Optional[SaaSConfigBase] = None
|
90
|
+
secrets: Optional[Dict[str, Any]] = None
|
91
|
+
authorized: Optional[bool] = False
|
92
|
+
enabled_actions: Optional[List[ActionType]] = None
|
84
93
|
|
85
94
|
@model_validator(mode="after")
|
86
|
-
def mask_sensitive_values(self) -> "
|
87
|
-
"""
|
88
|
-
Mask sensitive values in the `secrets` attribute based on `connection_type`
|
89
|
-
and (optionally) a `saas_config`.
|
90
|
-
"""
|
95
|
+
def mask_sensitive_values(self) -> "ConnectionConfigurationResponse":
|
96
|
+
"""Mask sensitive values in the response."""
|
91
97
|
if self.secrets is None:
|
92
98
|
return self
|
93
99
|
|
@@ -110,26 +116,6 @@ class ConnectionConfigSecretsMixin(BaseModel):
|
|
110
116
|
self.secrets = mask_sensitive_fields(cast(dict, self.secrets), secret_schema)
|
111
117
|
return self
|
112
118
|
|
113
|
-
|
114
|
-
class ConnectionConfigurationResponse(ConnectionConfigSecretsMixin):
|
115
|
-
"""
|
116
|
-
Describes the returned schema for a ConnectionConfiguration.
|
117
|
-
|
118
|
-
The mixin base class ensures that `secrets` sensitive values are masked.
|
119
|
-
"""
|
120
|
-
|
121
|
-
name: Optional[str] = None
|
122
|
-
key: FidesKey
|
123
|
-
description: Optional[str] = None
|
124
|
-
access: AccessLevel
|
125
|
-
created_at: datetime
|
126
|
-
updated_at: Optional[datetime] = None
|
127
|
-
disabled: Optional[bool] = False
|
128
|
-
last_test_timestamp: Optional[datetime] = None
|
129
|
-
last_test_succeeded: Optional[bool] = None
|
130
|
-
authorized: Optional[bool] = False
|
131
|
-
enabled_actions: Optional[List[ActionType]] = None
|
132
|
-
|
133
119
|
model_config = ConfigDict(from_attributes=True)
|
134
120
|
|
135
121
|
|
fides/api/schemas/user.py
CHANGED
@@ -3,7 +3,7 @@ from datetime import datetime
|
|
3
3
|
from enum import Enum
|
4
4
|
from typing import Optional
|
5
5
|
|
6
|
-
from pydantic import
|
6
|
+
from pydantic import EmailStr, field_validator
|
7
7
|
|
8
8
|
from fides.api.cryptography.cryptographic_util import decode_password
|
9
9
|
from fides.api.schemas.base_class import FidesSchema
|
@@ -27,8 +27,6 @@ class UserCreate(FidesSchema):
|
|
27
27
|
last_name: Optional[str] = None
|
28
28
|
disabled: bool = False
|
29
29
|
|
30
|
-
model_config = ConfigDict(extra="ignore")
|
31
|
-
|
32
30
|
@field_validator("username")
|
33
31
|
@classmethod
|
34
32
|
def validate_username(cls, username: str) -> str:
|
@@ -142,8 +140,6 @@ class UserUpdate(FidesSchema):
|
|
142
140
|
first_name: Optional[str] = None
|
143
141
|
last_name: Optional[str] = None
|
144
142
|
|
145
|
-
model_config = ConfigDict(extra="ignore")
|
146
|
-
|
147
143
|
|
148
144
|
class DisabledReason(Enum):
|
149
145
|
"""Reasons for why a user is disabled"""
|
fides/api/service/deps.py
CHANGED
@@ -8,7 +8,6 @@ from fides.service.dataset.dataset_config_service import DatasetConfigService
|
|
8
8
|
from fides.service.dataset.dataset_service import DatasetService
|
9
9
|
from fides.service.messaging.messaging_service import MessagingService
|
10
10
|
from fides.service.privacy_request.privacy_request_service import PrivacyRequestService
|
11
|
-
from fides.service.user.user_service import UserService
|
12
11
|
|
13
12
|
|
14
13
|
def get_messaging_service(
|
@@ -33,11 +32,3 @@ def get_dataset_service(db: Session = Depends(get_db)) -> DatasetService:
|
|
33
32
|
|
34
33
|
def get_dataset_config_service(db: Session = Depends(get_db)) -> DatasetConfigService:
|
35
34
|
return DatasetConfigService(db)
|
36
|
-
|
37
|
-
|
38
|
-
def get_user_service(
|
39
|
-
db: Session = Depends(get_db),
|
40
|
-
config: FidesConfig = Depends(get_config),
|
41
|
-
config_proxy: ConfigProxy = Depends(get_config_proxy),
|
42
|
-
) -> UserService:
|
43
|
-
return UserService(db, config, config_proxy)
|
fides/api/service/storage/s3.py
CHANGED
@@ -64,7 +64,7 @@ def generic_upload_to_s3( # pylint: disable=R0913
|
|
64
64
|
size_threshold: int = LARGE_FILE_THRESHOLD, # 5 MB threshold
|
65
65
|
) -> Optional[AnyHttpUrlString]:
|
66
66
|
"""
|
67
|
-
Uploads
|
67
|
+
Uploads arbitrary data to S3 returned from an access request.
|
68
68
|
Handles both small and large uploads.
|
69
69
|
|
70
70
|
:param storage_secrets: S3 storage secrets
|
@@ -75,19 +75,6 @@ def generic_upload_to_s3( # pylint: disable=R0913
|
|
75
75
|
"""
|
76
76
|
logger.info("Starting S3 Upload of {}", file_key)
|
77
77
|
|
78
|
-
# Validate that the document is a file-like object
|
79
|
-
if not hasattr(document, "read") or not hasattr(document, "seek"):
|
80
|
-
raise TypeError(
|
81
|
-
f"The 'document' parameter must be a file-like object supporting 'read' and 'seek'. "
|
82
|
-
f"Received: {type(document)}"
|
83
|
-
)
|
84
|
-
|
85
|
-
# Ensure the file pointer is at the beginning
|
86
|
-
try:
|
87
|
-
document.seek(0)
|
88
|
-
except Exception as e:
|
89
|
-
raise ValueError(f"Failed to reset file pointer for document: {e}")
|
90
|
-
|
91
78
|
s3_client = maybe_get_s3_client(auth_method, storage_secrets)
|
92
79
|
|
93
80
|
# Define a transfer configuration for multipart uploads
|
@@ -0,0 +1,128 @@
|
|
1
|
+
import uuid
|
2
|
+
from datetime import datetime
|
3
|
+
from typing import Optional, Tuple
|
4
|
+
|
5
|
+
from loguru import logger
|
6
|
+
from sqlalchemy.orm import Session
|
7
|
+
|
8
|
+
from fides.api.api.v1.endpoints.messaging_endpoints import user_email_invite_status
|
9
|
+
from fides.api.common_exceptions import AuthorizationError
|
10
|
+
from fides.api.models.client import ClientDetail
|
11
|
+
from fides.api.models.fides_user import FidesUser
|
12
|
+
from fides.api.models.fides_user_invite import FidesUserInvite
|
13
|
+
from fides.api.schemas.messaging.messaging import (
|
14
|
+
MessagingActionType,
|
15
|
+
UserInviteBodyParams,
|
16
|
+
)
|
17
|
+
from fides.api.schemas.redis_cache import Identity
|
18
|
+
from fides.api.service.messaging.message_dispatch_service import dispatch_message
|
19
|
+
from fides.config import FidesConfig
|
20
|
+
from fides.config.config_proxy import ConfigProxy
|
21
|
+
|
22
|
+
|
23
|
+
def invite_user(db: Session, config_proxy: ConfigProxy, user: FidesUser) -> None:
|
24
|
+
"""
|
25
|
+
Generates a user invite and sends the invite code to the user via email.
|
26
|
+
|
27
|
+
This is a no-op if email messaging isn't configured.
|
28
|
+
"""
|
29
|
+
|
30
|
+
# invite user via email if email messaging is enabled and the Admin UI URL is defined
|
31
|
+
if user_email_invite_status(db=db, config_proxy=config_proxy).enabled:
|
32
|
+
invite_code = str(uuid.uuid4())
|
33
|
+
FidesUserInvite.create(
|
34
|
+
db=db, data={"username": user.username, "invite_code": invite_code}
|
35
|
+
)
|
36
|
+
user.update(db, data={"disabled": True})
|
37
|
+
dispatch_message(
|
38
|
+
db,
|
39
|
+
action_type=MessagingActionType.USER_INVITE,
|
40
|
+
to_identity=Identity(email=user.email_address),
|
41
|
+
service_type=config_proxy.notifications.notification_service_type,
|
42
|
+
message_body_params=UserInviteBodyParams(
|
43
|
+
username=user.username, invite_code=invite_code
|
44
|
+
),
|
45
|
+
)
|
46
|
+
|
47
|
+
|
48
|
+
def accept_invite(
|
49
|
+
db: Session, config: FidesConfig, user: FidesUser, new_password: str
|
50
|
+
) -> Tuple[FidesUser, str]:
|
51
|
+
"""
|
52
|
+
Updates the user password and enables the user. Also removes the user invite from the database.
|
53
|
+
Returns a tuple of the updated user and their access code.
|
54
|
+
"""
|
55
|
+
|
56
|
+
# update password and enable
|
57
|
+
user.update_password(db=db, new_password=new_password)
|
58
|
+
user.update(
|
59
|
+
db,
|
60
|
+
data={"disabled": False, "disabled_reason": None},
|
61
|
+
)
|
62
|
+
db.refresh(user)
|
63
|
+
|
64
|
+
# delete invite
|
65
|
+
if user.username:
|
66
|
+
invite = FidesUserInvite.get_by(db=db, field="username", value=user.username)
|
67
|
+
if invite:
|
68
|
+
invite.delete(db)
|
69
|
+
else:
|
70
|
+
logger.warning("Username is missing, skipping invite deletion.")
|
71
|
+
|
72
|
+
client = perform_login(
|
73
|
+
db,
|
74
|
+
config.security.oauth_client_id_length_bytes,
|
75
|
+
config.security.oauth_client_secret_length_bytes,
|
76
|
+
user,
|
77
|
+
)
|
78
|
+
|
79
|
+
logger.info("Creating login access token")
|
80
|
+
access_code = client.create_access_code_jwe(config.security.app_encryption_key)
|
81
|
+
|
82
|
+
return user, access_code
|
83
|
+
|
84
|
+
|
85
|
+
def perform_login(
|
86
|
+
db: Session,
|
87
|
+
client_id_byte_length: int,
|
88
|
+
client_secret_byte_length: int,
|
89
|
+
user: FidesUser,
|
90
|
+
skip_save: Optional[bool] = False,
|
91
|
+
) -> ClientDetail:
|
92
|
+
"""Performs a login by updating the FidesUser instance and creating and returning
|
93
|
+
an associated ClientDetail.
|
94
|
+
|
95
|
+
If the username or password was bad, skip_save should be True. We still run through
|
96
|
+
parallel operations to keep the timing of operations similar, but should skip
|
97
|
+
saving to the database.
|
98
|
+
"""
|
99
|
+
|
100
|
+
client = user.client
|
101
|
+
if not client:
|
102
|
+
logger.info("Creating client for login")
|
103
|
+
client, _ = ClientDetail.create_client_and_secret(
|
104
|
+
db,
|
105
|
+
client_id_byte_length,
|
106
|
+
client_secret_byte_length,
|
107
|
+
scopes=[], # type: ignore
|
108
|
+
roles=user.permissions.roles, # type: ignore
|
109
|
+
systems=user.system_ids, # type: ignore
|
110
|
+
user_id=user.id,
|
111
|
+
in_memory=skip_save, # If login flow has already errored, don't persist this to the database
|
112
|
+
)
|
113
|
+
else:
|
114
|
+
# Refresh the client just in case - for example, scopes and roles were added via the db directly.
|
115
|
+
client.roles = user.permissions.roles # type: ignore
|
116
|
+
client.systems = user.system_ids # type: ignore
|
117
|
+
if not skip_save:
|
118
|
+
client.save(db)
|
119
|
+
|
120
|
+
if user.permissions and (not user.permissions.roles and not user.systems): # type: ignore
|
121
|
+
logger.warning("User {} needs roles or systems to login.", user.id)
|
122
|
+
raise AuthorizationError(detail="Not Authorized for this action")
|
123
|
+
|
124
|
+
if not skip_save:
|
125
|
+
user.last_login_at = datetime.utcnow()
|
126
|
+
user.save(db)
|
127
|
+
|
128
|
+
return client
|
fides/api/task/graph_task.py
CHANGED
@@ -10,7 +10,7 @@ from loguru import logger
|
|
10
10
|
from ordered_set import OrderedSet
|
11
11
|
from sqlalchemy.orm import Session
|
12
12
|
|
13
|
-
from fides.api.api.deps import
|
13
|
+
from fides.api.api.deps import get_db_contextmanager as get_db
|
14
14
|
from fides.api.common_exceptions import (
|
15
15
|
ActionDisabled,
|
16
16
|
AwaitingAsyncTaskCallback,
|
@@ -122,7 +122,6 @@ def extract_key_for_address(
|
|
122
122
|
return f"{dataset}:{collection}"
|
123
123
|
|
124
124
|
|
125
|
-
# pylint: disable=too-many-branches
|
126
125
|
def unflatten_dict(flat_dict: Dict[str, Any], separator: str = ".") -> Dict[str, Any]:
|
127
126
|
"""
|
128
127
|
Converts a dictionary of paths/values into a nested dictionary
|
@@ -150,29 +149,17 @@ def unflatten_dict(flat_dict: Dict[str, Any], separator: str = ".") -> Dict[str,
|
|
150
149
|
for i, current_key in enumerate(keys[:-1]):
|
151
150
|
next_key = keys[i + 1]
|
152
151
|
if next_key.isdigit():
|
153
|
-
|
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]
|
152
|
+
target = target.setdefault(current_key, [])
|
162
153
|
else:
|
163
154
|
if isinstance(target, dict):
|
164
155
|
target = target.setdefault(current_key, {})
|
165
156
|
elif isinstance(target, list):
|
166
|
-
|
167
|
-
while len(target) <= idx:
|
157
|
+
while len(target) <= int(current_key):
|
168
158
|
target.append({})
|
169
|
-
target = target[
|
159
|
+
target = target[int(current_key)]
|
170
160
|
try:
|
171
161
|
if isinstance(target, list):
|
172
|
-
|
173
|
-
while len(target) <= idx:
|
174
|
-
target.append(None)
|
175
|
-
target[idx] = value
|
162
|
+
target.append(value)
|
176
163
|
else:
|
177
164
|
# If the value is a dictionary, add its components to the queue for processing
|
178
165
|
if isinstance(value, dict):
|
@@ -189,12 +176,10 @@ def unflatten_dict(flat_dict: Dict[str, Any], separator: str = ".") -> Dict[str,
|
|
189
176
|
return output
|
190
177
|
|
191
178
|
|
192
|
-
# pylint: disable=too-many-branches
|
193
179
|
def flatten_dict(data: Any, prefix: str = "", separator: str = ".") -> Dict[str, Any]:
|
194
180
|
"""
|
195
181
|
Recursively flatten a dictionary or list into a flat dictionary with dot-notation keys.
|
196
182
|
Handles nested dictionaries and arrays with proper indices.
|
197
|
-
Preserves empty lists and dictionaries.
|
198
183
|
|
199
184
|
example:
|
200
185
|
|
@@ -206,9 +191,7 @@ def flatten_dict(data: Any, prefix: str = "", separator: str = ".") -> Dict[str,
|
|
206
191
|
"D": [
|
207
192
|
{"E": "3"},
|
208
193
|
{"E": "4"}
|
209
|
-
]
|
210
|
-
"E": [],
|
211
|
-
"F": {}
|
194
|
+
]
|
212
195
|
}
|
213
196
|
|
214
197
|
becomes
|
@@ -217,9 +200,7 @@ def flatten_dict(data: Any, prefix: str = "", separator: str = ".") -> Dict[str,
|
|
217
200
|
"A.B": "1",
|
218
201
|
"A.C": "2",
|
219
202
|
"D.0.E": "3",
|
220
|
-
"D.1.E": "4"
|
221
|
-
"E": [],
|
222
|
-
"F": {}
|
203
|
+
"D.1.E": "4"
|
223
204
|
}
|
224
205
|
|
225
206
|
Args:
|
@@ -230,40 +211,20 @@ def flatten_dict(data: Any, prefix: str = "", separator: str = ".") -> Dict[str,
|
|
230
211
|
Returns:
|
231
212
|
A flattened dictionary with dot-notation keys
|
232
213
|
"""
|
233
|
-
items
|
214
|
+
items = {}
|
234
215
|
|
235
216
|
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
|
-
|
245
217
|
for k, v in data.items():
|
246
218
|
new_key = f"{prefix}{separator}{k}" if prefix else k
|
247
219
|
if isinstance(v, (dict, list)):
|
248
|
-
|
249
|
-
items[new_key] = v
|
250
|
-
else:
|
251
|
-
items.update(flatten_dict(v, new_key, separator))
|
220
|
+
items.update(flatten_dict(v, new_key, separator))
|
252
221
|
else:
|
253
222
|
items[new_key] = v
|
254
223
|
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
|
-
|
260
224
|
for i, v in enumerate(data):
|
261
225
|
new_key = f"{prefix}{separator}{i}"
|
262
226
|
if isinstance(v, (dict, list)):
|
263
|
-
|
264
|
-
items[new_key] = v
|
265
|
-
else:
|
266
|
-
items.update(flatten_dict(v, new_key, separator))
|
227
|
+
items.update(flatten_dict(v, new_key, separator))
|
267
228
|
else:
|
268
229
|
items[new_key] = v
|
269
230
|
else:
|