ethyca-fides 2.68.0rc0__py2.py3-none-any.whl → 2.68.0rc1__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.68.0rc0.dist-info → ethyca_fides-2.68.0rc1.dist-info}/METADATA +1 -1
- {ethyca_fides-2.68.0rc0.dist-info → ethyca_fides-2.68.0rc1.dist-info}/RECORD +94 -93
- fides/_version.py +3 -3
- fides/api/alembic/migrations/versions/2f3c1a2d6b10_disallow_dot_in_monitor_key_and_update_refs.py +439 -0
- fides/api/db/base_class.py +19 -1
- fides/api/models/db_cache.py +8 -0
- fides/api/models/detection_discovery/core.py +14 -1
- fides/ui-build/static/admin/404.html +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/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/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/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
- {ethyca_fides-2.68.0rc0.dist-info → ethyca_fides-2.68.0rc1.dist-info}/WHEEL +0 -0
- {ethyca_fides-2.68.0rc0.dist-info → ethyca_fides-2.68.0rc1.dist-info}/entry_points.txt +0 -0
- {ethyca_fides-2.68.0rc0.dist-info → ethyca_fides-2.68.0rc1.dist-info}/licenses/LICENSE +0 -0
- {ethyca_fides-2.68.0rc0.dist-info → ethyca_fides-2.68.0rc1.dist-info}/top_level.txt +0 -0
- /fides/ui-build/static/admin/_next/static/{QlH9EstEPGgswLXq9aikH → nd386tKRkuP7h0W5NiKwd}/_buildManifest.js +0 -0
- /fides/ui-build/static/admin/_next/static/{QlH9EstEPGgswLXq9aikH → nd386tKRkuP7h0W5NiKwd}/_ssgManifest.js +0 -0
fides/api/alembic/migrations/versions/2f3c1a2d6b10_disallow_dot_in_monitor_key_and_update_refs.py
ADDED
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Disallow dots in monitor keys and update references to monitor keys.
|
|
3
|
+
|
|
4
|
+
This is a data migration.
|
|
5
|
+
|
|
6
|
+
This migration does the following:
|
|
7
|
+
- Fetches a mapping of old monitor keys containing '.' to new, safe keys (replace '.' with '_').
|
|
8
|
+
- Persists the mapping into the dbcache table.
|
|
9
|
+
- Drops the FK constraints on the stagedresourceancestor table.
|
|
10
|
+
- Updates the monitorconfig.key, monitorexecution.monitor_config_key, stagedresource.monitor_config_id, stagedresource.urn, stagedresource.parent, stagedresource.children, stagedresource.meta, stagedresourceancestor.ancestor_urn, and stagedresourceancestor.descendant_urn to reference new keys.
|
|
11
|
+
- Recreates the FK constraints on the stagedresourceancestor table.
|
|
12
|
+
- Adds a CHECK constraint to forbid dots in monitorconfig.key going forward.
|
|
13
|
+
|
|
14
|
+
Revision ID: 2f3c1a2d6b10
|
|
15
|
+
Revises: a7065df4dcf1
|
|
16
|
+
Create Date: 2025-08-11 00:00:00.000000
|
|
17
|
+
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
import json
|
|
23
|
+
from typing import Dict
|
|
24
|
+
|
|
25
|
+
import sqlalchemy as sa
|
|
26
|
+
from alembic import op
|
|
27
|
+
from loguru import logger
|
|
28
|
+
from sqlalchemy.engine import Connection
|
|
29
|
+
|
|
30
|
+
# revision identifiers, used by Alembic.
|
|
31
|
+
revision = "2f3c1a2d6b10"
|
|
32
|
+
down_revision = "a7065df4dcf1"
|
|
33
|
+
branch_labels = None
|
|
34
|
+
depends_on = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _fetch_monitor_key_mapping(conn: Connection) -> Dict[str, str]:
|
|
38
|
+
"""
|
|
39
|
+
Build a mapping of old monitor keys containing '.' to new, safe keys (replace '.' with '_').
|
|
40
|
+
Ensures uniqueness by appending a short numeric suffix when needed.
|
|
41
|
+
"""
|
|
42
|
+
rows = conn.execute(
|
|
43
|
+
sa.text(
|
|
44
|
+
"""
|
|
45
|
+
SELECT key FROM monitorconfig
|
|
46
|
+
"""
|
|
47
|
+
)
|
|
48
|
+
).fetchall()
|
|
49
|
+
existing_keys = {r[0] for r in rows}
|
|
50
|
+
|
|
51
|
+
mapping: Dict[str, str] = {}
|
|
52
|
+
taken: set[str] = set(existing_keys)
|
|
53
|
+
|
|
54
|
+
for old_key in sorted(existing_keys):
|
|
55
|
+
if "." not in old_key:
|
|
56
|
+
continue
|
|
57
|
+
base = old_key.replace(".", "_")
|
|
58
|
+
new_key = base
|
|
59
|
+
# ensure uniqueness
|
|
60
|
+
if new_key in taken:
|
|
61
|
+
i = 1
|
|
62
|
+
while f"{base}_{i}" in taken:
|
|
63
|
+
i += 1
|
|
64
|
+
new_key = f"{base}_{i}"
|
|
65
|
+
mapping[old_key] = new_key
|
|
66
|
+
taken.add(new_key)
|
|
67
|
+
|
|
68
|
+
if mapping:
|
|
69
|
+
logger.info(
|
|
70
|
+
f"Found {len(mapping)} monitor keys with dots, migrating monitorconfig keys based on mapping: {mapping}"
|
|
71
|
+
)
|
|
72
|
+
return mapping
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _update_monitorconfig_keys(conn: Connection, mapping: Dict[str, str]) -> None:
|
|
76
|
+
"""
|
|
77
|
+
Updates `MonitorConfig.key`s based on the mapping of 'old' keys to 'new' keys.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
logger.info("Updating monitorconfig keys...")
|
|
81
|
+
for old, new in mapping.items():
|
|
82
|
+
conn.execute(
|
|
83
|
+
sa.text(
|
|
84
|
+
"""
|
|
85
|
+
UPDATE monitorconfig
|
|
86
|
+
SET key = :new
|
|
87
|
+
WHERE key = :old
|
|
88
|
+
"""
|
|
89
|
+
),
|
|
90
|
+
{"old": old, "new": new},
|
|
91
|
+
)
|
|
92
|
+
logger.info("Finished updating monitorconfig keys.")
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _update_monitorexecution(conn: Connection, mapping: Dict[str, str]) -> None:
|
|
96
|
+
"""
|
|
97
|
+
Updates `monitorexecution.monitor_config_key` to reference new `MonitorConfig.key`s.
|
|
98
|
+
"""
|
|
99
|
+
|
|
100
|
+
logger.info("Updating monitorexecution monitor_config_key...")
|
|
101
|
+
for old, new in mapping.items():
|
|
102
|
+
conn.execute(
|
|
103
|
+
sa.text(
|
|
104
|
+
"""
|
|
105
|
+
UPDATE monitorexecution
|
|
106
|
+
SET monitor_config_key = :new
|
|
107
|
+
WHERE monitor_config_key = :old
|
|
108
|
+
"""
|
|
109
|
+
),
|
|
110
|
+
{"old": old, "new": new},
|
|
111
|
+
)
|
|
112
|
+
logger.info("Finished updating monitorexecution monitor_config_key.")
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _update_stagedresource_monitor_config_id(
|
|
116
|
+
conn: Connection, mapping: Dict[str, str]
|
|
117
|
+
) -> None:
|
|
118
|
+
"""
|
|
119
|
+
Updates `stagedresource.monitor_config_id` to reference new `MonitorConfig.key`s.
|
|
120
|
+
"""
|
|
121
|
+
logger.info("Updating stagedresource monitor_config_id...")
|
|
122
|
+
for old, new in mapping.items():
|
|
123
|
+
conn.execute(
|
|
124
|
+
sa.text(
|
|
125
|
+
"""
|
|
126
|
+
UPDATE stagedresource
|
|
127
|
+
SET monitor_config_id = :new
|
|
128
|
+
WHERE monitor_config_id = :old
|
|
129
|
+
"""
|
|
130
|
+
),
|
|
131
|
+
{"old": old, "new": new},
|
|
132
|
+
)
|
|
133
|
+
logger.info("Finished updating stagedresource monitor_config_id.")
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def _update_stagedresource_urns_and_parent(
|
|
137
|
+
conn: Connection, mapping: Dict[str, str]
|
|
138
|
+
) -> None:
|
|
139
|
+
"""
|
|
140
|
+
Updates `stagedresource.urn`s and `stagedresource.parent`s to reference new `MonitorConfig.key`s.
|
|
141
|
+
"""
|
|
142
|
+
logger.info("Updating stagedresource urns and parent...")
|
|
143
|
+
for old, new in mapping.items():
|
|
144
|
+
params = {"old": old, "new": new, "old_prefix": f"{old}.%"}
|
|
145
|
+
|
|
146
|
+
conn.execute(
|
|
147
|
+
sa.text(
|
|
148
|
+
f"""
|
|
149
|
+
UPDATE stagedresource
|
|
150
|
+
SET urn = :new || SUBSTRING(urn FROM CHAR_LENGTH(:old) + 1)
|
|
151
|
+
WHERE urn LIKE :old_prefix AND monitor_config_id = :new
|
|
152
|
+
"""
|
|
153
|
+
),
|
|
154
|
+
params,
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
conn.execute(
|
|
158
|
+
sa.text(
|
|
159
|
+
f"""
|
|
160
|
+
UPDATE stagedresource
|
|
161
|
+
SET parent = :new || SUBSTRING(parent FROM CHAR_LENGTH(:old) + 1)
|
|
162
|
+
WHERE parent LIKE :old_prefix AND monitor_config_id = :new
|
|
163
|
+
"""
|
|
164
|
+
),
|
|
165
|
+
params,
|
|
166
|
+
)
|
|
167
|
+
logger.info("Finished updating stagedresource urns and parent.")
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _update_stagedresource_children_array(
|
|
171
|
+
conn: Connection, mapping: Dict[str, str]
|
|
172
|
+
) -> None:
|
|
173
|
+
"""
|
|
174
|
+
Updates `stagedresource.children` to reference new `MonitorConfig.key`s.
|
|
175
|
+
|
|
176
|
+
Elements from `stagedresource.children` are unnested, updated, and re-aggregated in a single query.
|
|
177
|
+
"""
|
|
178
|
+
logger.info("Updating stagedresource children array...")
|
|
179
|
+
for old, new in mapping.items():
|
|
180
|
+
|
|
181
|
+
conn.execute(
|
|
182
|
+
sa.text(
|
|
183
|
+
"""
|
|
184
|
+
UPDATE stagedresource
|
|
185
|
+
SET children = (
|
|
186
|
+
SELECT COALESCE(array_agg(
|
|
187
|
+
CASE WHEN val LIKE :old_prefix
|
|
188
|
+
THEN :new || SUBSTRING(val FROM CHAR_LENGTH(:old) + 1)
|
|
189
|
+
ELSE val END
|
|
190
|
+
), '{}')
|
|
191
|
+
FROM unnest(children) AS val
|
|
192
|
+
)
|
|
193
|
+
WHERE monitor_config_id = :new
|
|
194
|
+
AND children IS NOT NULL
|
|
195
|
+
"""
|
|
196
|
+
),
|
|
197
|
+
{"old": old, "new": new, "old_prefix": f"{old}.%"},
|
|
198
|
+
)
|
|
199
|
+
logger.info("Finished updating stagedresource children array.")
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def _update_stagedresource_meta_json(conn: Connection, mapping: Dict[str, str]) -> None:
|
|
203
|
+
"""
|
|
204
|
+
Updates necessary keys in `stagedresource.meta` (`direct_child_urns` and `top_level_field_urn`)
|
|
205
|
+
to reference new `MonitorConfig.key`s.
|
|
206
|
+
"""
|
|
207
|
+
logger.info("Updating stagedresource meta json...")
|
|
208
|
+
|
|
209
|
+
for old, new in mapping.items():
|
|
210
|
+
params = {"old": old, "new": new, "old_prefix": f"{old}.%"}
|
|
211
|
+
|
|
212
|
+
# updates `stagedresource.meta.direct_child_urns` to reference new `MonitorConfig.key`
|
|
213
|
+
# elements from `stagedresource.meta.direct_child_urns` are unnested, updated, and re-aggregated in a single query.
|
|
214
|
+
conn.execute(
|
|
215
|
+
sa.text(
|
|
216
|
+
"""
|
|
217
|
+
UPDATE stagedresource
|
|
218
|
+
SET meta = jsonb_set(
|
|
219
|
+
COALESCE(meta, '{}'::jsonb),
|
|
220
|
+
'{direct_child_urns}',
|
|
221
|
+
(
|
|
222
|
+
SELECT COALESCE(jsonb_agg(
|
|
223
|
+
CASE WHEN elem LIKE :old_prefix
|
|
224
|
+
THEN to_jsonb(:new || SUBSTRING(elem FROM CHAR_LENGTH(:old) + 1))
|
|
225
|
+
ELSE to_jsonb(elem) END
|
|
226
|
+
), '[]'::jsonb)
|
|
227
|
+
FROM jsonb_array_elements_text(COALESCE(meta->'direct_child_urns','[]'::jsonb)) AS elem
|
|
228
|
+
),
|
|
229
|
+
true
|
|
230
|
+
)
|
|
231
|
+
WHERE monitor_config_id = :new AND meta ? 'direct_child_urns'
|
|
232
|
+
"""
|
|
233
|
+
),
|
|
234
|
+
params,
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
# updates `stagedresource.meta.top_level_field_urn` to reference new `MonitorConfig.key`
|
|
238
|
+
# we coalesce the value to 'null'::jsonb to ensure a 'JSON null' value is set, not a column-level NULL
|
|
239
|
+
conn.execute(
|
|
240
|
+
sa.text(
|
|
241
|
+
"""
|
|
242
|
+
UPDATE stagedresource
|
|
243
|
+
SET meta = jsonb_set(
|
|
244
|
+
COALESCE(meta, '{}'::jsonb),
|
|
245
|
+
'{top_level_field_urn}',
|
|
246
|
+
COALESCE(to_jsonb(
|
|
247
|
+
CASE WHEN (meta->>'top_level_field_urn') LIKE :old_prefix
|
|
248
|
+
THEN :new || SUBSTRING((meta->>'top_level_field_urn') FROM CHAR_LENGTH(:old) + 1)
|
|
249
|
+
ELSE (meta->>'top_level_field_urn') END
|
|
250
|
+
), 'null'::jsonb),
|
|
251
|
+
true
|
|
252
|
+
)
|
|
253
|
+
WHERE monitor_config_id = :new AND meta ? 'top_level_field_urn'
|
|
254
|
+
"""
|
|
255
|
+
),
|
|
256
|
+
params,
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
logger.info("Finished updating stagedresource meta json.")
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _update_stagedresourceancestor(conn: Connection, mapping: Dict[str, str]) -> None:
|
|
263
|
+
"""
|
|
264
|
+
Updates `stagedresourceancestor.ancestor_urn` and `stagedresourceancestor.descendant_urn`
|
|
265
|
+
to reference new `MonitorConfig.key`s.
|
|
266
|
+
|
|
267
|
+
NOTE: StagedResourceAncestor queries cannot be easily scoped by monitor_config_id
|
|
268
|
+
since the table doesn't have a direct reference to it. However, we optimize by
|
|
269
|
+
doing a single table scan to update both columns for all mappings at once.
|
|
270
|
+
|
|
271
|
+
The generated query will look like:
|
|
272
|
+
```
|
|
273
|
+
UPDATE stagedresourceancestor
|
|
274
|
+
SET
|
|
275
|
+
ancestor_urn = CASE
|
|
276
|
+
WHEN ancestor_urn LIKE 'monitor.config.1.%' THEN 'monitor_config_1' || SUBSTRING(ancestor_urn FROM 17)
|
|
277
|
+
WHEN ancestor_urn LIKE 'monitor.config.2.%' THEN 'monitor_config_2' || SUBSTRING(ancestor_urn FROM 17)
|
|
278
|
+
ELSE ancestor_urn
|
|
279
|
+
END,
|
|
280
|
+
descendant_urn = CASE
|
|
281
|
+
WHEN descendant_urn LIKE 'monitor.config.1.%' THEN 'monitor_config_1' || SUBSTRING(descendant_urn FROM 17)
|
|
282
|
+
WHEN descendant_urn LIKE 'monitor.config.2.%' THEN 'monitor_config_2' || SUBSTRING(descendant_urn FROM 17)
|
|
283
|
+
ELSE descendant_urn
|
|
284
|
+
END
|
|
285
|
+
WHERE ancestor_urn LIKE 'monitor.config.1.%' OR descendant_urn LIKE 'monitor.config.1.%'
|
|
286
|
+
OR ancestor_urn LIKE 'monitor.config.2.%' OR descendant_urn LIKE 'monitor.config.2.%'
|
|
287
|
+
```
|
|
288
|
+
"""
|
|
289
|
+
logger.info("Updating stagedresourceancestor...")
|
|
290
|
+
|
|
291
|
+
if not mapping:
|
|
292
|
+
return
|
|
293
|
+
|
|
294
|
+
ancestor_case_conditions = []
|
|
295
|
+
descendant_case_conditions = []
|
|
296
|
+
where_conditions = []
|
|
297
|
+
|
|
298
|
+
for old, new in mapping.items():
|
|
299
|
+
ancestor_case_conditions.append(
|
|
300
|
+
f"WHEN ancestor_urn LIKE '{old}.%' THEN '{new}' || SUBSTRING(ancestor_urn FROM {len(old) + 1})"
|
|
301
|
+
)
|
|
302
|
+
descendant_case_conditions.append(
|
|
303
|
+
f"WHEN descendant_urn LIKE '{old}.%' THEN '{new}' || SUBSTRING(descendant_urn FROM {len(old) + 1})"
|
|
304
|
+
)
|
|
305
|
+
where_conditions.extend(
|
|
306
|
+
[f"ancestor_urn LIKE '{old}.%'", f"descendant_urn LIKE '{old}.%'"]
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
sql = f"""
|
|
310
|
+
UPDATE stagedresourceancestor
|
|
311
|
+
SET
|
|
312
|
+
ancestor_urn = CASE
|
|
313
|
+
{' '.join(ancestor_case_conditions)}
|
|
314
|
+
ELSE ancestor_urn
|
|
315
|
+
END,
|
|
316
|
+
descendant_urn = CASE
|
|
317
|
+
{' '.join(descendant_case_conditions)}
|
|
318
|
+
ELSE descendant_urn
|
|
319
|
+
END
|
|
320
|
+
WHERE {' OR '.join(where_conditions)}
|
|
321
|
+
"""
|
|
322
|
+
|
|
323
|
+
conn.execute(sa.text(sql))
|
|
324
|
+
logger.info("Finished updating stagedresourceancestor.")
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def _persist_monitor_key_mapping(conn: Connection, mapping: Dict[str, str]) -> None:
|
|
328
|
+
"""
|
|
329
|
+
Persists the mapping of 'old' monitor keys with dots in them to 'new' keys without dots
|
|
330
|
+
into the dbcache table.
|
|
331
|
+
|
|
332
|
+
NOTE: this mapping is persisted only as a temporary measure (i.e. for a few releases)
|
|
333
|
+
just in case we want to revert this data migration for any reason.
|
|
334
|
+
|
|
335
|
+
|
|
336
|
+
TODO: remove the cache entry in a future migration!
|
|
337
|
+
"""
|
|
338
|
+
logger.info("Persisting monitor key mapping to dbcache...")
|
|
339
|
+
mapping_json = json.dumps(mapping)
|
|
340
|
+
conn.execute(
|
|
341
|
+
sa.text(
|
|
342
|
+
"""
|
|
343
|
+
INSERT INTO dbcache (id, namespace, cache_key, cache_value, created_at, updated_at)
|
|
344
|
+
VALUES ('dbc_' || gen_random_uuid(), :namespace, :cache_key, convert_to(:value, 'UTF8')::bytea, now(), now())
|
|
345
|
+
ON CONFLICT (namespace, cache_key)
|
|
346
|
+
DO UPDATE SET cache_value = EXCLUDED.cache_value, updated_at = now()
|
|
347
|
+
"""
|
|
348
|
+
),
|
|
349
|
+
{
|
|
350
|
+
"namespace": "monitor-config-key-mapping",
|
|
351
|
+
"cache_key": "monitor-config-key-mapping",
|
|
352
|
+
"value": mapping_json,
|
|
353
|
+
},
|
|
354
|
+
)
|
|
355
|
+
logger.info("Finished persisting monitor key mapping to dbcache.")
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
def _drop_fk_constraints(conn: Connection) -> None:
|
|
359
|
+
"""
|
|
360
|
+
Drops the FK constraints on the stagedresourceancestor table.
|
|
361
|
+
|
|
362
|
+
This is done to avoid immediate constraint violations while updating URNs.
|
|
363
|
+
"""
|
|
364
|
+
logger.info("Dropping FK constraints...")
|
|
365
|
+
op.drop_constraint(
|
|
366
|
+
"fk_staged_resource_ancestor_ancestor",
|
|
367
|
+
"stagedresourceancestor",
|
|
368
|
+
type_="foreignkey",
|
|
369
|
+
)
|
|
370
|
+
op.drop_constraint(
|
|
371
|
+
"fk_staged_resource_ancestor_descendant",
|
|
372
|
+
"stagedresourceancestor",
|
|
373
|
+
type_="foreignkey",
|
|
374
|
+
)
|
|
375
|
+
logger.info("Finished dropping FK constraints.")
|
|
376
|
+
|
|
377
|
+
|
|
378
|
+
def _recreate_fk_constraints(conn: Connection) -> None:
|
|
379
|
+
"""
|
|
380
|
+
Recreates the FK constraints on the stagedresourceancestor table.
|
|
381
|
+
"""
|
|
382
|
+
logger.info("Recreating FK constraints...")
|
|
383
|
+
op.create_foreign_key(
|
|
384
|
+
"fk_staged_resource_ancestor_ancestor",
|
|
385
|
+
"stagedresourceancestor",
|
|
386
|
+
"stagedresource",
|
|
387
|
+
["ancestor_urn"],
|
|
388
|
+
["urn"],
|
|
389
|
+
ondelete="CASCADE",
|
|
390
|
+
)
|
|
391
|
+
op.create_foreign_key(
|
|
392
|
+
"fk_staged_resource_ancestor_descendant",
|
|
393
|
+
"stagedresourceancestor",
|
|
394
|
+
"stagedresource",
|
|
395
|
+
["descendant_urn"],
|
|
396
|
+
["urn"],
|
|
397
|
+
ondelete="CASCADE",
|
|
398
|
+
)
|
|
399
|
+
logger.info("Finished recreating FK constraints.")
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
def upgrade() -> None:
|
|
403
|
+
conn = op.get_bind()
|
|
404
|
+
|
|
405
|
+
mapping = _fetch_monitor_key_mapping(conn)
|
|
406
|
+
if mapping:
|
|
407
|
+
logger.info("Beginning monitorconfig key 'dot' migration...")
|
|
408
|
+
logger.info(
|
|
409
|
+
f"Found {len(mapping)} monitor configs with dots to migrate: {list(mapping.keys())}"
|
|
410
|
+
)
|
|
411
|
+
|
|
412
|
+
_persist_monitor_key_mapping(conn, mapping)
|
|
413
|
+
|
|
414
|
+
_drop_fk_constraints(conn)
|
|
415
|
+
|
|
416
|
+
_update_monitorconfig_keys(conn, mapping)
|
|
417
|
+
_update_monitorexecution(conn, mapping)
|
|
418
|
+
_update_stagedresource_monitor_config_id(conn, mapping)
|
|
419
|
+
_update_stagedresource_urns_and_parent(conn, mapping)
|
|
420
|
+
_update_stagedresource_children_array(conn, mapping)
|
|
421
|
+
_update_stagedresource_meta_json(conn, mapping)
|
|
422
|
+
_update_stagedresourceancestor(conn, mapping)
|
|
423
|
+
|
|
424
|
+
_recreate_fk_constraints(conn)
|
|
425
|
+
|
|
426
|
+
# Add CHECK constraint to forbid dots in monitorconfig.key going forward
|
|
427
|
+
op.create_check_constraint(
|
|
428
|
+
"ck_monitorconfig_key_no_dots",
|
|
429
|
+
"monitorconfig",
|
|
430
|
+
"key NOT LIKE '%.%'",
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def downgrade() -> None:
|
|
435
|
+
# Remove the CHECK constraint
|
|
436
|
+
op.drop_constraint("ck_monitorconfig_key_no_dots", "monitorconfig", type_="check")
|
|
437
|
+
# Data downgrade left blank intentionally - if needed, we have the old key mapping in
|
|
438
|
+
# the dbcache entry and a data migration reversal can be written on an adhoc basis.
|
|
439
|
+
pass
|
fides/api/db/base_class.py
CHANGED
|
@@ -21,7 +21,12 @@ from fides.api.util.custom_json_encoder import CustomJSONEncoder, _custom_decode
|
|
|
21
21
|
from fides.api.util.text import to_snake_case
|
|
22
22
|
|
|
23
23
|
T = TypeVar("T", bound="OrmWrappedFidesBase")
|
|
24
|
-
|
|
24
|
+
ALLOWED_CHARS_PATTERN = r"[A-Za-z0-9\-_]"
|
|
25
|
+
DISALLOWED_CHARS_PATTERN = (
|
|
26
|
+
r"[^A-Za-z0-9\-_]" # anything that's _not_ matched by ALLOWED_CHARS_PATTERN above
|
|
27
|
+
)
|
|
28
|
+
ALLOWED_CHARS = re.compile(ALLOWED_CHARS_PATTERN)
|
|
29
|
+
DISALLOWED_CHARS = re.compile(DISALLOWED_CHARS_PATTERN)
|
|
25
30
|
|
|
26
31
|
|
|
27
32
|
class JSONTypeOverride(JSONType): # pylint: disable=W0223
|
|
@@ -109,6 +114,19 @@ class FidesBase:
|
|
|
109
114
|
"""
|
|
110
115
|
return f"{self.__tablename__}.id"
|
|
111
116
|
|
|
117
|
+
@classmethod
|
|
118
|
+
def sanitize_key(cls, proposed_key: str) -> str:
|
|
119
|
+
"""
|
|
120
|
+
Sanitize the key by removing invalid characters.
|
|
121
|
+
|
|
122
|
+
Invalid characters are based on the allowed characters defined in this module.
|
|
123
|
+
Note that this differs slightly from the allowed characters in `sanitize_fides_key`
|
|
124
|
+
from `core/utils.py`; specifically, `.` are disallowed (converted to `_`) here, while
|
|
125
|
+
they are allowed in `sanitize_fides_key`. This is because `.` must be allowed in
|
|
126
|
+
`fides_key`s, but they are not allowed in `key`s.
|
|
127
|
+
"""
|
|
128
|
+
return DISALLOWED_CHARS.sub("_", proposed_key)
|
|
129
|
+
|
|
112
130
|
id = Column(String(255), primary_key=True, index=True, default=generate_uuid)
|
|
113
131
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
|
114
132
|
updated_at = Column(
|
fides/api/models/db_cache.py
CHANGED
|
@@ -14,6 +14,14 @@ class DBCacheNamespace(Enum):
|
|
|
14
14
|
|
|
15
15
|
LIST_PRIVACY_EXPERIENCE = "list-privacy-experience"
|
|
16
16
|
|
|
17
|
+
# NOTE: monitor config key mapping entry is populated by migration
|
|
18
|
+
# 2f3c1a2d6b10_disallow_dot_in_monitor_key_and_update_refs as a temporary means of
|
|
19
|
+
# storing the mapping of 'old' monitor config keys with dots in them
|
|
20
|
+
# to their new keys without dots.
|
|
21
|
+
# This is a safety measure, in case we need to revert that data migration for whatever reason.
|
|
22
|
+
# TODO: remove this cache entry in a future migration.
|
|
23
|
+
MONITOR_CONFIG_KEY_MAPPING = "monitor-config-key-mapping"
|
|
24
|
+
|
|
17
25
|
|
|
18
26
|
class DBCache(Base):
|
|
19
27
|
"""
|
|
@@ -9,6 +9,7 @@ from loguru import logger
|
|
|
9
9
|
from sqlalchemy import (
|
|
10
10
|
ARRAY,
|
|
11
11
|
Boolean,
|
|
12
|
+
CheckConstraint,
|
|
12
13
|
Column,
|
|
13
14
|
DateTime,
|
|
14
15
|
ForeignKey,
|
|
@@ -23,7 +24,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
23
24
|
from sqlalchemy.ext.declarative import declared_attr
|
|
24
25
|
from sqlalchemy.ext.mutable import MutableDict
|
|
25
26
|
from sqlalchemy.future import select
|
|
26
|
-
from sqlalchemy.orm import RelationshipProperty, Session, relationship
|
|
27
|
+
from sqlalchemy.orm import RelationshipProperty, Session, relationship, validates
|
|
27
28
|
from sqlalchemy.orm.query import Query
|
|
28
29
|
|
|
29
30
|
from fides.api.db.base_class import Base, FidesBase
|
|
@@ -172,6 +173,12 @@ class MonitorConfig(Base):
|
|
|
172
173
|
|
|
173
174
|
shared_config = relationship(SharedMonitorConfig)
|
|
174
175
|
|
|
176
|
+
__table_args__ = (
|
|
177
|
+
CheckConstraint( # type: ignore
|
|
178
|
+
"key NOT LIKE '%.%'", name="ck_monitorconfig_key_no_dots"
|
|
179
|
+
),
|
|
180
|
+
)
|
|
181
|
+
|
|
175
182
|
@property
|
|
176
183
|
def classify_params(self) -> dict:
|
|
177
184
|
"""
|
|
@@ -332,6 +339,12 @@ class MonitorConfig(Base):
|
|
|
332
339
|
cron_trigger_dict["month"] = execution_start_date.month
|
|
333
340
|
data["monitor_execution_trigger"] = cron_trigger_dict
|
|
334
341
|
|
|
342
|
+
@validates("key")
|
|
343
|
+
def validate_key_no_dots(self, _key: str, value: str) -> str:
|
|
344
|
+
if value and "." in value:
|
|
345
|
+
raise ValueError('MonitorConfig.key cannot contain "." characters')
|
|
346
|
+
return value
|
|
347
|
+
|
|
335
348
|
|
|
336
349
|
class StagedResourceAncestor(Base):
|
|
337
350
|
"""
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/e1628f15dd5f019b.css" as="style"/><link rel="stylesheet" href="/_next/static/css/e1628f15dd5f019b.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-69658aeaf6155d89.js" defer=""></script><script src="/_next/static/chunks/framework-c92fc3344e6fd165.js" defer=""></script><script src="/_next/static/chunks/main-090643377c8254e6.js" defer=""></script><script src="/_next/static/chunks/pages/_app-65723cd4b8fc36ac.js" defer=""></script><script src="/_next/static/chunks/pages/404-9174cdb70126c2c5.js" defer=""></script><script src="/_next/static/
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/e1628f15dd5f019b.css" as="style"/><link rel="stylesheet" href="/_next/static/css/e1628f15dd5f019b.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-69658aeaf6155d89.js" defer=""></script><script src="/_next/static/chunks/framework-c92fc3344e6fd165.js" defer=""></script><script src="/_next/static/chunks/main-090643377c8254e6.js" defer=""></script><script src="/_next/static/chunks/pages/_app-65723cd4b8fc36ac.js" defer=""></script><script src="/_next/static/chunks/pages/404-9174cdb70126c2c5.js" defer=""></script><script src="/_next/static/nd386tKRkuP7h0W5NiKwd/_buildManifest.js" defer=""></script><script src="/_next/static/nd386tKRkuP7h0W5NiKwd/_ssgManifest.js" defer=""></script><style>.data-ant-cssinjs-cache-path{content:"";}</style></head><body><div id="__next"><div style="height:100%;display:flex"></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/404","query":{},"buildId":"nd386tKRkuP7h0W5NiKwd","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/e1628f15dd5f019b.css" as="style"/><link rel="stylesheet" href="/_next/static/css/e1628f15dd5f019b.css" data-n-g=""/><link rel="preload" href="/_next/static/css/304c6f148886a8d4.css" as="style"/><link rel="stylesheet" href="/_next/static/css/304c6f148886a8d4.css" data-n-p=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-69658aeaf6155d89.js" defer=""></script><script src="/_next/static/chunks/framework-c92fc3344e6fd165.js" defer=""></script><script src="/_next/static/chunks/main-090643377c8254e6.js" defer=""></script><script src="/_next/static/chunks/pages/_app-65723cd4b8fc36ac.js" defer=""></script><script src="/_next/static/chunks/8765-f622a35b40a7ec63.js" defer=""></script><script src="/_next/static/chunks/9278-08cc704317fe535e.js" defer=""></script><script src="/_next/static/chunks/5163-e682273cd76a7d07.js" defer=""></script><script src="/_next/static/chunks/699-8ca44b0de9fa20f0.js" defer=""></script><script src="/_next/static/chunks/5277-e8a036319456127f.js" defer=""></script><script src="/_next/static/chunks/409-5bc4369b80a8c11d.js" defer=""></script><script src="/_next/static/chunks/401-741bb31b586b7c96.js" defer=""></script><script src="/_next/static/chunks/9951-4df2b67e0def5500.js" defer=""></script><script src="/_next/static/chunks/6780-7d28e030f6516e5d.js" defer=""></script><script src="/_next/static/chunks/3450-9314e1b15df8a8da.js" defer=""></script><script src="/_next/static/chunks/pages/add-systems/manual-621416493c89ef01.js" defer=""></script><script src="/_next/static/
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/e1628f15dd5f019b.css" as="style"/><link rel="stylesheet" href="/_next/static/css/e1628f15dd5f019b.css" data-n-g=""/><link rel="preload" href="/_next/static/css/304c6f148886a8d4.css" as="style"/><link rel="stylesheet" href="/_next/static/css/304c6f148886a8d4.css" data-n-p=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-69658aeaf6155d89.js" defer=""></script><script src="/_next/static/chunks/framework-c92fc3344e6fd165.js" defer=""></script><script src="/_next/static/chunks/main-090643377c8254e6.js" defer=""></script><script src="/_next/static/chunks/pages/_app-65723cd4b8fc36ac.js" defer=""></script><script src="/_next/static/chunks/8765-f622a35b40a7ec63.js" defer=""></script><script src="/_next/static/chunks/9278-08cc704317fe535e.js" defer=""></script><script src="/_next/static/chunks/5163-e682273cd76a7d07.js" defer=""></script><script src="/_next/static/chunks/699-8ca44b0de9fa20f0.js" defer=""></script><script src="/_next/static/chunks/5277-e8a036319456127f.js" defer=""></script><script src="/_next/static/chunks/409-5bc4369b80a8c11d.js" defer=""></script><script src="/_next/static/chunks/401-741bb31b586b7c96.js" defer=""></script><script src="/_next/static/chunks/9951-4df2b67e0def5500.js" defer=""></script><script src="/_next/static/chunks/6780-7d28e030f6516e5d.js" defer=""></script><script src="/_next/static/chunks/3450-9314e1b15df8a8da.js" defer=""></script><script src="/_next/static/chunks/pages/add-systems/manual-621416493c89ef01.js" defer=""></script><script src="/_next/static/nd386tKRkuP7h0W5NiKwd/_buildManifest.js" defer=""></script><script src="/_next/static/nd386tKRkuP7h0W5NiKwd/_ssgManifest.js" defer=""></script><style>.data-ant-cssinjs-cache-path{content:"";}</style></head><body><div id="__next"><div style="height:100%;display:flex"></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/add-systems/manual","query":{},"buildId":"nd386tKRkuP7h0W5NiKwd","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/e1628f15dd5f019b.css" as="style"/><link rel="stylesheet" href="/_next/static/css/e1628f15dd5f019b.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-69658aeaf6155d89.js" defer=""></script><script src="/_next/static/chunks/framework-c92fc3344e6fd165.js" defer=""></script><script src="/_next/static/chunks/main-090643377c8254e6.js" defer=""></script><script src="/_next/static/chunks/pages/_app-65723cd4b8fc36ac.js" defer=""></script><script src="/_next/static/chunks/401-741bb31b586b7c96.js" defer=""></script><script src="/_next/static/chunks/3923-a33633feba5e655e.js" defer=""></script><script src="/_next/static/chunks/796-0b768155bf20505f.js" defer=""></script><script src="/_next/static/chunks/pages/add-systems/multiple-0b9908c3e1dfe49e.js" defer=""></script><script src="/_next/static/
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/e1628f15dd5f019b.css" as="style"/><link rel="stylesheet" href="/_next/static/css/e1628f15dd5f019b.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-69658aeaf6155d89.js" defer=""></script><script src="/_next/static/chunks/framework-c92fc3344e6fd165.js" defer=""></script><script src="/_next/static/chunks/main-090643377c8254e6.js" defer=""></script><script src="/_next/static/chunks/pages/_app-65723cd4b8fc36ac.js" defer=""></script><script src="/_next/static/chunks/401-741bb31b586b7c96.js" defer=""></script><script src="/_next/static/chunks/3923-a33633feba5e655e.js" defer=""></script><script src="/_next/static/chunks/796-0b768155bf20505f.js" defer=""></script><script src="/_next/static/chunks/pages/add-systems/multiple-0b9908c3e1dfe49e.js" defer=""></script><script src="/_next/static/nd386tKRkuP7h0W5NiKwd/_buildManifest.js" defer=""></script><script src="/_next/static/nd386tKRkuP7h0W5NiKwd/_ssgManifest.js" defer=""></script><style>.data-ant-cssinjs-cache-path{content:"";}</style></head><body><div id="__next"><div style="height:100%;display:flex"></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/add-systems/multiple","query":{},"buildId":"nd386tKRkuP7h0W5NiKwd","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/e1628f15dd5f019b.css" as="style"/><link rel="stylesheet" href="/_next/static/css/e1628f15dd5f019b.css" data-n-g=""/><link rel="preload" href="/_next/static/css/972dc7eef106ee7c.css" as="style"/><link rel="stylesheet" href="/_next/static/css/972dc7eef106ee7c.css" data-n-p=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-69658aeaf6155d89.js" defer=""></script><script src="/_next/static/chunks/framework-c92fc3344e6fd165.js" defer=""></script><script src="/_next/static/chunks/main-090643377c8254e6.js" defer=""></script><script src="/_next/static/chunks/pages/_app-65723cd4b8fc36ac.js" defer=""></script><script src="/_next/static/chunks/pages/add-systems-18e96ce81dab51a4.js" defer=""></script><script src="/_next/static/
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/e1628f15dd5f019b.css" as="style"/><link rel="stylesheet" href="/_next/static/css/e1628f15dd5f019b.css" data-n-g=""/><link rel="preload" href="/_next/static/css/972dc7eef106ee7c.css" as="style"/><link rel="stylesheet" href="/_next/static/css/972dc7eef106ee7c.css" data-n-p=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-69658aeaf6155d89.js" defer=""></script><script src="/_next/static/chunks/framework-c92fc3344e6fd165.js" defer=""></script><script src="/_next/static/chunks/main-090643377c8254e6.js" defer=""></script><script src="/_next/static/chunks/pages/_app-65723cd4b8fc36ac.js" defer=""></script><script src="/_next/static/chunks/pages/add-systems-18e96ce81dab51a4.js" defer=""></script><script src="/_next/static/nd386tKRkuP7h0W5NiKwd/_buildManifest.js" defer=""></script><script src="/_next/static/nd386tKRkuP7h0W5NiKwd/_ssgManifest.js" defer=""></script><style>.data-ant-cssinjs-cache-path{content:"";}</style></head><body><div id="__next"><div style="height:100%;display:flex"></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/add-systems","query":{},"buildId":"nd386tKRkuP7h0W5NiKwd","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/e1628f15dd5f019b.css" as="style"/><link rel="stylesheet" href="/_next/static/css/e1628f15dd5f019b.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-69658aeaf6155d89.js" defer=""></script><script src="/_next/static/chunks/framework-c92fc3344e6fd165.js" defer=""></script><script src="/_next/static/chunks/main-090643377c8254e6.js" defer=""></script><script src="/_next/static/chunks/pages/_app-65723cd4b8fc36ac.js" defer=""></script><script src="/_next/static/chunks/401-741bb31b586b7c96.js" defer=""></script><script src="/_next/static/chunks/3923-a33633feba5e655e.js" defer=""></script><script src="/_next/static/chunks/796-0b768155bf20505f.js" defer=""></script><script src="/_next/static/chunks/pages/consent/configure/add-vendors-5bb1b31ae8752250.js" defer=""></script><script src="/_next/static/
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/e1628f15dd5f019b.css" as="style"/><link rel="stylesheet" href="/_next/static/css/e1628f15dd5f019b.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-69658aeaf6155d89.js" defer=""></script><script src="/_next/static/chunks/framework-c92fc3344e6fd165.js" defer=""></script><script src="/_next/static/chunks/main-090643377c8254e6.js" defer=""></script><script src="/_next/static/chunks/pages/_app-65723cd4b8fc36ac.js" defer=""></script><script src="/_next/static/chunks/401-741bb31b586b7c96.js" defer=""></script><script src="/_next/static/chunks/3923-a33633feba5e655e.js" defer=""></script><script src="/_next/static/chunks/796-0b768155bf20505f.js" defer=""></script><script src="/_next/static/chunks/pages/consent/configure/add-vendors-5bb1b31ae8752250.js" defer=""></script><script src="/_next/static/nd386tKRkuP7h0W5NiKwd/_buildManifest.js" defer=""></script><script src="/_next/static/nd386tKRkuP7h0W5NiKwd/_ssgManifest.js" defer=""></script><style>.data-ant-cssinjs-cache-path{content:"";}</style></head><body><div id="__next"><div style="height:100%;display:flex"></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/consent/configure/add-vendors","query":{},"buildId":"nd386tKRkuP7h0W5NiKwd","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/e1628f15dd5f019b.css" as="style"/><link rel="stylesheet" href="/_next/static/css/e1628f15dd5f019b.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-69658aeaf6155d89.js" defer=""></script><script src="/_next/static/chunks/framework-c92fc3344e6fd165.js" defer=""></script><script src="/_next/static/chunks/main-090643377c8254e6.js" defer=""></script><script src="/_next/static/chunks/pages/_app-65723cd4b8fc36ac.js" defer=""></script><script src="/_next/static/chunks/401-741bb31b586b7c96.js" defer=""></script><script src="/_next/static/chunks/3923-a33633feba5e655e.js" defer=""></script><script src="/_next/static/chunks/pages/consent/configure-54d7c7310763c66d.js" defer=""></script><script src="/_next/static/
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/e1628f15dd5f019b.css" as="style"/><link rel="stylesheet" href="/_next/static/css/e1628f15dd5f019b.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-69658aeaf6155d89.js" defer=""></script><script src="/_next/static/chunks/framework-c92fc3344e6fd165.js" defer=""></script><script src="/_next/static/chunks/main-090643377c8254e6.js" defer=""></script><script src="/_next/static/chunks/pages/_app-65723cd4b8fc36ac.js" defer=""></script><script src="/_next/static/chunks/401-741bb31b586b7c96.js" defer=""></script><script src="/_next/static/chunks/3923-a33633feba5e655e.js" defer=""></script><script src="/_next/static/chunks/pages/consent/configure-54d7c7310763c66d.js" defer=""></script><script src="/_next/static/nd386tKRkuP7h0W5NiKwd/_buildManifest.js" defer=""></script><script src="/_next/static/nd386tKRkuP7h0W5NiKwd/_ssgManifest.js" defer=""></script><style>.data-ant-cssinjs-cache-path{content:"";}</style></head><body><div id="__next"><div style="height:100%;display:flex"></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/consent/configure","query":{},"buildId":"nd386tKRkuP7h0W5NiKwd","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/e1628f15dd5f019b.css" as="style"/><link rel="stylesheet" href="/_next/static/css/e1628f15dd5f019b.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-69658aeaf6155d89.js" defer=""></script><script src="/_next/static/chunks/framework-c92fc3344e6fd165.js" defer=""></script><script src="/_next/static/chunks/main-090643377c8254e6.js" defer=""></script><script src="/_next/static/chunks/pages/_app-65723cd4b8fc36ac.js" defer=""></script><script src="/_next/static/chunks/8765-f622a35b40a7ec63.js" defer=""></script><script src="/_next/static/chunks/3662-f6a1ddca5ee42076.js" defer=""></script><script src="/_next/static/chunks/6419-d0c00d661b01f8fa.js" defer=""></script><script src="/_next/static/chunks/6084-02abe12327fc3dbc.js" defer=""></script><script src="/_next/static/chunks/1817-c90365325f8a3d75.js" defer=""></script><script src="/_next/static/chunks/pages/consent/privacy-experience/%5Bid%5D-4e4d9426743b5cb4.js" defer=""></script><script src="/_next/static/
|
|
1
|
+
<!DOCTYPE html><html lang="en"><head><meta charSet="utf-8"/><meta name="viewport" content="width=device-width"/><meta name="next-head-count" content="2"/><link data-next-font="" rel="preconnect" href="/" crossorigin="anonymous"/><link rel="preload" href="/_next/static/css/e1628f15dd5f019b.css" as="style"/><link rel="stylesheet" href="/_next/static/css/e1628f15dd5f019b.css" data-n-g=""/><noscript data-n-css=""></noscript><script defer="" nomodule="" src="/_next/static/chunks/polyfills-42372ed130431b0a.js"></script><script src="/_next/static/chunks/webpack-69658aeaf6155d89.js" defer=""></script><script src="/_next/static/chunks/framework-c92fc3344e6fd165.js" defer=""></script><script src="/_next/static/chunks/main-090643377c8254e6.js" defer=""></script><script src="/_next/static/chunks/pages/_app-65723cd4b8fc36ac.js" defer=""></script><script src="/_next/static/chunks/8765-f622a35b40a7ec63.js" defer=""></script><script src="/_next/static/chunks/3662-f6a1ddca5ee42076.js" defer=""></script><script src="/_next/static/chunks/6419-d0c00d661b01f8fa.js" defer=""></script><script src="/_next/static/chunks/6084-02abe12327fc3dbc.js" defer=""></script><script src="/_next/static/chunks/1817-c90365325f8a3d75.js" defer=""></script><script src="/_next/static/chunks/pages/consent/privacy-experience/%5Bid%5D-4e4d9426743b5cb4.js" defer=""></script><script src="/_next/static/nd386tKRkuP7h0W5NiKwd/_buildManifest.js" defer=""></script><script src="/_next/static/nd386tKRkuP7h0W5NiKwd/_ssgManifest.js" defer=""></script><style>.data-ant-cssinjs-cache-path{content:"";}</style></head><body><div id="__next"><div style="height:100%;display:flex"></div></div><script id="__NEXT_DATA__" type="application/json">{"props":{"pageProps":{}},"page":"/consent/privacy-experience/[id]","query":{},"buildId":"nd386tKRkuP7h0W5NiKwd","nextExport":true,"autoExport":true,"isFallback":false,"scriptLoader":[]}</script></body></html>
|