ethyca-fides 2.64.1b0__py2.py3-none-any.whl → 2.64.1b2__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.64.1b0.dist-info → ethyca_fides-2.64.1b2.dist-info}/METADATA +1 -1
- {ethyca_fides-2.64.1b0.dist-info → ethyca_fides-2.64.1b2.dist-info}/RECORD +106 -102
- fides/_version.py +3 -3
- fides/api/alembic/migrations/versions/6a76a1fa4f3f_add_manual_task_instance_table.py +256 -0
- fides/api/alembic/migrations/versions/aadfe83c5644_add_manual_task_to_connectiontype_enum.py +46 -0
- fides/api/db/base.py +4 -0
- fides/api/models/connectionconfig.py +11 -0
- fides/api/models/manual_tasks/__init__.py +7 -1
- fides/api/models/manual_tasks/manual_task.py +19 -3
- fides/api/models/manual_tasks/manual_task_config.py +39 -6
- fides/api/models/manual_tasks/manual_task_instance.py +187 -0
- fides/api/models/manual_tasks/manual_task_log.py +20 -7
- fides/api/schemas/manual_tasks/manual_task_schemas.py +42 -0
- fides/api/schemas/manual_tasks/manual_task_status.py +107 -46
- fides/api/service/connectors/postgres_connector.py +2 -2
- fides/common/api/v1/urn_registry.py +4 -0
- fides/service/manual_tasks/manual_task_config_service.py +17 -5
- fides/service/manual_tasks/manual_task_instance_service.py +285 -0
- fides/service/manual_tasks/manual_task_service.py +66 -10
- 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.64.1b0.dist-info → ethyca_fides-2.64.1b2.dist-info}/WHEEL +0 -0
- {ethyca_fides-2.64.1b0.dist-info → ethyca_fides-2.64.1b2.dist-info}/entry_points.txt +0 -0
- {ethyca_fides-2.64.1b0.dist-info → ethyca_fides-2.64.1b2.dist-info}/licenses/LICENSE +0 -0
- {ethyca_fides-2.64.1b0.dist-info → ethyca_fides-2.64.1b2.dist-info}/top_level.txt +0 -0
- /fides/ui-build/static/admin/_next/static/{nRQ3pmK_d3F5PJE39rP2h → xYqpgK9yFhQK_wL_F_kAF}/_buildManifest.js +0 -0
- /fides/ui-build/static/admin/_next/static/{nRQ3pmK_d3F5PJE39rP2h → xYqpgK9yFhQK_wL_F_kAF}/_ssgManifest.js +0 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
"""add_manual_task_to_connectiontype_enum
|
2
|
+
|
3
|
+
Revision ID: aadfe83c5644
|
4
|
+
Revises: 6a76a1fa4f3f
|
5
|
+
Create Date: 2025-06-19 18:55:08.131278
|
6
|
+
|
7
|
+
"""
|
8
|
+
|
9
|
+
import sqlalchemy as sa
|
10
|
+
from alembic import op
|
11
|
+
|
12
|
+
# revision identifiers, used by Alembic.
|
13
|
+
revision = "aadfe83c5644"
|
14
|
+
down_revision = "6a76a1fa4f3f"
|
15
|
+
branch_labels = None
|
16
|
+
depends_on = None
|
17
|
+
|
18
|
+
|
19
|
+
def upgrade():
|
20
|
+
# Add manual_task to ConnectionType enum
|
21
|
+
op.execute("alter type connectiontype rename to connectiontype_old")
|
22
|
+
op.execute(
|
23
|
+
"create type connectiontype as enum('attentive_email', 'bigquery', 'datahub', 'dynamodb', 'fides', 'generic_consent_email', 'generic_erasure_email', 'dynamic_erasure_email', 'google_cloud_sql_mysql', 'google_cloud_sql_postgres', 'https', 'manual', 'manual_webhook', 'manual_task', 'mariadb', 'mongodb', 'mssql', 'mysql', 'okta', 'postgres', 'rds_mysql', 'rds_postgres', 'redshift', 's3', 'saas', 'scylla', 'snowflake', 'sovrn', 'timescale', 'website')"
|
24
|
+
)
|
25
|
+
op.execute(
|
26
|
+
(
|
27
|
+
"alter table connectionconfig alter column connection_type type connectiontype using "
|
28
|
+
"connection_type::text::connectiontype"
|
29
|
+
)
|
30
|
+
)
|
31
|
+
op.execute("drop type connectiontype_old")
|
32
|
+
|
33
|
+
|
34
|
+
def downgrade():
|
35
|
+
# Remove manual_task from ConnectionType enum
|
36
|
+
op.execute("alter type connectiontype rename to connectiontype_old")
|
37
|
+
op.execute(
|
38
|
+
"create type connectiontype as enum('attentive_email', 'bigquery', 'datahub', 'dynamodb', 'fides', 'generic_consent_email', 'generic_erasure_email', 'dynamic_erasure_email', 'google_cloud_sql_mysql', 'google_cloud_sql_postgres', 'https', 'manual', 'manual_webhook', 'mariadb', 'mongodb', 'mssql', 'mysql', 'okta', 'postgres', 'rds_mysql', 'rds_postgres', 'redshift', 's3', 'saas', 'scylla', 'snowflake', 'sovrn', 'timescale', 'website')"
|
39
|
+
)
|
40
|
+
op.execute(
|
41
|
+
(
|
42
|
+
"alter table connectionconfig alter column connection_type type connectiontype using "
|
43
|
+
"connection_type::text::connectiontype"
|
44
|
+
)
|
45
|
+
)
|
46
|
+
op.execute("drop type connectiontype_old")
|
fides/api/db/base.py
CHANGED
@@ -36,6 +36,10 @@ from fides.api.models.manual_tasks.manual_task_config import (
|
|
36
36
|
ManualTaskConfig,
|
37
37
|
ManualTaskConfigField,
|
38
38
|
)
|
39
|
+
from fides.api.models.manual_tasks.manual_task_instance import (
|
40
|
+
ManualTaskInstance,
|
41
|
+
ManualTaskSubmission,
|
42
|
+
)
|
39
43
|
from fides.api.models.manual_tasks.manual_task_log import ManualTaskLog
|
40
44
|
from fides.api.models.manual_webhook import AccessManualWebhook
|
41
45
|
from fides.api.models.messaging import MessagingConfig
|
@@ -53,6 +53,7 @@ class ConnectionType(enum.Enum):
|
|
53
53
|
https = "https"
|
54
54
|
manual = "manual" # Deprecated - use manual_webhook instead
|
55
55
|
manual_webhook = "manual_webhook" # Runs upfront before the traversal
|
56
|
+
manual_task = "manual_task" # Manual task integration
|
56
57
|
mariadb = "mariadb"
|
57
58
|
mongodb = "mongodb"
|
58
59
|
mssql = "mssql"
|
@@ -88,6 +89,7 @@ class ConnectionType(enum.Enum):
|
|
88
89
|
ConnectionType.google_cloud_sql_postgres.value: "Google Cloud SQL for Postgres",
|
89
90
|
ConnectionType.https.value: "Policy Webhook",
|
90
91
|
ConnectionType.manual_webhook.value: "Manual Process",
|
92
|
+
ConnectionType.manual_task.value: "Manual Task",
|
91
93
|
ConnectionType.manual.value: "Manual Connector",
|
92
94
|
ConnectionType.mariadb.value: "MariaDB",
|
93
95
|
ConnectionType.mongodb.value: "MongoDB",
|
@@ -132,6 +134,7 @@ class ConnectionType(enum.Enum):
|
|
132
134
|
ConnectionType.google_cloud_sql_postgres.value: SystemType.database,
|
133
135
|
ConnectionType.https.value: SystemType.manual,
|
134
136
|
ConnectionType.manual_webhook.value: SystemType.manual,
|
137
|
+
ConnectionType.manual_task.value: SystemType.manual,
|
135
138
|
ConnectionType.manual.value: SystemType.manual,
|
136
139
|
ConnectionType.mariadb.value: SystemType.database,
|
137
140
|
ConnectionType.mongodb.value: SystemType.database,
|
@@ -229,6 +232,14 @@ class ConnectionConfig(Base):
|
|
229
232
|
uselist=False,
|
230
233
|
)
|
231
234
|
|
235
|
+
manual_task = relationship( # type: ignore[misc]
|
236
|
+
"ManualTask",
|
237
|
+
primaryjoin="and_(ConnectionConfig.id == foreign(ManualTask.parent_entity_id), "
|
238
|
+
"ManualTask.parent_entity_type == 'connection_config')",
|
239
|
+
cascade="delete",
|
240
|
+
uselist=False,
|
241
|
+
)
|
242
|
+
|
232
243
|
pre_approval_webhooks = relationship( # type: ignore[misc]
|
233
244
|
"PreApprovalWebhook",
|
234
245
|
back_populates="connection_config",
|
@@ -3,12 +3,18 @@ from fides.api.models.manual_tasks.manual_task_config import (
|
|
3
3
|
ManualTaskConfig,
|
4
4
|
ManualTaskConfigField,
|
5
5
|
)
|
6
|
+
from fides.api.models.manual_tasks.manual_task_instance import (
|
7
|
+
ManualTaskInstance,
|
8
|
+
ManualTaskSubmission,
|
9
|
+
)
|
6
10
|
from fides.api.models.manual_tasks.manual_task_log import ManualTaskLog
|
7
11
|
|
8
12
|
__all__ = [
|
9
13
|
"ManualTask",
|
10
14
|
"ManualTaskConfig",
|
11
15
|
"ManualTaskConfigField",
|
12
|
-
"
|
16
|
+
"ManualTaskInstance",
|
13
17
|
"ManualTaskLog",
|
18
|
+
"ManualTaskReference",
|
19
|
+
"ManualTaskSubmission",
|
14
20
|
]
|
@@ -1,6 +1,6 @@
|
|
1
1
|
from typing import TYPE_CHECKING, Any
|
2
2
|
|
3
|
-
from sqlalchemy import Column,
|
3
|
+
from sqlalchemy import Column, ForeignKey, String
|
4
4
|
from sqlalchemy.ext.declarative import declared_attr
|
5
5
|
from sqlalchemy.orm import Session, relationship
|
6
6
|
|
@@ -18,6 +18,10 @@ if TYPE_CHECKING: # pragma: no cover
|
|
18
18
|
from fides.api.models.manual_tasks.manual_task_config import ( # pragma: no cover
|
19
19
|
ManualTaskConfig,
|
20
20
|
)
|
21
|
+
from fides.api.models.manual_tasks.manual_task_instance import (
|
22
|
+
ManualTaskInstance,
|
23
|
+
ManualTaskSubmission,
|
24
|
+
)
|
21
25
|
|
22
26
|
|
23
27
|
class ManualTask(Base):
|
@@ -48,7 +52,6 @@ class ManualTask(Base):
|
|
48
52
|
nullable=False,
|
49
53
|
default=ManualTaskParentEntityType.connection_config,
|
50
54
|
)
|
51
|
-
due_date = Column(DateTime, nullable=True)
|
52
55
|
|
53
56
|
# Relationships
|
54
57
|
references = relationship(
|
@@ -68,6 +71,19 @@ class ManualTask(Base):
|
|
68
71
|
"ManualTaskConfig",
|
69
72
|
back_populates="task",
|
70
73
|
cascade="all, delete-orphan",
|
74
|
+
uselist=True,
|
75
|
+
)
|
76
|
+
instances = relationship(
|
77
|
+
"ManualTaskInstance",
|
78
|
+
back_populates="task",
|
79
|
+
viewonly=True,
|
80
|
+
uselist=True,
|
81
|
+
)
|
82
|
+
submissions = relationship(
|
83
|
+
"ManualTaskSubmission",
|
84
|
+
back_populates="task",
|
85
|
+
uselist=True,
|
86
|
+
viewonly=True,
|
71
87
|
)
|
72
88
|
|
73
89
|
# Properties
|
@@ -117,4 +133,4 @@ class ManualTaskReference(Base):
|
|
117
133
|
reference_type = Column(EnumColumn(ManualTaskReferenceType), nullable=False)
|
118
134
|
|
119
135
|
# Relationships
|
120
|
-
task = relationship("ManualTask", back_populates="references")
|
136
|
+
task = relationship("ManualTask", back_populates="references", viewonly=True)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
from typing import TYPE_CHECKING, Any, Optional
|
1
|
+
from typing import TYPE_CHECKING, Any, Optional, cast
|
2
2
|
|
3
3
|
from sqlalchemy import Boolean, Column, ForeignKey, Integer, String
|
4
4
|
from sqlalchemy.dialects.postgresql import JSONB
|
@@ -13,10 +13,19 @@ from fides.api.schemas.manual_tasks.manual_task_config import (
|
|
13
13
|
ManualTaskFieldMetadata,
|
14
14
|
ManualTaskFieldType,
|
15
15
|
)
|
16
|
-
from fides.api.schemas.manual_tasks.manual_task_schemas import
|
16
|
+
from fides.api.schemas.manual_tasks.manual_task_schemas import (
|
17
|
+
ManualTaskExecutionTiming,
|
18
|
+
ManualTaskLogStatus,
|
19
|
+
)
|
17
20
|
|
18
21
|
if TYPE_CHECKING: # pragma: no cover
|
19
22
|
from fides.api.models.manual_tasks.manual_task import ManualTask # pragma: no cover
|
23
|
+
from fides.api.models.manual_tasks.manual_task_instance import (
|
24
|
+
ManualTaskInstance, # pragma: no cover
|
25
|
+
)
|
26
|
+
from fides.api.models.manual_tasks.manual_task_instance import (
|
27
|
+
ManualTaskSubmission, # pragma: no cover
|
28
|
+
)
|
20
29
|
|
21
30
|
|
22
31
|
class ManualTaskConfig(Base):
|
@@ -32,9 +41,20 @@ class ManualTaskConfig(Base):
|
|
32
41
|
config_type = Column(EnumColumn(ManualTaskConfigurationType), nullable=False)
|
33
42
|
version = Column(Integer, nullable=False, default=1)
|
34
43
|
is_current = Column(Boolean, nullable=False, default=True)
|
44
|
+
execution_timing = Column(
|
45
|
+
EnumColumn(ManualTaskExecutionTiming),
|
46
|
+
nullable=False,
|
47
|
+
default=ManualTaskExecutionTiming.pre_execution,
|
48
|
+
)
|
35
49
|
|
36
50
|
# Relationships
|
37
|
-
task = relationship("ManualTask", back_populates="configs")
|
51
|
+
task = relationship("ManualTask", back_populates="configs", viewonly=True)
|
52
|
+
instances = relationship(
|
53
|
+
"ManualTaskInstance", back_populates="config", uselist=True, viewonly=True
|
54
|
+
)
|
55
|
+
submissions = relationship(
|
56
|
+
"ManualTaskSubmission", back_populates="config", uselist=True, viewonly=True
|
57
|
+
)
|
38
58
|
field_definitions = relationship(
|
39
59
|
"ManualTaskConfigField",
|
40
60
|
back_populates="config",
|
@@ -45,7 +65,7 @@ class ManualTaskConfig(Base):
|
|
45
65
|
"ManualTaskLog",
|
46
66
|
back_populates="config",
|
47
67
|
primaryjoin="ManualTaskConfig.id == ManualTaskLog.config_id",
|
48
|
-
|
68
|
+
cascade="all, delete-orphan",
|
49
69
|
)
|
50
70
|
|
51
71
|
@classmethod
|
@@ -95,14 +115,27 @@ class ManualTaskConfigField(Base):
|
|
95
115
|
field_type = Column(
|
96
116
|
EnumColumn(ManualTaskFieldType), nullable=False
|
97
117
|
) # Using ManualTaskFieldType
|
98
|
-
field_metadata
|
118
|
+
field_metadata: dict[str, Any] = cast(
|
119
|
+
dict[str, Any], Column(JSONB, nullable=False, default={})
|
120
|
+
)
|
99
121
|
|
100
122
|
# Relationships
|
101
|
-
config = relationship(
|
123
|
+
config = relationship(
|
124
|
+
"ManualTaskConfig", back_populates="field_definitions", viewonly=True
|
125
|
+
)
|
126
|
+
submissions = relationship(
|
127
|
+
"ManualTaskSubmission",
|
128
|
+
back_populates="field",
|
129
|
+
uselist=True,
|
130
|
+
cascade="all, delete-orphan",
|
131
|
+
)
|
102
132
|
|
103
133
|
@property
|
104
134
|
def field_metadata_model(self) -> ManualTaskFieldMetadata:
|
105
135
|
"""Get the field metadata as a Pydantic model."""
|
136
|
+
assert isinstance(
|
137
|
+
self.field_metadata, dict
|
138
|
+
), "field_metadata must be a dictionary"
|
106
139
|
return ManualTaskFieldMetadata.model_validate(self.field_metadata)
|
107
140
|
|
108
141
|
@classmethod
|
@@ -0,0 +1,187 @@
|
|
1
|
+
from datetime import datetime, timezone
|
2
|
+
from typing import TYPE_CHECKING, Optional
|
3
|
+
|
4
|
+
from sqlalchemy import Column, DateTime, ForeignKey, String
|
5
|
+
from sqlalchemy.dialects.postgresql import JSONB
|
6
|
+
from sqlalchemy.ext.declarative import declared_attr
|
7
|
+
from sqlalchemy.orm import relationship
|
8
|
+
|
9
|
+
from fides.api.db.base_class import Base
|
10
|
+
from fides.api.db.util import EnumColumn
|
11
|
+
from fides.api.models.manual_tasks.manual_task_config import ManualTaskConfigField
|
12
|
+
from fides.api.schemas.manual_tasks.manual_task_schemas import ManualTaskEntityType
|
13
|
+
from fides.api.schemas.manual_tasks.manual_task_status import (
|
14
|
+
StatusTransitionMixin,
|
15
|
+
StatusType,
|
16
|
+
)
|
17
|
+
|
18
|
+
if TYPE_CHECKING: # pragma: no cover
|
19
|
+
from fides.api.models.attachment import Attachment # pragma: no cover
|
20
|
+
from fides.api.models.fides_user import FidesUser # pragma: no cover
|
21
|
+
from fides.api.models.manual_tasks.manual_task import ManualTask # pragma: no cover
|
22
|
+
from fides.api.models.manual_tasks.manual_task_config import (
|
23
|
+
ManualTaskConfig, # pragma: no cover
|
24
|
+
)
|
25
|
+
from fides.api.models.manual_tasks.manual_task_log import (
|
26
|
+
ManualTaskLog, # pragma: no cover; pragma: no cover
|
27
|
+
)
|
28
|
+
|
29
|
+
|
30
|
+
class ManualTaskInstance(Base, StatusTransitionMixin):
|
31
|
+
"""Model for tracking task status per entity instance.
|
32
|
+
|
33
|
+
This model implements StatusTransitionProtocol through the StatusTransitionMixin.
|
34
|
+
"""
|
35
|
+
|
36
|
+
@declared_attr
|
37
|
+
def __tablename__(cls) -> str:
|
38
|
+
"""Overriding base class method to set the table name."""
|
39
|
+
return "manual_task_instance"
|
40
|
+
|
41
|
+
# Database columns
|
42
|
+
task_id: Column[str] = Column(String, ForeignKey("manual_task.id"), nullable=False)
|
43
|
+
config_id: Column[str] = Column(
|
44
|
+
String, ForeignKey("manual_task_config.id"), nullable=False
|
45
|
+
)
|
46
|
+
# entity id is the entity that the instance relates to
|
47
|
+
# (e.g. a privacy request is an entity that has its own manual task instance)
|
48
|
+
entity_id: Column[str] = Column(String, nullable=False)
|
49
|
+
entity_type: Column[ManualTaskEntityType] = Column(
|
50
|
+
EnumColumn(ManualTaskEntityType), nullable=False
|
51
|
+
)
|
52
|
+
# ingnore[assignment] because the mypy and sqlalchemy types mismatch
|
53
|
+
# upgrading to 2.0 allows mapping which provides better type safety visibility.
|
54
|
+
status: Column[StatusType] = Column(EnumColumn(StatusType), nullable=False, default=StatusType.pending) # type: ignore[assignment]
|
55
|
+
completed_at: Column[Optional[datetime]] = Column(DateTime, nullable=True) # type: ignore[assignment]
|
56
|
+
completed_by_id: Column[Optional[str]] = Column(String, nullable=True) # type: ignore[assignment]
|
57
|
+
due_date: Column[Optional[datetime]] = Column(DateTime, nullable=True)
|
58
|
+
|
59
|
+
# Relationships
|
60
|
+
task = relationship("ManualTask", back_populates="instances")
|
61
|
+
config = relationship("ManualTaskConfig", back_populates="instances")
|
62
|
+
submissions = relationship(
|
63
|
+
"ManualTaskSubmission",
|
64
|
+
back_populates="instance",
|
65
|
+
cascade="all, delete-orphan",
|
66
|
+
uselist=True,
|
67
|
+
)
|
68
|
+
logs = relationship(
|
69
|
+
"ManualTaskLog",
|
70
|
+
back_populates="instance",
|
71
|
+
primaryjoin="ManualTaskInstance.id == ManualTaskLog.instance_id",
|
72
|
+
cascade="all, delete-orphan",
|
73
|
+
order_by="ManualTaskLog.created_at",
|
74
|
+
uselist=True,
|
75
|
+
)
|
76
|
+
attachments = relationship(
|
77
|
+
"Attachment",
|
78
|
+
secondary="attachment_reference",
|
79
|
+
primaryjoin="and_(ManualTaskInstance.id == ManualTaskSubmission.instance_id, "
|
80
|
+
"ManualTaskSubmission.id == AttachmentReference.reference_id, "
|
81
|
+
"AttachmentReference.reference_type == 'manual_task_submission')",
|
82
|
+
secondaryjoin="Attachment.id == AttachmentReference.attachment_id",
|
83
|
+
order_by="Attachment.created_at",
|
84
|
+
viewonly=True,
|
85
|
+
uselist=True,
|
86
|
+
)
|
87
|
+
|
88
|
+
@property
|
89
|
+
def required_fields(self) -> list["ManualTaskConfigField"]:
|
90
|
+
"""Get all required fields."""
|
91
|
+
return [
|
92
|
+
field
|
93
|
+
for field in self.config.field_definitions
|
94
|
+
if field.field_metadata.get("required", False)
|
95
|
+
]
|
96
|
+
|
97
|
+
@property
|
98
|
+
def incomplete_fields(self) -> list["ManualTaskConfigField"]:
|
99
|
+
"""Get all fields that haven't been completed yet.
|
100
|
+
A field is considered incomplete if:
|
101
|
+
1. It's required and has no submission
|
102
|
+
Returns:
|
103
|
+
list[ManualTaskConfigField]: List of incomplete fields
|
104
|
+
"""
|
105
|
+
return [
|
106
|
+
field
|
107
|
+
for field in self.required_fields
|
108
|
+
if not self.get_submission_for_field(field.id)
|
109
|
+
]
|
110
|
+
|
111
|
+
@property
|
112
|
+
def completed_fields(self) -> list["ManualTaskConfigField"]:
|
113
|
+
"""Get all fields that have been completed."""
|
114
|
+
return [
|
115
|
+
field
|
116
|
+
for field in self.config.field_definitions
|
117
|
+
if field.field_metadata.get("required", False)
|
118
|
+
and self.get_submission_for_field(field.id)
|
119
|
+
]
|
120
|
+
|
121
|
+
def get_submission_for_field(
|
122
|
+
self, field_id: str
|
123
|
+
) -> Optional["ManualTaskSubmission"]:
|
124
|
+
"""Get the submission for a specific field.
|
125
|
+
|
126
|
+
Args:
|
127
|
+
field_id: The ID of the field to get the submission for
|
128
|
+
|
129
|
+
Returns:
|
130
|
+
Optional[ManualTaskSubmission]: The submission for the field, or None if no submission exists
|
131
|
+
"""
|
132
|
+
return next(
|
133
|
+
(
|
134
|
+
submission
|
135
|
+
for submission in self.submissions
|
136
|
+
if submission.field_id == field_id
|
137
|
+
),
|
138
|
+
None,
|
139
|
+
)
|
140
|
+
|
141
|
+
|
142
|
+
class ManualTaskSubmission(Base):
|
143
|
+
"""Model for storing user submissions.
|
144
|
+
Each submission represents data for a single field.
|
145
|
+
"""
|
146
|
+
|
147
|
+
@declared_attr
|
148
|
+
def __tablename__(cls) -> str:
|
149
|
+
"""Overriding base class method to set the table name."""
|
150
|
+
return "manual_task_submission"
|
151
|
+
|
152
|
+
# Database columns
|
153
|
+
task_id = Column(String, ForeignKey("manual_task.id"))
|
154
|
+
config_id = Column(String, ForeignKey("manual_task_config.id"))
|
155
|
+
field_id = Column(String, ForeignKey("manual_task_config_field.id"))
|
156
|
+
instance_id = Column(String, ForeignKey("manual_task_instance.id"), nullable=False)
|
157
|
+
submitted_by = Column(String, ForeignKey("fidesuser.id"), nullable=True)
|
158
|
+
submitted_at = Column(DateTime, default=datetime.now(timezone.utc), nullable=False)
|
159
|
+
data = Column(JSONB, nullable=False)
|
160
|
+
|
161
|
+
# Relationships
|
162
|
+
task = relationship("ManualTask", back_populates="submissions", viewonly=True)
|
163
|
+
config = relationship(
|
164
|
+
"ManualTaskConfig", back_populates="submissions", viewonly=True
|
165
|
+
)
|
166
|
+
field = relationship(
|
167
|
+
"ManualTaskConfigField", back_populates="submissions", viewonly=True
|
168
|
+
)
|
169
|
+
instance = relationship(
|
170
|
+
"ManualTaskInstance", back_populates="submissions", viewonly=True
|
171
|
+
)
|
172
|
+
attachments = relationship(
|
173
|
+
"Attachment",
|
174
|
+
secondary="attachment_reference",
|
175
|
+
primaryjoin="and_(ManualTaskSubmission.id == AttachmentReference.reference_id, "
|
176
|
+
"AttachmentReference.reference_type == 'manual_task_submission')",
|
177
|
+
secondaryjoin="Attachment.id == AttachmentReference.attachment_id",
|
178
|
+
order_by="Attachment.created_at",
|
179
|
+
viewonly=True,
|
180
|
+
uselist=True,
|
181
|
+
)
|
182
|
+
|
183
|
+
user = relationship(
|
184
|
+
"FidesUser",
|
185
|
+
primaryjoin="FidesUser.id == ManualTaskSubmission.submitted_by",
|
186
|
+
viewonly=True,
|
187
|
+
)
|
@@ -13,6 +13,9 @@ if TYPE_CHECKING: # pragma: no cover
|
|
13
13
|
from fides.api.models.manual_tasks.manual_task_config import (
|
14
14
|
ManualTaskConfig, # pragma: no cover
|
15
15
|
)
|
16
|
+
from fides.api.models.manual_tasks.manual_task_instance import ( # pragma: no cover
|
17
|
+
ManualTaskInstance,
|
18
|
+
)
|
16
19
|
|
17
20
|
|
18
21
|
class ManualTaskLog(Base):
|
@@ -26,19 +29,29 @@ class ManualTaskLog(Base):
|
|
26
29
|
task_id = Column(
|
27
30
|
String, ForeignKey("manual_task.id", ondelete="CASCADE"), nullable=False
|
28
31
|
)
|
29
|
-
config_id = Column(
|
30
|
-
|
32
|
+
config_id = Column(
|
33
|
+
String, ForeignKey("manual_task_config.id", ondelete="CASCADE"), nullable=True
|
34
|
+
)
|
35
|
+
instance_id = Column(
|
36
|
+
String,
|
37
|
+
ForeignKey("manual_task_instance.id", ondelete="CASCADE"),
|
38
|
+
nullable=True,
|
39
|
+
)
|
31
40
|
status = Column(String, nullable=False)
|
32
41
|
message = Column(String, nullable=False)
|
33
42
|
details = Column(JSONB, nullable=True)
|
34
43
|
|
35
|
-
# Relationships
|
36
|
-
task = relationship(
|
44
|
+
# Relationships
|
45
|
+
task = relationship(
|
46
|
+
"ManualTask", back_populates="logs", foreign_keys=[task_id], viewonly=True
|
47
|
+
)
|
37
48
|
config = relationship(
|
38
|
-
"ManualTaskConfig",
|
49
|
+
"ManualTaskConfig",
|
50
|
+
back_populates="logs",
|
51
|
+
foreign_keys=[config_id],
|
52
|
+
viewonly=True,
|
39
53
|
)
|
40
|
-
|
41
|
-
# instance = relationship("ManualTaskInstance", back_populates="logs")
|
54
|
+
instance = relationship("ManualTaskInstance", back_populates="logs", viewonly=True)
|
42
55
|
|
43
56
|
@classmethod
|
44
57
|
def create_log(
|
@@ -5,6 +5,15 @@ from typing import Annotated, Any, Optional
|
|
5
5
|
from pydantic import ConfigDict, Field
|
6
6
|
|
7
7
|
from fides.api.schemas.base_class import FidesSchema
|
8
|
+
from fides.api.schemas.manual_tasks.manual_task_status import StatusType
|
9
|
+
|
10
|
+
|
11
|
+
class ManualTaskExecutionTiming(str, Enum):
|
12
|
+
"""Enum for when a manual task should be executed in the privacy request DAG."""
|
13
|
+
|
14
|
+
pre_execution = "pre_execution" # Execute before the main DAG
|
15
|
+
post_execution = "post_execution" # Execute after the main DAG
|
16
|
+
parallel = "parallel" # Execute in parallel with the main DAG
|
8
17
|
|
9
18
|
|
10
19
|
class ManualTaskType(str, Enum):
|
@@ -23,6 +32,13 @@ class ManualTaskParentEntityType(str, Enum):
|
|
23
32
|
# Add more parent entity types as needed
|
24
33
|
|
25
34
|
|
35
|
+
class ManualTaskEntityType(str, Enum):
|
36
|
+
"""Enum for manual task entity types."""
|
37
|
+
|
38
|
+
privacy_request = "privacy_request"
|
39
|
+
# Add more entity types as needed
|
40
|
+
|
41
|
+
|
26
42
|
class ManualTaskReferenceType(str, Enum):
|
27
43
|
"""Enum for manual task reference types."""
|
28
44
|
|
@@ -46,6 +62,32 @@ class ManualTaskLogStatus(str, Enum):
|
|
46
62
|
awaiting_input = "awaiting_input"
|
47
63
|
|
48
64
|
|
65
|
+
class ManualTaskResponse(FidesSchema):
|
66
|
+
"""Schema for manual task response."""
|
67
|
+
|
68
|
+
model_config = ConfigDict(extra="forbid")
|
69
|
+
|
70
|
+
id: Annotated[str, Field(..., description="Task ID")]
|
71
|
+
parent_entity_id: Annotated[str, Field(..., description="Parent entity ID")]
|
72
|
+
parent_entity_type: Annotated[
|
73
|
+
ManualTaskParentEntityType, Field(..., description="Parent entity type")
|
74
|
+
]
|
75
|
+
status: Annotated[StatusType, Field(..., description="Task status")]
|
76
|
+
created_at: Annotated[datetime, Field(..., description="Creation timestamp")]
|
77
|
+
updated_at: Annotated[datetime, Field(..., description="Last update timestamp")]
|
78
|
+
|
79
|
+
|
80
|
+
class ManualTaskCreate(FidesSchema):
|
81
|
+
"""Schema for creating a manual task."""
|
82
|
+
|
83
|
+
model_config = ConfigDict(extra="forbid")
|
84
|
+
|
85
|
+
parent_entity_id: Annotated[str, Field(..., description="Parent entity ID")]
|
86
|
+
parent_entity_type: Annotated[
|
87
|
+
ManualTaskParentEntityType, Field(..., description="Parent entity type")
|
88
|
+
]
|
89
|
+
|
90
|
+
|
49
91
|
class ManualTaskLogCreate(FidesSchema):
|
50
92
|
"""Schema for creating a manual task log entry."""
|
51
93
|
|