binalyze-air-sdk 1.0.2__py3-none-any.whl → 1.0.3__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.
Files changed (142) hide show
  1. binalyze_air/__init__.py +77 -77
  2. binalyze_air/apis/__init__.py +67 -27
  3. binalyze_air/apis/acquisitions.py +107 -0
  4. binalyze_air/apis/api_tokens.py +49 -0
  5. binalyze_air/apis/assets.py +161 -0
  6. binalyze_air/apis/audit_logs.py +26 -0
  7. binalyze_air/apis/{authentication.py → auth.py} +29 -27
  8. binalyze_air/apis/auto_asset_tags.py +79 -75
  9. binalyze_air/apis/backup.py +177 -0
  10. binalyze_air/apis/baseline.py +46 -0
  11. binalyze_air/apis/cases.py +225 -0
  12. binalyze_air/apis/cloud_forensics.py +116 -0
  13. binalyze_air/apis/event_subscription.py +96 -96
  14. binalyze_air/apis/evidence.py +249 -53
  15. binalyze_air/apis/interact.py +153 -36
  16. binalyze_air/apis/investigation_hub.py +234 -0
  17. binalyze_air/apis/license.py +104 -0
  18. binalyze_air/apis/logger.py +83 -0
  19. binalyze_air/apis/multipart_upload.py +201 -0
  20. binalyze_air/apis/notifications.py +115 -0
  21. binalyze_air/apis/organizations.py +267 -0
  22. binalyze_air/apis/params.py +44 -39
  23. binalyze_air/apis/policies.py +186 -0
  24. binalyze_air/apis/preset_filters.py +79 -0
  25. binalyze_air/apis/recent_activities.py +71 -0
  26. binalyze_air/apis/relay_server.py +104 -0
  27. binalyze_air/apis/settings.py +395 -27
  28. binalyze_air/apis/tasks.py +80 -0
  29. binalyze_air/apis/triage.py +197 -0
  30. binalyze_air/apis/user_management.py +183 -74
  31. binalyze_air/apis/webhook_executions.py +50 -0
  32. binalyze_air/apis/webhooks.py +322 -230
  33. binalyze_air/base.py +207 -133
  34. binalyze_air/client.py +217 -1337
  35. binalyze_air/commands/__init__.py +175 -145
  36. binalyze_air/commands/acquisitions.py +661 -387
  37. binalyze_air/commands/api_tokens.py +55 -0
  38. binalyze_air/commands/assets.py +324 -362
  39. binalyze_air/commands/{authentication.py → auth.py} +36 -36
  40. binalyze_air/commands/auto_asset_tags.py +230 -230
  41. binalyze_air/commands/backup.py +47 -0
  42. binalyze_air/commands/baseline.py +32 -396
  43. binalyze_air/commands/cases.py +609 -602
  44. binalyze_air/commands/cloud_forensics.py +88 -0
  45. binalyze_air/commands/event_subscription.py +101 -101
  46. binalyze_air/commands/evidences.py +918 -988
  47. binalyze_air/commands/interact.py +172 -58
  48. binalyze_air/commands/investigation_hub.py +315 -0
  49. binalyze_air/commands/license.py +183 -0
  50. binalyze_air/commands/logger.py +126 -0
  51. binalyze_air/commands/multipart_upload.py +363 -0
  52. binalyze_air/commands/notifications.py +45 -0
  53. binalyze_air/commands/organizations.py +200 -221
  54. binalyze_air/commands/policies.py +175 -203
  55. binalyze_air/commands/preset_filters.py +55 -0
  56. binalyze_air/commands/recent_activities.py +32 -0
  57. binalyze_air/commands/relay_server.py +144 -0
  58. binalyze_air/commands/settings.py +431 -29
  59. binalyze_air/commands/tasks.py +95 -56
  60. binalyze_air/commands/triage.py +224 -360
  61. binalyze_air/commands/user_management.py +351 -126
  62. binalyze_air/commands/webhook_executions.py +77 -0
  63. binalyze_air/config.py +244 -244
  64. binalyze_air/exceptions.py +49 -49
  65. binalyze_air/http_client.py +426 -305
  66. binalyze_air/models/__init__.py +287 -285
  67. binalyze_air/models/acquisitions.py +365 -250
  68. binalyze_air/models/api_tokens.py +73 -0
  69. binalyze_air/models/assets.py +438 -438
  70. binalyze_air/models/audit.py +247 -272
  71. binalyze_air/models/audit_logs.py +14 -0
  72. binalyze_air/models/{authentication.py → auth.py} +69 -69
  73. binalyze_air/models/auto_asset_tags.py +227 -116
  74. binalyze_air/models/backup.py +138 -0
  75. binalyze_air/models/baseline.py +231 -231
  76. binalyze_air/models/cases.py +275 -275
  77. binalyze_air/models/cloud_forensics.py +145 -0
  78. binalyze_air/models/event_subscription.py +170 -171
  79. binalyze_air/models/evidence.py +65 -65
  80. binalyze_air/models/evidences.py +367 -348
  81. binalyze_air/models/interact.py +266 -135
  82. binalyze_air/models/investigation_hub.py +265 -0
  83. binalyze_air/models/license.py +150 -0
  84. binalyze_air/models/logger.py +83 -0
  85. binalyze_air/models/multipart_upload.py +352 -0
  86. binalyze_air/models/notifications.py +138 -0
  87. binalyze_air/models/organizations.py +293 -293
  88. binalyze_air/models/params.py +153 -127
  89. binalyze_air/models/policies.py +260 -249
  90. binalyze_air/models/preset_filters.py +79 -0
  91. binalyze_air/models/recent_activities.py +70 -0
  92. binalyze_air/models/relay_server.py +121 -0
  93. binalyze_air/models/settings.py +538 -84
  94. binalyze_air/models/tasks.py +215 -149
  95. binalyze_air/models/triage.py +141 -142
  96. binalyze_air/models/user_management.py +200 -97
  97. binalyze_air/models/webhook_executions.py +33 -0
  98. binalyze_air/queries/__init__.py +121 -133
  99. binalyze_air/queries/acquisitions.py +155 -155
  100. binalyze_air/queries/api_tokens.py +46 -0
  101. binalyze_air/queries/assets.py +186 -105
  102. binalyze_air/queries/audit.py +400 -416
  103. binalyze_air/queries/{authentication.py → auth.py} +55 -55
  104. binalyze_air/queries/auto_asset_tags.py +59 -59
  105. binalyze_air/queries/backup.py +66 -0
  106. binalyze_air/queries/baseline.py +21 -185
  107. binalyze_air/queries/cases.py +292 -292
  108. binalyze_air/queries/cloud_forensics.py +137 -0
  109. binalyze_air/queries/event_subscription.py +54 -54
  110. binalyze_air/queries/evidence.py +139 -139
  111. binalyze_air/queries/evidences.py +279 -279
  112. binalyze_air/queries/interact.py +140 -28
  113. binalyze_air/queries/investigation_hub.py +329 -0
  114. binalyze_air/queries/license.py +85 -0
  115. binalyze_air/queries/logger.py +58 -0
  116. binalyze_air/queries/multipart_upload.py +180 -0
  117. binalyze_air/queries/notifications.py +71 -0
  118. binalyze_air/queries/organizations.py +222 -222
  119. binalyze_air/queries/params.py +154 -115
  120. binalyze_air/queries/policies.py +149 -149
  121. binalyze_air/queries/preset_filters.py +60 -0
  122. binalyze_air/queries/recent_activities.py +44 -0
  123. binalyze_air/queries/relay_server.py +42 -0
  124. binalyze_air/queries/settings.py +533 -20
  125. binalyze_air/queries/tasks.py +125 -81
  126. binalyze_air/queries/triage.py +230 -230
  127. binalyze_air/queries/user_management.py +193 -83
  128. binalyze_air/queries/webhook_executions.py +39 -0
  129. binalyze_air_sdk-1.0.3.dist-info/METADATA +752 -0
  130. binalyze_air_sdk-1.0.3.dist-info/RECORD +132 -0
  131. {binalyze_air_sdk-1.0.2.dist-info → binalyze_air_sdk-1.0.3.dist-info}/WHEEL +1 -1
  132. binalyze_air/apis/endpoints.py +0 -22
  133. binalyze_air/apis/evidences.py +0 -216
  134. binalyze_air/apis/users.py +0 -68
  135. binalyze_air/commands/users.py +0 -101
  136. binalyze_air/models/endpoints.py +0 -76
  137. binalyze_air/models/users.py +0 -82
  138. binalyze_air/queries/endpoints.py +0 -25
  139. binalyze_air/queries/users.py +0 -69
  140. binalyze_air_sdk-1.0.2.dist-info/METADATA +0 -706
  141. binalyze_air_sdk-1.0.2.dist-info/RECORD +0 -82
  142. {binalyze_air_sdk-1.0.2.dist-info → binalyze_air_sdk-1.0.3.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,186 @@
1
+ """
2
+ Policies API for the Binalyze AIR SDK using CQRS pattern.
3
+ """
4
+
5
+ from typing import List, Optional, Dict, Any, Union
6
+ from ..http_client import HTTPClient
7
+ from ..models.policies import (
8
+ Policy, PolicyFilter, PolicyAssignment, PolicyExecution,
9
+ CreatePolicyRequest, UpdatePolicyRequest, AssignPolicyRequest
10
+ )
11
+ from ..queries.policies import (
12
+ ListPoliciesQuery,
13
+ GetPolicyQuery,
14
+ GetPolicyAssignmentsQuery,
15
+ GetPolicyExecutionsQuery,
16
+ )
17
+ from ..commands.policies import (
18
+ CreatePolicyCommand,
19
+ UpdatePolicyCommand,
20
+ DeletePolicyCommand,
21
+ ActivatePolicyCommand,
22
+ DeactivatePolicyCommand,
23
+ AssignPolicyCommand,
24
+ UnassignPolicyCommand,
25
+ ExecutePolicyCommand,
26
+ )
27
+
28
+
29
+ class PoliciesAPI:
30
+ """Policies API with CQRS pattern - separated queries and commands."""
31
+
32
+ def __init__(self, http_client: HTTPClient):
33
+ self.http_client = http_client
34
+
35
+ # QUERIES (Read operations)
36
+ def list(self, filter_params: Optional[PolicyFilter] = None, organization_ids: Optional[List[int]] = None) -> List[Policy]:
37
+ """List policies with optional filtering."""
38
+ query = ListPoliciesQuery(self.http_client, filter_params, organization_ids)
39
+ result = query.execute()
40
+ # Extract the policies list from the paginated response
41
+ if hasattr(result, 'entities'):
42
+ return result.entities
43
+ elif isinstance(result, list):
44
+ return result
45
+ else:
46
+ return []
47
+
48
+ def get(self, policy_id: str) -> Policy:
49
+ """Get a specific policy by ID."""
50
+ query = GetPolicyQuery(self.http_client, policy_id)
51
+ return query.execute()
52
+
53
+ def get_assignments(self, policy_id: str) -> List[PolicyAssignment]:
54
+ """Get policy assignments."""
55
+ query = GetPolicyAssignmentsQuery(self.http_client, policy_id)
56
+ return query.execute()
57
+
58
+ def get_executions(self, policy_id: str) -> List[PolicyExecution]:
59
+ """Get policy executions."""
60
+ query = GetPolicyExecutionsQuery(self.http_client, policy_id)
61
+ return query.execute()
62
+
63
+ def get_match_stats(self, filter_params: Optional[Dict[str, Any]] = None, organization_ids: Optional[List[int]] = None) -> Dict[str, Any]:
64
+ """Get policy match statistics with filtering.
65
+
66
+ Args:
67
+ filter_params: Optional filter parameters (name, platform, tags, etc.)
68
+ organization_ids: List of organization IDs (defaults to [0])
69
+
70
+ Returns:
71
+ Dictionary containing policy match statistics
72
+ """
73
+ try:
74
+ # Fix API-001: Ensure organizationIds are provided to prevent errors
75
+ if organization_ids is None or len(organization_ids) == 0:
76
+ organization_ids = [0] # Default to organization 0
77
+
78
+ # Build payload with default filter structure
79
+ payload = {
80
+ "name": "",
81
+ "searchTerm": "",
82
+ "ipAddress": "",
83
+ "groupId": "",
84
+ "groupFullPath": "",
85
+ "managedStatus": [],
86
+ "isolationStatus": [],
87
+ "platform": [],
88
+ "issue": "",
89
+ "onlineStatus": [],
90
+ "tags": [],
91
+ "version": "",
92
+ "policy": "",
93
+ "includedEndpointIds": [],
94
+ "excludedEndpointIds": [],
95
+ "organizationIds": organization_ids
96
+ }
97
+
98
+ # Apply custom filter parameters if provided
99
+ if filter_params:
100
+ for key, value in filter_params.items():
101
+ if key in payload:
102
+ payload[key] = value
103
+
104
+ # Use correct API endpoint: POST policies/match-stats (not GET policies/stats)
105
+ response = self.http_client.post("policies/match-stats", json_data=payload)
106
+ return response
107
+
108
+ except Exception as e:
109
+ # Return a simulated response for testing
110
+ return {
111
+ "success": False,
112
+ "error": str(e),
113
+ "result": []
114
+ }
115
+
116
+ # COMMANDS (Write operations)
117
+ def create(self, policy_data: Union[CreatePolicyRequest, Dict[str, Any]]) -> Policy:
118
+ """Create a new policy."""
119
+ command = CreatePolicyCommand(self.http_client, policy_data)
120
+ return command.execute()
121
+
122
+ def update(self, policy_id: str, update_data: Union[UpdatePolicyRequest, Dict[str, Any]]) -> Policy:
123
+ """Update an existing policy."""
124
+ command = UpdatePolicyCommand(self.http_client, policy_id, update_data)
125
+ return command.execute()
126
+
127
+ def delete(self, policy_id: str) -> Dict[str, Any]:
128
+ """Delete a policy."""
129
+ command = DeletePolicyCommand(self.http_client, policy_id)
130
+ return command.execute()
131
+
132
+ def activate(self, policy_id: str) -> Policy:
133
+ """Activate a policy."""
134
+ command = ActivatePolicyCommand(self.http_client, policy_id)
135
+ return command.execute()
136
+
137
+ def deactivate(self, policy_id: str) -> Policy:
138
+ """Deactivate a policy."""
139
+ command = DeactivatePolicyCommand(self.http_client, policy_id)
140
+ return command.execute()
141
+
142
+ def assign(self, assignment_data: Union[AssignPolicyRequest, Dict[str, Any]]) -> Dict[str, Any]:
143
+ """Assign policy to endpoints."""
144
+ command = AssignPolicyCommand(self.http_client, assignment_data)
145
+ return command.execute()
146
+
147
+ def unassign(self, policy_id: str, endpoint_ids: List[str]) -> Dict[str, Any]:
148
+ """Unassign policy from endpoints."""
149
+ command = UnassignPolicyCommand(self.http_client, policy_id, endpoint_ids)
150
+ return command.execute()
151
+
152
+ def execute(self, policy_id: str, endpoint_ids: Optional[List[str]] = None) -> Dict[str, Any]:
153
+ """Execute a policy on assigned endpoints."""
154
+ command = ExecutePolicyCommand(self.http_client, policy_id, endpoint_ids)
155
+ return command.execute()
156
+
157
+ def update_priorities(self, policy_ids: List[str], organization_ids: Optional[List[int]] = None) -> Dict[str, Any]:
158
+ """Update policy priorities.
159
+
160
+ Args:
161
+ policy_ids: List of policy IDs in priority order (System policy must be first)
162
+ organization_ids: List of organization IDs (defaults to [0])
163
+
164
+ Returns:
165
+ Response dictionary with success status
166
+ """
167
+ try:
168
+ # Fix API-001: Ensure organizationIds are provided to prevent issues
169
+ if organization_ids is None or len(organization_ids) == 0:
170
+ organization_ids = [0] # Default to organization 0
171
+
172
+ # Use correct API parameter names according to specification
173
+ payload = {
174
+ "ids": policy_ids, # API expects 'ids', not 'policyIds'
175
+ "organizationIds": organization_ids # Required parameter
176
+ }
177
+
178
+ response = self.http_client.put("policies/priorities", json_data=payload)
179
+ return response
180
+ except Exception as e:
181
+ # Return a simulated response for testing
182
+ return {
183
+ "success": False,
184
+ "error": str(e),
185
+ "updated_policies": []
186
+ }
@@ -0,0 +1,79 @@
1
+ """
2
+ Preset Filters API for the Binalyze AIR SDK.
3
+ """
4
+
5
+ from typing import Optional, Union, Dict, Any, List
6
+
7
+ from ..http_client import HTTPClient
8
+ from ..models.preset_filters import (
9
+ PresetFilter, PresetFiltersList, PresetFiltersFilter, CreatePresetFilterRequest, UpdatePresetFilterRequest
10
+ )
11
+ from ..queries.preset_filters import GetPresetFiltersQuery, GetPresetFilterByIdQuery
12
+ from ..commands.preset_filters import CreatePresetFilterCommand, UpdatePresetFilterCommand, DeletePresetFilterCommand
13
+
14
+
15
+ class PresetFiltersAPI:
16
+ """Preset Filters API with CQRS pattern - separated queries and commands."""
17
+
18
+ def __init__(self, http_client: HTTPClient):
19
+ self.http_client = http_client
20
+
21
+ # QUERIES (Read operations)
22
+ def get_preset_filters(self, filter_params: Optional[PresetFiltersFilter] = None) -> PresetFiltersList:
23
+ """Get preset filters with optional filtering."""
24
+ query = GetPresetFiltersQuery(self.http_client, filter_params)
25
+ return query.execute()
26
+
27
+ def get_preset_filter_by_id(self, filter_id: Union[int, str]) -> Optional[PresetFilter]:
28
+ """Get a specific preset filter by ID."""
29
+ query = GetPresetFilterByIdQuery(self.http_client, str(filter_id))
30
+ return query.execute()
31
+
32
+ # Convenience methods for common queries
33
+ def get_preset_filters_by_organization(self, organization_id: Union[int, str]) -> PresetFiltersList:
34
+ """Get preset filters by organization ID."""
35
+ filter_params = PresetFiltersFilter()
36
+ filter_params.organization_id = int(organization_id)
37
+ return self.get_preset_filters(filter_params)
38
+
39
+ def get_preset_filters_by_type(self, filter_type: str, organization_id: Optional[Union[int, str]] = None) -> PresetFiltersList:
40
+ """Get preset filters by type (e.g., 'ENDPOINT')."""
41
+ filter_params = PresetFiltersFilter()
42
+ filter_params.type = filter_type
43
+ if organization_id is not None:
44
+ filter_params.organization_id = int(organization_id)
45
+ return self.get_preset_filters(filter_params)
46
+
47
+ # COMMANDS (Write operations)
48
+ def create_preset_filter(self, preset_filter_data: CreatePresetFilterRequest) -> PresetFilter:
49
+ """Create a new preset filter."""
50
+ command = CreatePresetFilterCommand(self.http_client, preset_filter_data)
51
+ return command.execute()
52
+
53
+ def update_preset_filter(self, filter_id: Union[int, str], preset_filter_data: UpdatePresetFilterRequest) -> PresetFilter:
54
+ """Update an existing preset filter."""
55
+ command = UpdatePresetFilterCommand(self.http_client, str(filter_id), preset_filter_data)
56
+ return command.execute()
57
+
58
+ def delete_preset_filter(self, filter_id: Union[int, str]) -> Dict[str, Any]:
59
+ """Delete a preset filter by ID."""
60
+ command = DeletePresetFilterCommand(self.http_client, str(filter_id))
61
+ return command.execute()
62
+
63
+ # Convenience methods
64
+ def create_endpoint_filter(
65
+ self, name: str, organization_id: Union[int, str], filter_criteria: List[Dict[str, Any]], created_by: str
66
+ ) -> PresetFilter:
67
+ """Create an endpoint preset filter."""
68
+ preset_filter_data = CreatePresetFilterRequest(
69
+ name=name,
70
+ organizationId=int(organization_id),
71
+ type="ENDPOINT",
72
+ filter=filter_criteria,
73
+ createdBy=created_by
74
+ )
75
+ return self.create_preset_filter(preset_filter_data)
76
+
77
+ def get_endpoint_filters(self, organization_id: Optional[Union[int, str]] = None) -> PresetFiltersList:
78
+ """Get all endpoint preset filters."""
79
+ return self.get_preset_filters_by_type("ENDPOINT", organization_id)
@@ -0,0 +1,71 @@
1
+ """
2
+ Recent Activities API for the Binalyze AIR SDK.
3
+ """
4
+
5
+ from typing import Optional, Union, Dict, Any
6
+
7
+ from ..http_client import HTTPClient
8
+ from ..models.recent_activities import RecentActivity, RecentActivitiesList, RecentActivitiesFilter, CreateRecentActivityRequest
9
+ from ..queries.recent_activities import GetRecentActivitiesQuery
10
+ from ..commands.recent_activities import CreateRecentActivityCommand
11
+
12
+
13
+ class RecentActivitiesAPI:
14
+ """Recent Activities API with CQRS pattern - separated queries and commands."""
15
+
16
+ def __init__(self, http_client: HTTPClient):
17
+ self.http_client = http_client
18
+
19
+ # QUERIES (Read operations)
20
+ def get_recent_activities(self, filter_params: Optional[RecentActivitiesFilter] = None) -> RecentActivitiesList:
21
+ """Get recent activities with optional filtering."""
22
+ query = GetRecentActivitiesQuery(self.http_client, filter_params)
23
+ return query.execute()
24
+
25
+ # Convenience methods for common queries
26
+ def get_recent_activities_by_organization(self, organization_id: Union[int, str]) -> RecentActivitiesList:
27
+ """Get recent activities by organization ID."""
28
+ filter_params = RecentActivitiesFilter()
29
+ filter_params.organization_id = int(organization_id)
30
+ return self.get_recent_activities(filter_params)
31
+
32
+ def get_recent_activities_by_type(self, activity_type: str, organization_id: Optional[Union[int, str]] = None) -> RecentActivitiesList:
33
+ """Get recent activities by type (e.g., 'asset', 'case', 'task', 'report')."""
34
+ filter_params = RecentActivitiesFilter()
35
+ filter_params.type = activity_type
36
+ if organization_id is not None:
37
+ filter_params.organization_id = int(organization_id)
38
+ return self.get_recent_activities(filter_params)
39
+
40
+ def get_recent_activities_by_user(self, username: str, organization_id: Optional[Union[int, str]] = None) -> RecentActivitiesList:
41
+ """Get recent activities by username."""
42
+ filter_params = RecentActivitiesFilter()
43
+ filter_params.username = username
44
+ if organization_id is not None:
45
+ filter_params.organization_id = int(organization_id)
46
+ return self.get_recent_activities(filter_params)
47
+
48
+ def search_recent_activities(self, search_term: str, organization_id: Optional[Union[int, str]] = None) -> RecentActivitiesList:
49
+ """Search recent activities by search term."""
50
+ filter_params = RecentActivitiesFilter()
51
+ filter_params.search_term = search_term
52
+ if organization_id is not None:
53
+ filter_params.organization_id = int(organization_id)
54
+ return self.get_recent_activities(filter_params)
55
+
56
+ # COMMANDS (Write operations)
57
+ def create_recent_activity(self, activity_data: CreateRecentActivityRequest) -> Dict[str, Any]:
58
+ """Create a new recent activity."""
59
+ command = CreateRecentActivityCommand(self.http_client, activity_data)
60
+ return command.execute()
61
+
62
+ # Convenience methods
63
+ def get_recent_activities_count(self, organization_id: Optional[Union[int, str]] = None) -> int:
64
+ """Get total recent activities count."""
65
+ filter_params = RecentActivitiesFilter()
66
+ if organization_id is not None:
67
+ filter_params.organization_id = int(organization_id)
68
+ filter_params.page_size = 1 # We only need the count
69
+
70
+ result = self.get_recent_activities(filter_params)
71
+ return result.total_entity_count or 0
@@ -0,0 +1,104 @@
1
+ """
2
+ Relay Server API for the Binalyze AIR SDK.
3
+ """
4
+
5
+ from typing import Optional, Union, Dict, Any
6
+
7
+ from ..http_client import HTTPClient
8
+ from ..models.relay_server import (
9
+ RelayServer, RelayServersList, RelayServersFilter,
10
+ RebootTaskRequest, ShutdownTaskRequest, LogRetrievalTaskRequest, VersionUpdateTaskRequest,
11
+ UpdateTagsRequest, UpdateLabelRequest, UpdateAddressRequest
12
+ )
13
+ from ..queries.relay_server import GetRelayServersQuery, GetRelayServerByIdQuery
14
+ from ..commands.relay_server import (
15
+ AssignRebootTaskCommand, AssignShutdownTaskCommand, AssignLogRetrievalTaskCommand,
16
+ AssignVersionUpdateTaskCommand, DeleteRelayServerCommand, UpdateTagsCommand,
17
+ UpdateLabelCommand, UpdateAddressCommand
18
+ )
19
+
20
+
21
+ class RelayServerAPI:
22
+ """Relay Server API with CQRS pattern - separated queries and commands."""
23
+
24
+ def __init__(self, http_client: HTTPClient):
25
+ self.http_client = http_client
26
+
27
+ # QUERIES (Read operations)
28
+ def get_relay_servers(self, filter_params: Optional[RelayServersFilter] = None) -> RelayServersList:
29
+ """Get relay servers with optional filtering."""
30
+ query = GetRelayServersQuery(self.http_client, filter_params)
31
+ return query.execute()
32
+
33
+ def get_relay_server_by_id(self, server_id: Union[int, str]) -> Optional[RelayServer]:
34
+ """Get a specific relay server by ID."""
35
+ query = GetRelayServerByIdQuery(self.http_client, str(server_id))
36
+ return query.execute()
37
+
38
+ # Convenience methods for common queries
39
+ def get_relay_servers_by_organization(self, organization_id: Union[int, str]) -> RelayServersList:
40
+ """Get relay servers by organization ID."""
41
+ filter_params = RelayServersFilter()
42
+ filter_params.organization_id = int(organization_id)
43
+ return self.get_relay_servers(filter_params)
44
+
45
+ def get_online_relay_servers(self, organization_id: Optional[Union[int, str]] = None) -> RelayServersList:
46
+ """Get online relay servers."""
47
+ filter_params = RelayServersFilter()
48
+ filter_params.online_status = "online"
49
+ if organization_id is not None:
50
+ filter_params.organization_id = int(organization_id)
51
+ return self.get_relay_servers(filter_params)
52
+
53
+ # COMMANDS (Write operations - Task assignments)
54
+ def assign_reboot_task(self, relay_server_id: Union[int, str], task_request: RebootTaskRequest) -> Dict[str, Any]:
55
+ """Assign reboot task to relay server."""
56
+ command = AssignRebootTaskCommand(self.http_client, str(relay_server_id), task_request)
57
+ return command.execute()
58
+
59
+ def assign_shutdown_task(self, relay_server_id: Union[int, str], task_request: ShutdownTaskRequest) -> Dict[str, Any]:
60
+ """Assign shutdown task to relay server."""
61
+ command = AssignShutdownTaskCommand(self.http_client, str(relay_server_id), task_request)
62
+ return command.execute()
63
+
64
+ def assign_log_retrieval_task(self, relay_server_id: Union[int, str], task_request: LogRetrievalTaskRequest) -> Dict[str, Any]:
65
+ """Assign log retrieval task to relay server."""
66
+ command = AssignLogRetrievalTaskCommand(self.http_client, str(relay_server_id), task_request)
67
+ return command.execute()
68
+
69
+ def assign_version_update_task(self, relay_server_id: Union[int, str], task_request: VersionUpdateTaskRequest) -> Dict[str, Any]:
70
+ """Assign version update task to relay server."""
71
+ command = AssignVersionUpdateTaskCommand(self.http_client, str(relay_server_id), task_request)
72
+ return command.execute()
73
+
74
+ # COMMANDS (Write operations - Server management)
75
+ def delete_relay_server(self, server_id: Union[int, str]) -> Dict[str, Any]:
76
+ """Delete a relay server by ID."""
77
+ command = DeleteRelayServerCommand(self.http_client, str(server_id))
78
+ return command.execute()
79
+
80
+ def update_tags(self, relay_server_id: Union[int, str], tags_request: UpdateTagsRequest) -> Dict[str, Any]:
81
+ """Update tags for a relay server."""
82
+ command = UpdateTagsCommand(self.http_client, str(relay_server_id), tags_request)
83
+ return command.execute()
84
+
85
+ def update_label(self, relay_server_id: Union[int, str], label_request: UpdateLabelRequest) -> Dict[str, Any]:
86
+ """Update label for a relay server."""
87
+ command = UpdateLabelCommand(self.http_client, str(relay_server_id), label_request)
88
+ return command.execute()
89
+
90
+ def update_address(self, relay_server_id: Union[int, str], address_request: UpdateAddressRequest) -> Dict[str, Any]:
91
+ """Update address for a relay server."""
92
+ command = UpdateAddressCommand(self.http_client, str(relay_server_id), address_request)
93
+ return command.execute()
94
+
95
+ # Convenience methods
96
+ def get_relay_servers_count(self, organization_id: Optional[Union[int, str]] = None) -> int:
97
+ """Get total relay servers count."""
98
+ filter_params = RelayServersFilter()
99
+ if organization_id is not None:
100
+ filter_params.organization_id = int(organization_id)
101
+ filter_params.page_size = 1 # We only need the count
102
+
103
+ result = self.get_relay_servers(filter_params)
104
+ return result.total_entity_count or 0