brynq-sdk-brynq 4.2.6.dev0__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 brynq-sdk-brynq might be problematic. Click here for more details.
- brynq_sdk_brynq/__init__.py +1 -0
- brynq_sdk_brynq/brynq.py +289 -0
- brynq_sdk_brynq/credentials.py +157 -0
- brynq_sdk_brynq/customers.py +88 -0
- brynq_sdk_brynq/interfaces.py +234 -0
- brynq_sdk_brynq/mappings.py +107 -0
- brynq_sdk_brynq/organization_chart.py +251 -0
- brynq_sdk_brynq/roles.py +272 -0
- brynq_sdk_brynq/scenarios.py +3495 -0
- brynq_sdk_brynq/schemas/__init__.py +52 -0
- brynq_sdk_brynq/schemas/credentials.py +37 -0
- brynq_sdk_brynq/schemas/customers.py +108 -0
- brynq_sdk_brynq/schemas/interfaces.py +237 -0
- brynq_sdk_brynq/schemas/organization_chart.py +70 -0
- brynq_sdk_brynq/schemas/roles.py +95 -0
- brynq_sdk_brynq/schemas/scenarios.py +419 -0
- brynq_sdk_brynq/schemas/users.py +126 -0
- brynq_sdk_brynq/source_systems.py +175 -0
- brynq_sdk_brynq/users.py +405 -0
- brynq_sdk_brynq-4.2.6.dev0.dist-info/METADATA +17 -0
- brynq_sdk_brynq-4.2.6.dev0.dist-info/RECORD +23 -0
- brynq_sdk_brynq-4.2.6.dev0.dist-info/WHEEL +5 -0
- brynq_sdk_brynq-4.2.6.dev0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from .credentials import CredentialsConfig, CredentialSource, CredentialData
|
|
2
|
+
from .customers import CustomerSchema, CustomerUsers, CustomerContractDetailsSchema
|
|
3
|
+
from .scenarios import Scenario, ScenarioMappingConfiguration, ScenarioDetail, SourceOrTargetField
|
|
4
|
+
from .interfaces import Interface, InterfaceApps, InterfaceDetail, InterfaceConfig, Schedule, Scope, DevSettings, Frequency, TaskSchedule, MappingValue, MappingItem
|
|
5
|
+
from .organization_chart import OrganizationChartNode, OrganizationLayerCreate, OrganizationLayerUpdate, OrganizationLayerGet, OrganizationNode, OrganizationNodeCreate, OrganizationNodeUpdate
|
|
6
|
+
from .roles import DashboardRight, QlikDashboardRight, CreateRoleRequest, RoleUser, RoleSchema
|
|
7
|
+
from .users import UserProducts, UserCreate, UserUpdate, UserInvite, QlikDashboardRight, QlikDashboardRightsPayload, DashboardRight, DashboardRightsPayload, UserEntitiesPayload, User, QlikAppUserAuthorization
|
|
8
|
+
from .interfaces import MappingValue
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"CredentialsConfig",
|
|
12
|
+
"CredentialSource",
|
|
13
|
+
"CredentialData",
|
|
14
|
+
"CustomerSchema",
|
|
15
|
+
"CustomerUsers",
|
|
16
|
+
"CustomerContractDetailsSchema",
|
|
17
|
+
"Interface",
|
|
18
|
+
"InterfaceApps",
|
|
19
|
+
"InterfaceDetail",
|
|
20
|
+
"InterfaceConfig",
|
|
21
|
+
"Schedule",
|
|
22
|
+
"Scope",
|
|
23
|
+
"DevSettings",
|
|
24
|
+
"Frequency",
|
|
25
|
+
"TaskSchedule",
|
|
26
|
+
"MappingValue",
|
|
27
|
+
"MappingItem",
|
|
28
|
+
"OrganizationChartNode",
|
|
29
|
+
"OrganizationLayerCreate",
|
|
30
|
+
"OrganizationLayerUpdate",
|
|
31
|
+
"OrganizationLayerGet",
|
|
32
|
+
"OrganizationNode",
|
|
33
|
+
"OrganizationNodeCreate",
|
|
34
|
+
"OrganizationNodeUpdate",
|
|
35
|
+
"DashboardRight",
|
|
36
|
+
"QlikDashboardRight",
|
|
37
|
+
"CreateRoleRequest",
|
|
38
|
+
"RoleUser",
|
|
39
|
+
"UserProducts",
|
|
40
|
+
"UserCreate",
|
|
41
|
+
"UserUpdate",
|
|
42
|
+
"UserInvite",
|
|
43
|
+
"QlikDashboardRight",
|
|
44
|
+
"QlikDashboardRightsPayload",
|
|
45
|
+
"DashboardRight",
|
|
46
|
+
"DashboardRightsPayload",
|
|
47
|
+
"UserEntitiesPayload",
|
|
48
|
+
"User",
|
|
49
|
+
"QlikAppUserAuthorization",
|
|
50
|
+
"MappingValue",
|
|
51
|
+
"Scenario",
|
|
52
|
+
]
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from typing import Dict, Any, List
|
|
2
|
+
from pydantic import BaseModel, Field, RootModel
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class CredentialData(RootModel[Dict[str, Any]]):
|
|
6
|
+
"""Schema for credential data which can contain any key-value pairs"""
|
|
7
|
+
root: Dict[str, Any]
|
|
8
|
+
|
|
9
|
+
class Config:
|
|
10
|
+
frozen = True
|
|
11
|
+
strict = False
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CredentialSource(BaseModel):
|
|
15
|
+
"""Schema for a credential source or target"""
|
|
16
|
+
app: str = Field(..., description="Application identifier")
|
|
17
|
+
type: str = Field(..., description="Type of the credential source/target")
|
|
18
|
+
data: Dict[str, Any] = Field(..., description="Credential data key-value pairs")
|
|
19
|
+
direction: str = Field(..., description="Direction of the credential flow (source/target)")
|
|
20
|
+
|
|
21
|
+
class Config:
|
|
22
|
+
frozen = True
|
|
23
|
+
strict = False
|
|
24
|
+
populate_by_name = True
|
|
25
|
+
extra = 'allow'
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class CredentialsConfig(BaseModel):
|
|
29
|
+
"""Schema for the complete credentials configuration"""
|
|
30
|
+
sources: List[CredentialSource] = Field(..., description="List of credential sources")
|
|
31
|
+
targets: List[CredentialSource] = Field(..., description="List of credential targets")
|
|
32
|
+
|
|
33
|
+
class Config:
|
|
34
|
+
frozen = True
|
|
35
|
+
strict = False
|
|
36
|
+
populate_by_name = True
|
|
37
|
+
extra = 'allow'
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field, ValidationError
|
|
2
|
+
from typing import Optional, Union, List, Dict, Any, Tuple, Type
|
|
3
|
+
from datetime import date
|
|
4
|
+
from pydantic.functional_validators import BeforeValidator
|
|
5
|
+
from typing_extensions import Annotated
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def parse_date(value: Union[str, None]) -> Optional[date]:
|
|
9
|
+
"""Convert string date to date object or return None"""
|
|
10
|
+
if value is None:
|
|
11
|
+
return None
|
|
12
|
+
if isinstance(value, date):
|
|
13
|
+
return value
|
|
14
|
+
return date.fromisoformat(value)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def convert_to_str(value: Union[str, int]) -> str:
|
|
18
|
+
"""Convert integer or string value to string"""
|
|
19
|
+
return str(value)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
DateField = Annotated[Union[date, None], BeforeValidator(parse_date)]
|
|
23
|
+
StringOrInt = Annotated[str, BeforeValidator(convert_to_str)]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def validate_data(data: List[Dict[str, Any]], schema: Type[BaseModel], debug: bool = False) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]:
|
|
27
|
+
"""Validate a list of dictionaries using a Pydantic schema and separate valid and invalid data.
|
|
28
|
+
|
|
29
|
+
Args:
|
|
30
|
+
data (List[Dict[str, Any]]): List of dictionaries to validate
|
|
31
|
+
schema (Type[BaseModel]): Pydantic schema class to use for validation
|
|
32
|
+
debug (bool, optional): Whether to print debug information. Defaults to False.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]: Tuple of (valid_data, invalid_data)
|
|
36
|
+
- valid_data: List of validated dictionaries that conform to the schema
|
|
37
|
+
- invalid_data: List of dictionaries that failed validation, with error details
|
|
38
|
+
"""
|
|
39
|
+
valid_data = []
|
|
40
|
+
invalid_data = []
|
|
41
|
+
|
|
42
|
+
for idx, item in enumerate(data):
|
|
43
|
+
try:
|
|
44
|
+
validated = schema(**item).model_dump()
|
|
45
|
+
valid_data.append(validated)
|
|
46
|
+
except ValidationError as e:
|
|
47
|
+
if debug:
|
|
48
|
+
print(f"Validation error at index {idx}:")
|
|
49
|
+
print(e.json(indent=2))
|
|
50
|
+
|
|
51
|
+
# Add error information to the invalid item
|
|
52
|
+
invalid_item = item.copy()
|
|
53
|
+
invalid_item['_validation_errors'] = [
|
|
54
|
+
{
|
|
55
|
+
'loc': ' -> '.join(str(loc) for loc in error['loc']),
|
|
56
|
+
'msg': error['msg'],
|
|
57
|
+
'type': error['type']
|
|
58
|
+
}
|
|
59
|
+
for error in e.errors()
|
|
60
|
+
]
|
|
61
|
+
invalid_data.append(invalid_item)
|
|
62
|
+
|
|
63
|
+
return valid_data, invalid_data
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class CustomerSchema(BaseModel):
|
|
67
|
+
"""Schema for basic customer data validation"""
|
|
68
|
+
name: str = Field(..., description="Name of the customer")
|
|
69
|
+
domain: str = Field(..., description="Domain identifier for the customer")
|
|
70
|
+
|
|
71
|
+
class Config:
|
|
72
|
+
frozen = True
|
|
73
|
+
strict = True
|
|
74
|
+
populate_by_name = True
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class CustomerUsers(BaseModel):
|
|
78
|
+
"""Schema for customer users counts"""
|
|
79
|
+
global_: StringOrInt = Field(..., alias="global", description="Number of global users")
|
|
80
|
+
qlik_analyzer: StringOrInt = Field(..., alias="qlikAnalyzer", description="Number of Qlik Analyzer users")
|
|
81
|
+
qlik_pro: StringOrInt = Field(..., alias="qlikPro", description="Number of Qlik Pro users")
|
|
82
|
+
jira: int = Field(..., description="Number of Jira users")
|
|
83
|
+
|
|
84
|
+
class Config:
|
|
85
|
+
frozen = True
|
|
86
|
+
strict = True
|
|
87
|
+
populate_by_name = True
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class CustomerContractDetailsSchema(BaseModel):
|
|
91
|
+
"""Schema for customer contract details"""
|
|
92
|
+
id: int = Field(..., description="Unique identifier for the customer", gt=0)
|
|
93
|
+
name: str = Field(..., description="Name of the customer")
|
|
94
|
+
profit_subscription: Optional[str] = Field(None, alias="profitSubscription", description="Profit subscription identifier")
|
|
95
|
+
subscription_cost: float = Field(..., alias="subscriptionCost", description="Cost of the subscription")
|
|
96
|
+
subscription_cost_mutation: Optional[float] = Field(None, alias="subscriptionCostMutation", description="Change in subscription cost")
|
|
97
|
+
effective_date: DateField = Field(..., alias="effectiveDate", description="Start date of the contract")
|
|
98
|
+
expiration_date: DateField = Field(None, alias="expirationDate", description="End date of the contract")
|
|
99
|
+
payment_term: str = Field(..., alias="paymentTerm", description="Payment term (e.g., annual)")
|
|
100
|
+
subscription_type: str = Field(..., alias="subscriptionType", description="Type of subscription")
|
|
101
|
+
referred_by: Optional[str] = Field(None, alias="referredBy", description="Referral source")
|
|
102
|
+
users: CustomerUsers = Field(..., description="User counts by type")
|
|
103
|
+
apps: int = Field(..., description="Number of apps")
|
|
104
|
+
|
|
105
|
+
class Config:
|
|
106
|
+
frozen = True
|
|
107
|
+
strict = True
|
|
108
|
+
populate_by_name = True
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
from typing import Dict, List, Any, Optional, Union, Annotated
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
from enum import Enum
|
|
4
|
+
import re
|
|
5
|
+
from pydantic import BaseModel, Field, field_validator, model_validator, ConfigDict, StringConstraints
|
|
6
|
+
|
|
7
|
+
SDK_PREFIX = "brynq_sdk_"
|
|
8
|
+
VERSION_PATTERN = r'^\d+\.\d+\.\d+$'
|
|
9
|
+
|
|
10
|
+
# strips whitespace from strings before validation
|
|
11
|
+
CleanStr = Annotated[str, StringConstraints(strip_whitespace=True)]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class InterfaceType(str, Enum):
|
|
15
|
+
"""Enumeration of interface types."""
|
|
16
|
+
TEMPLATE = "TEMPLATE"
|
|
17
|
+
ADVANCED = "ADVANCED"
|
|
18
|
+
|
|
19
|
+
class Frequency(BaseModel):
|
|
20
|
+
"""Schema for task schedule frequency"""
|
|
21
|
+
day: int = Field(..., description="Day of frequency")
|
|
22
|
+
hour: int = Field(..., description="Hour of frequency")
|
|
23
|
+
month: int = Field(..., description="Month of frequency")
|
|
24
|
+
minute: int = Field(..., description="Minute of frequency")
|
|
25
|
+
|
|
26
|
+
class Config:
|
|
27
|
+
frozen = True
|
|
28
|
+
strict = True
|
|
29
|
+
populate_by_name = True
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class TaskSchedule(BaseModel):
|
|
33
|
+
"""Schema for interface task schedule"""
|
|
34
|
+
id: int = Field(..., description="Task schedule ID")
|
|
35
|
+
task_type: str = Field(..., alias="taskType", description="Type of task")
|
|
36
|
+
trigger_type: str = Field(..., alias="triggerType", description="Type of trigger")
|
|
37
|
+
trigger_pattern: str = Field(..., alias="triggerPattern", description="Pattern of trigger")
|
|
38
|
+
timezone: str = Field(..., description="Timezone for the task")
|
|
39
|
+
next_reload: Optional[str] = Field(None, alias="nextReload", description="Next reload time")
|
|
40
|
+
frequency: Frequency = Field(..., description="Frequency settings")
|
|
41
|
+
variables: Dict[str, Any] = Field(default_factory=dict, description="Task variables")
|
|
42
|
+
start_after_preceding_task: Optional[bool] = Field(None, alias="startAfterPrecedingTask", description="Whether to start after preceding task")
|
|
43
|
+
start_after_task_id: Optional[int] = Field(None, alias="startAfterTaskId", description="ID of task to start after")
|
|
44
|
+
last_reload: str = Field(..., alias="lastReload", description="Last reload time")
|
|
45
|
+
last_error_message: str = Field(..., alias="lastErrorMessage", description="Last error message")
|
|
46
|
+
status: str = Field(..., description="Current status")
|
|
47
|
+
disabled: bool = Field(..., description="Whether task is disabled")
|
|
48
|
+
run_instant: bool = Field(..., alias="runInstant", description="Whether to run instantly")
|
|
49
|
+
stopped_by_user: bool = Field(..., alias="stoppedByUser", description="Whether stopped by user")
|
|
50
|
+
stepnr: int = Field(..., description="Step number")
|
|
51
|
+
created_at: str = Field(..., alias="createdAt", description="Creation time")
|
|
52
|
+
updated_at: str = Field(..., alias="updatedAt", description="Last update time")
|
|
53
|
+
|
|
54
|
+
@field_validator('last_reload', 'created_at', 'updated_at', 'next_reload')
|
|
55
|
+
def validate_datetime(cls, v: Optional[str]) -> Optional[str]:
|
|
56
|
+
"""Validate that the string is a valid ISO format datetime"""
|
|
57
|
+
if v is None:
|
|
58
|
+
return v
|
|
59
|
+
try:
|
|
60
|
+
datetime.fromisoformat(v.replace('Z', '+00:00'))
|
|
61
|
+
return v
|
|
62
|
+
except (ValueError, AttributeError) as e:
|
|
63
|
+
raise ValueError(f"Invalid datetime format: {str(e)}")
|
|
64
|
+
|
|
65
|
+
class Config:
|
|
66
|
+
frozen = True
|
|
67
|
+
strict = True
|
|
68
|
+
populate_by_name = True
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class Interface(BaseModel):
|
|
72
|
+
"""Schema for interface data"""
|
|
73
|
+
id: int = Field(..., description="Interface ID")
|
|
74
|
+
name: str = Field(..., description="Interface name")
|
|
75
|
+
description: str = Field(..., description="Interface description")
|
|
76
|
+
source_systems: List[int] = Field(..., alias="sourceSystems", description="List of source system IDs")
|
|
77
|
+
target_systems: List[int] = Field(..., alias="targetSystems", description="List of target system IDs")
|
|
78
|
+
task_schedule: TaskSchedule = Field(..., alias="taskSchedule", description="Task schedule details")
|
|
79
|
+
|
|
80
|
+
class Config:
|
|
81
|
+
frozen = True
|
|
82
|
+
strict = True
|
|
83
|
+
populate_by_name = True
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class InterfaceAppConfig(BaseModel):
|
|
87
|
+
"""Schema for individual app configuration within interface apps.
|
|
88
|
+
|
|
89
|
+
Attributes:
|
|
90
|
+
app: Application name
|
|
91
|
+
sdk: SDK package name (must start with "brynq_sdk_")
|
|
92
|
+
sdk_version: SDK version in format digits.digits.digits (aliased as "sdkVersion" in API)
|
|
93
|
+
"""
|
|
94
|
+
model_config = ConfigDict(frozen=True, strict=True, populate_by_name=True)
|
|
95
|
+
|
|
96
|
+
app: CleanStr = Field(..., description="Application name")
|
|
97
|
+
sdk: CleanStr = Field(default="", description="SDK package name (may be empty for ADVANCED interfaces)")
|
|
98
|
+
sdk_version: CleanStr = Field(default="", alias="sdkVersion", description="SDK version (may be empty for ADVANCED interfaces)")
|
|
99
|
+
|
|
100
|
+
@field_validator('sdk')
|
|
101
|
+
@classmethod
|
|
102
|
+
def validate_sdk_prefix(cls, v: str) -> str:
|
|
103
|
+
"""Validate that SDK name starts with 'brynq_sdk_' or is empty."""
|
|
104
|
+
if v and not v.startswith(SDK_PREFIX):
|
|
105
|
+
raise ValueError(f"SDK name must start with '{SDK_PREFIX}' or be empty, got: {v}")
|
|
106
|
+
return v
|
|
107
|
+
|
|
108
|
+
@field_validator('sdk_version')
|
|
109
|
+
@classmethod
|
|
110
|
+
def validate_version_format(cls, v: str) -> str:
|
|
111
|
+
"""Validate that version follows digits.digits.digits format or is empty."""
|
|
112
|
+
if v and not re.match(VERSION_PATTERN, v):
|
|
113
|
+
raise ValueError(f"SDK version must be 'digits.digits.digits' (e.g., '2.8.2'), got: {v}")
|
|
114
|
+
return v
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class InterfaceApps(BaseModel):
|
|
118
|
+
"""Schema for interface apps configuration"""
|
|
119
|
+
source: InterfaceAppConfig = Field(..., description="Source application configuration")
|
|
120
|
+
target: InterfaceAppConfig = Field(..., description="Target application configuration")
|
|
121
|
+
|
|
122
|
+
class Config:
|
|
123
|
+
frozen = True
|
|
124
|
+
strict = True
|
|
125
|
+
populate_by_name = True
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class InterfaceDetail(BaseModel):
|
|
129
|
+
"""Schema for detailed interface information"""
|
|
130
|
+
model_config = ConfigDict(frozen=True, strict=False, populate_by_name=True)
|
|
131
|
+
|
|
132
|
+
id: int = Field(..., description="Interface id")
|
|
133
|
+
uuid: str = Field(..., description="Interace uid")
|
|
134
|
+
name: str = Field(..., description="Interface name")
|
|
135
|
+
type: InterfaceType = Field(..., description="Interface type (TEMPLATE or ADVANCED)")
|
|
136
|
+
in_development: bool = Field(..., description="Whether interface is in development.", alias="inDevelopment")
|
|
137
|
+
apps: InterfaceApps = Field(..., description="Interface applications configuration")
|
|
138
|
+
|
|
139
|
+
@model_validator(mode='after')
|
|
140
|
+
def validate_apps_for_type(self) -> 'InterfaceDetail':
|
|
141
|
+
"""Validate that sdk and sdk_version are non-empty unless type is ADVANCED."""
|
|
142
|
+
if self.type == InterfaceType.TEMPLATE:
|
|
143
|
+
for app_name in ['source', 'target']:
|
|
144
|
+
config = getattr(self.apps, app_name)
|
|
145
|
+
if not config.sdk:
|
|
146
|
+
raise ValueError(f"{app_name}.sdk must be non-empty for interface type {self.type.value}")
|
|
147
|
+
if not config.sdk_version:
|
|
148
|
+
raise ValueError(f"{app_name}.sdk_version must be non-empty for interface type {self.type.value}")
|
|
149
|
+
return self
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
class MappingValue(BaseModel):
|
|
153
|
+
"""Schema for a single mapping value"""
|
|
154
|
+
input: Dict[Any, Any] = Field(..., description="Input mapping key-value pairs")
|
|
155
|
+
output: Dict[Any, Any] = Field(..., description="Output mapping key-value pairs")
|
|
156
|
+
|
|
157
|
+
class Config:
|
|
158
|
+
frozen = True
|
|
159
|
+
strict = True
|
|
160
|
+
populate_by_name = True
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class MappingItem(BaseModel):
|
|
164
|
+
"""Schema for a single mapping configuration"""
|
|
165
|
+
guid: str = Field(..., description="Unique identifier for the mapping")
|
|
166
|
+
name: str = Field(..., description="Name of the mapping")
|
|
167
|
+
values: List[MappingValue] = Field(default_factory=list, description="List of mapping values")
|
|
168
|
+
default_value: Optional[str] = Field(None, alias="defaultValue", description="Default value if no mapping matches")
|
|
169
|
+
|
|
170
|
+
class Config:
|
|
171
|
+
frozen = True
|
|
172
|
+
strict = True
|
|
173
|
+
populate_by_name = True
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
class InterfaceConfig(BaseModel):
|
|
177
|
+
"""Schema for interface configuration"""
|
|
178
|
+
mapping: List[Dict[str, Any]] = Field(default_factory=list, description="List of mappings")
|
|
179
|
+
variables: Dict[str, Any] = Field(default_factory=dict, description="Configuration variables")
|
|
180
|
+
|
|
181
|
+
class Config:
|
|
182
|
+
frozen = True
|
|
183
|
+
strict = True
|
|
184
|
+
populate_by_name = True
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
class Schedule(BaseModel):
|
|
188
|
+
"""Schema for interface schedule configuration"""
|
|
189
|
+
id: int = Field(..., description="Schedule ID")
|
|
190
|
+
trigger_type: str = Field(..., alias="triggerType", description="Type of trigger")
|
|
191
|
+
trigger_pattern: str = Field(..., alias="triggerPattern", description="Pattern for the trigger")
|
|
192
|
+
timezone: str = Field(..., description="Timezone setting")
|
|
193
|
+
next_reload: Optional[str] = Field(None, alias="nextReload", description="Next scheduled reload time")
|
|
194
|
+
frequency: Frequency = Field(..., description="Frequency settings")
|
|
195
|
+
start_after_preceding_task: Optional[bool] = Field(None, alias="startAfterPrecedingTask", description="Whether to start after preceding task")
|
|
196
|
+
last_reload: str = Field(..., alias="lastReload", description="Last reload time")
|
|
197
|
+
last_error_message: str = Field(..., alias="lastErrorMessage", description="Last error message")
|
|
198
|
+
|
|
199
|
+
@field_validator('last_reload', 'next_reload')
|
|
200
|
+
def validate_datetime(cls, v: Optional[str]) -> Optional[str]:
|
|
201
|
+
"""Validate that the string is a valid ISO format datetime"""
|
|
202
|
+
if v is None:
|
|
203
|
+
return v
|
|
204
|
+
try:
|
|
205
|
+
datetime.fromisoformat(v.replace('Z', '+00:00'))
|
|
206
|
+
return v
|
|
207
|
+
except (ValueError, AttributeError) as e:
|
|
208
|
+
raise ValueError(f"Invalid datetime format: {str(e)}")
|
|
209
|
+
|
|
210
|
+
class Config:
|
|
211
|
+
frozen = True
|
|
212
|
+
strict = True
|
|
213
|
+
populate_by_name = True
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class Scope(BaseModel):
|
|
217
|
+
"""Schema for interface scope data"""
|
|
218
|
+
live: Optional[Dict[str, Any]] = Field(None, description="Live scope configuration")
|
|
219
|
+
draft: Optional[Dict[str, Any]] = Field(None, description="Draft scope configuration")
|
|
220
|
+
|
|
221
|
+
class Config:
|
|
222
|
+
frozen = True
|
|
223
|
+
strict = True
|
|
224
|
+
populate_by_name = True
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
class DevSettings(BaseModel):
|
|
228
|
+
"""Schema for interface dev settings"""
|
|
229
|
+
docker_image: str = Field(..., alias="dockerImage", description="Docker image name")
|
|
230
|
+
sftp_mapping: List[dict] = Field(..., alias="sftpMapping", description="SFTP mapping configuration")
|
|
231
|
+
runfile_path: str = Field(..., alias="runfilePath", description="Path to the runfile")
|
|
232
|
+
stop_is_allowed: bool = Field(..., alias="stopIsAllowed", description="Whether stopping is allowed")
|
|
233
|
+
|
|
234
|
+
class Config:
|
|
235
|
+
frozen = True
|
|
236
|
+
strict = True
|
|
237
|
+
populate_by_name = True
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class OrganizationChartNode(BaseModel):
|
|
6
|
+
"""Schema for organization chart node"""
|
|
7
|
+
id: int = Field(..., description="Node ID")
|
|
8
|
+
name: str = Field(..., description="Node name")
|
|
9
|
+
drop_index: int = Field(..., alias="dropIndex", description="Drop index for ordering")
|
|
10
|
+
parent_id: Optional[int] = Field(None, alias="parent_id", description="Parent node ID, null for root nodes")
|
|
11
|
+
source_system_entities: List[str] = Field(default_factory=list, alias="source_system_entities", description="List of source system entities")
|
|
12
|
+
|
|
13
|
+
class Config:
|
|
14
|
+
frozen = True
|
|
15
|
+
strict = True
|
|
16
|
+
populate_by_name = True
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class OrganizationLayerCreate(BaseModel):
|
|
20
|
+
"""Schema for organization layer input"""
|
|
21
|
+
name: str = Field(..., description="Layer name")
|
|
22
|
+
level: int = Field(..., ge=0, description="Layer level in hierarchy")
|
|
23
|
+
|
|
24
|
+
class Config:
|
|
25
|
+
frozen = True
|
|
26
|
+
strict = True
|
|
27
|
+
populate_by_name = True
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class OrganizationLayerUpdate(BaseModel):
|
|
31
|
+
"""Schema for organization layer response"""
|
|
32
|
+
id: int = Field(..., description="Layer ID")
|
|
33
|
+
level: int = Field(None, ge=0, description="Layer level in hierarchy")
|
|
34
|
+
name: str = Field(None, description="Layer name")
|
|
35
|
+
|
|
36
|
+
class Config:
|
|
37
|
+
frozen = True
|
|
38
|
+
strict = True
|
|
39
|
+
populate_by_name = True
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class OrganizationLayerGet(OrganizationLayerUpdate):
|
|
43
|
+
pass
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class OrganizationNode(BaseModel):
|
|
47
|
+
"""Schema for organization chart node"""
|
|
48
|
+
id: int = Field(..., description="Node ID")
|
|
49
|
+
name: str = Field(..., description="Node name")
|
|
50
|
+
parent_id: Optional[int] = Field(None, alias="parentId", description="Parent node ID")
|
|
51
|
+
|
|
52
|
+
class Config:
|
|
53
|
+
frozen = True
|
|
54
|
+
strict = True
|
|
55
|
+
populate_by_name = True
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class OrganizationNodeCreate(BaseModel):
|
|
59
|
+
"""Schema for creating organization chart node"""
|
|
60
|
+
name: str = Field(..., description="Node name")
|
|
61
|
+
parent_id: Optional[int] = Field(None, alias="parentId", description="Parent node ID")
|
|
62
|
+
position: Optional[int] = Field(None, description="Position among siblings")
|
|
63
|
+
|
|
64
|
+
class Config:
|
|
65
|
+
frozen = True
|
|
66
|
+
strict = True
|
|
67
|
+
populate_by_name = True
|
|
68
|
+
|
|
69
|
+
class OrganizationNodeUpdate(OrganizationNodeCreate):
|
|
70
|
+
id: int = Field(..., description="Node ID")
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from pydantic import BaseModel, Field, field_validator
|
|
2
|
+
from typing import List, Optional, Any, Dict
|
|
3
|
+
|
|
4
|
+
class DashboardRight(BaseModel):
|
|
5
|
+
"""Schema for dashboard rights"""
|
|
6
|
+
dashboard_id: int = Field(..., alias="dashboardId", description="ID of the dashboard")
|
|
7
|
+
editable: bool = Field(..., description="Whether the dashboard is editable")
|
|
8
|
+
entities: List[int] = Field(default_factory=list, description="List of entity IDs")
|
|
9
|
+
|
|
10
|
+
class Config:
|
|
11
|
+
frozen = True
|
|
12
|
+
strict = True
|
|
13
|
+
populate_by_name = True
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class QlikDashboardRight(BaseModel):
|
|
17
|
+
"""Schema for Qlik dashboard rights"""
|
|
18
|
+
guid: str = Field(..., description="Dashboard GUID")
|
|
19
|
+
data_model_editable: bool = Field(..., alias="dataModelEditable", description="Whether the data model is editable")
|
|
20
|
+
editable: bool = Field(..., description="Whether the dashboard is editable")
|
|
21
|
+
entities: List[int] = Field(default_factory=list, description="List of entity IDs")
|
|
22
|
+
|
|
23
|
+
class Config:
|
|
24
|
+
frozen = True
|
|
25
|
+
strict = True
|
|
26
|
+
populate_by_name = True
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class CreateRoleRequest(BaseModel):
|
|
30
|
+
"""Schema for creating a new role"""
|
|
31
|
+
name: str = Field(..., description="Name of the role")
|
|
32
|
+
dashboards: List[DashboardRight] = Field(default_factory=list, description="List of dashboard rights")
|
|
33
|
+
qlik_dashboards: List[QlikDashboardRight] = Field(default_factory=list, alias="qlikDashboards", description="List of Qlik dashboard rights")
|
|
34
|
+
|
|
35
|
+
class Config:
|
|
36
|
+
frozen = True
|
|
37
|
+
strict = True
|
|
38
|
+
populate_by_name = True
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class RoleUser(BaseModel):
|
|
42
|
+
"""Schema for users assigned to a role"""
|
|
43
|
+
id: int = Field(..., description="User ID")
|
|
44
|
+
name: str = Field(..., description="User name")
|
|
45
|
+
email: str = Field(..., description="User email")
|
|
46
|
+
active: bool = Field(..., description="Whether the user is active")
|
|
47
|
+
|
|
48
|
+
class Config:
|
|
49
|
+
frozen = True
|
|
50
|
+
strict = True
|
|
51
|
+
populate_by_name = True
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class RoleSchema(BaseModel):
|
|
55
|
+
"""Schema for role data"""
|
|
56
|
+
id: int = Field(..., description="Unique identifier for the role")
|
|
57
|
+
name: str = Field(..., description="Name of the role")
|
|
58
|
+
permissions: Optional[Dict[str, Any]] = Field(None, description="Role permissions")
|
|
59
|
+
dashboards: List[Dict[str, Any]] = Field(default_factory=list, description="List of dashboard rights")
|
|
60
|
+
qlik_dashboards: List[Dict[str, Any]] = Field(default_factory=list, alias="qlikDashboards", description="List of Qlik dashboard rights")
|
|
61
|
+
|
|
62
|
+
class Config:
|
|
63
|
+
frozen = True
|
|
64
|
+
strict = True
|
|
65
|
+
populate_by_name = True
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class RoleSchema(BaseModel):
|
|
69
|
+
"""Schema for role data validation"""
|
|
70
|
+
id: int = Field(..., description="Unique identifier for the role", gt=0)
|
|
71
|
+
name: str = Field(None, description="Name of the role")
|
|
72
|
+
dashboards: List[Any] = Field(default_factory=list, description="List of dashboards associated with the role")
|
|
73
|
+
permissions: Optional[Any] = Field(default=None, description="Role permissions")
|
|
74
|
+
qlik_dashboards: List[Any] = Field(default_factory=list, description="List of Qlik dashboards associated with the role")
|
|
75
|
+
|
|
76
|
+
@field_validator('name')
|
|
77
|
+
@classmethod
|
|
78
|
+
def validate_name(cls, v: str) -> str:
|
|
79
|
+
"""Validate that the name is not empty or just whitespace"""
|
|
80
|
+
if not v.strip():
|
|
81
|
+
raise ValueError("Role name cannot be empty or just whitespace")
|
|
82
|
+
return v.strip()
|
|
83
|
+
|
|
84
|
+
@field_validator('dashboards', 'qlik_dashboards')
|
|
85
|
+
@classmethod
|
|
86
|
+
def validate_dashboard_lists(cls, v: List[Any]) -> List[Any]:
|
|
87
|
+
"""Ensure dashboard lists are always lists, even if empty"""
|
|
88
|
+
if v is None:
|
|
89
|
+
return []
|
|
90
|
+
return v
|
|
91
|
+
|
|
92
|
+
class Config:
|
|
93
|
+
frozen = True # Makes the model immutable
|
|
94
|
+
strict = True # Ensures strict type checking
|
|
95
|
+
populate_by_name = True # Allows both camelCase and snake_case access
|