mcp-eregistrations-bpa 0.8.5__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 mcp-eregistrations-bpa might be problematic. Click here for more details.

Files changed (66) hide show
  1. mcp_eregistrations_bpa/__init__.py +121 -0
  2. mcp_eregistrations_bpa/__main__.py +6 -0
  3. mcp_eregistrations_bpa/arazzo/__init__.py +21 -0
  4. mcp_eregistrations_bpa/arazzo/expression.py +379 -0
  5. mcp_eregistrations_bpa/audit/__init__.py +56 -0
  6. mcp_eregistrations_bpa/audit/context.py +66 -0
  7. mcp_eregistrations_bpa/audit/logger.py +236 -0
  8. mcp_eregistrations_bpa/audit/models.py +131 -0
  9. mcp_eregistrations_bpa/auth/__init__.py +64 -0
  10. mcp_eregistrations_bpa/auth/callback.py +391 -0
  11. mcp_eregistrations_bpa/auth/cas.py +409 -0
  12. mcp_eregistrations_bpa/auth/oidc.py +252 -0
  13. mcp_eregistrations_bpa/auth/permissions.py +162 -0
  14. mcp_eregistrations_bpa/auth/token_manager.py +348 -0
  15. mcp_eregistrations_bpa/bpa_client/__init__.py +84 -0
  16. mcp_eregistrations_bpa/bpa_client/client.py +740 -0
  17. mcp_eregistrations_bpa/bpa_client/endpoints.py +193 -0
  18. mcp_eregistrations_bpa/bpa_client/errors.py +276 -0
  19. mcp_eregistrations_bpa/bpa_client/models.py +203 -0
  20. mcp_eregistrations_bpa/config.py +349 -0
  21. mcp_eregistrations_bpa/db/__init__.py +21 -0
  22. mcp_eregistrations_bpa/db/connection.py +64 -0
  23. mcp_eregistrations_bpa/db/migrations.py +168 -0
  24. mcp_eregistrations_bpa/exceptions.py +39 -0
  25. mcp_eregistrations_bpa/py.typed +0 -0
  26. mcp_eregistrations_bpa/rollback/__init__.py +19 -0
  27. mcp_eregistrations_bpa/rollback/manager.py +616 -0
  28. mcp_eregistrations_bpa/server.py +152 -0
  29. mcp_eregistrations_bpa/tools/__init__.py +372 -0
  30. mcp_eregistrations_bpa/tools/actions.py +155 -0
  31. mcp_eregistrations_bpa/tools/analysis.py +352 -0
  32. mcp_eregistrations_bpa/tools/audit.py +399 -0
  33. mcp_eregistrations_bpa/tools/behaviours.py +1042 -0
  34. mcp_eregistrations_bpa/tools/bots.py +627 -0
  35. mcp_eregistrations_bpa/tools/classifications.py +575 -0
  36. mcp_eregistrations_bpa/tools/costs.py +765 -0
  37. mcp_eregistrations_bpa/tools/debug_strategies.py +351 -0
  38. mcp_eregistrations_bpa/tools/debugger.py +1230 -0
  39. mcp_eregistrations_bpa/tools/determinants.py +2235 -0
  40. mcp_eregistrations_bpa/tools/document_requirements.py +670 -0
  41. mcp_eregistrations_bpa/tools/export.py +899 -0
  42. mcp_eregistrations_bpa/tools/fields.py +162 -0
  43. mcp_eregistrations_bpa/tools/form_errors.py +36 -0
  44. mcp_eregistrations_bpa/tools/formio_helpers.py +971 -0
  45. mcp_eregistrations_bpa/tools/forms.py +1269 -0
  46. mcp_eregistrations_bpa/tools/jsonlogic_builder.py +466 -0
  47. mcp_eregistrations_bpa/tools/large_response.py +163 -0
  48. mcp_eregistrations_bpa/tools/messages.py +523 -0
  49. mcp_eregistrations_bpa/tools/notifications.py +241 -0
  50. mcp_eregistrations_bpa/tools/registration_institutions.py +680 -0
  51. mcp_eregistrations_bpa/tools/registrations.py +897 -0
  52. mcp_eregistrations_bpa/tools/role_status.py +447 -0
  53. mcp_eregistrations_bpa/tools/role_units.py +400 -0
  54. mcp_eregistrations_bpa/tools/roles.py +1236 -0
  55. mcp_eregistrations_bpa/tools/rollback.py +335 -0
  56. mcp_eregistrations_bpa/tools/services.py +674 -0
  57. mcp_eregistrations_bpa/tools/workflows.py +2487 -0
  58. mcp_eregistrations_bpa/tools/yaml_transformer.py +991 -0
  59. mcp_eregistrations_bpa/workflows/__init__.py +28 -0
  60. mcp_eregistrations_bpa/workflows/loader.py +440 -0
  61. mcp_eregistrations_bpa/workflows/models.py +336 -0
  62. mcp_eregistrations_bpa-0.8.5.dist-info/METADATA +965 -0
  63. mcp_eregistrations_bpa-0.8.5.dist-info/RECORD +66 -0
  64. mcp_eregistrations_bpa-0.8.5.dist-info/WHEEL +4 -0
  65. mcp_eregistrations_bpa-0.8.5.dist-info/entry_points.txt +2 -0
  66. mcp_eregistrations_bpa-0.8.5.dist-info/licenses/LICENSE +86 -0
@@ -0,0 +1,351 @@
1
+ """Issue type definitions and fix strategies for BPA service debugger.
2
+
3
+ This module defines the known issue types returned by the BPA debug endpoint,
4
+ their severity levels, and strategies for investigating and fixing them.
5
+
6
+ The debug endpoint (POST /service/{id}/recover-orphan-config) returns issues
7
+ that reference orphaned or invalid configuration elements.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from dataclasses import dataclass
13
+ from enum import Enum
14
+ from typing import Any
15
+
16
+ # Type alias for issue dicts
17
+ IssueDict = dict[str, Any]
18
+ IssueList = list[IssueDict]
19
+
20
+ __all__ = [
21
+ "Severity",
22
+ "IssueTypeInfo",
23
+ "FixStrategy",
24
+ "ISSUE_TYPES",
25
+ "get_issue_info",
26
+ "group_issues_by_type",
27
+ "group_issues_by_severity",
28
+ "prioritize_issues",
29
+ ]
30
+
31
+
32
+ class Severity(str, Enum):
33
+ """Issue severity levels."""
34
+
35
+ HIGH = "high"
36
+ MEDIUM = "medium"
37
+ LOW = "low"
38
+
39
+
40
+ class FixStrategy(str, Enum):
41
+ """Available fix strategies."""
42
+
43
+ DELETE_EFFECT = "delete_effect"
44
+ DELETE_DETERMINANT = "delete_determinant"
45
+ DELETE_BEHAVIOUR = "delete_behaviour"
46
+ CLEAR_CATALOG_REFERENCE = "clear_catalog_reference"
47
+ REMOVE_TRANSLATION = "remove_translation"
48
+ CLEAR_COPY_VALUE = "clear_copy_value"
49
+ REMOVE_DUPLICATE = "remove_duplicate"
50
+ FIX_ROLE_REGISTRATION = "fix_role_registration"
51
+ MANUAL_REVIEW = "manual_review"
52
+
53
+
54
+ @dataclass
55
+ class IssueTypeInfo:
56
+ """Information about an issue type."""
57
+
58
+ name: str
59
+ description: str
60
+ severity: Severity
61
+ fix_strategy: FixStrategy
62
+ investigation_tool: str | None
63
+ fix_tool: str | None
64
+ requires_user_input: bool = False
65
+ batch_fixable: bool = True
66
+
67
+ def to_dict(self) -> dict[str, Any]:
68
+ """Convert to dictionary representation."""
69
+ return {
70
+ "name": self.name,
71
+ "description": self.description,
72
+ "severity": self.severity.value,
73
+ "fix_strategy": self.fix_strategy.value,
74
+ "investigation_tool": self.investigation_tool,
75
+ "fix_tool": self.fix_tool,
76
+ "requires_user_input": self.requires_user_input,
77
+ "batch_fixable": self.batch_fixable,
78
+ }
79
+
80
+
81
+ # Issue type definitions based on BPA debug endpoint response
82
+ ISSUE_TYPES: dict[str, IssueTypeInfo] = {
83
+ # HIGH SEVERITY - Break service functionality
84
+ "effects_determinant": IssueTypeInfo(
85
+ name="effects_determinant",
86
+ description="Component behaviour references a non-existent determinant",
87
+ severity=Severity.HIGH,
88
+ fix_strategy=FixStrategy.DELETE_EFFECT,
89
+ investigation_tool="componentbehaviour_get_by_component",
90
+ fix_tool="effect_delete",
91
+ requires_user_input=False,
92
+ batch_fixable=True,
93
+ ),
94
+ "determinant": IssueTypeInfo(
95
+ name="determinant",
96
+ description="Orphaned determinant with invalid field references",
97
+ severity=Severity.HIGH,
98
+ fix_strategy=FixStrategy.DELETE_DETERMINANT,
99
+ investigation_tool="determinant_get",
100
+ fix_tool="determinant_delete",
101
+ requires_user_input=False,
102
+ batch_fixable=True,
103
+ ),
104
+ # MEDIUM SEVERITY - May cause display or logic issues
105
+ "missing_determinants_in_component_behaviours": IssueTypeInfo(
106
+ name="missing_determinants_in_component_behaviours",
107
+ description="Component behaviour has empty determinant list",
108
+ severity=Severity.MEDIUM,
109
+ fix_strategy=FixStrategy.DELETE_BEHAVIOUR,
110
+ investigation_tool="componentbehaviour_get_by_component",
111
+ fix_tool="effect_delete",
112
+ requires_user_input=False,
113
+ batch_fixable=True,
114
+ ),
115
+ "translation_moustache": IssueTypeInfo(
116
+ name="translation_moustache",
117
+ description="Translation references non-existent form field",
118
+ severity=Severity.MEDIUM,
119
+ fix_strategy=FixStrategy.REMOVE_TRANSLATION,
120
+ investigation_tool=None,
121
+ fix_tool="debug_fix", # Uses /translations/sync
122
+ requires_user_input=False,
123
+ batch_fixable=True, # Translation sync can be batch-applied
124
+ ),
125
+ "catalog": IssueTypeInfo(
126
+ name="catalog",
127
+ description="Component references non-existent catalog",
128
+ severity=Severity.MEDIUM,
129
+ fix_strategy=FixStrategy.CLEAR_CATALOG_REFERENCE,
130
+ investigation_tool="form_component_get",
131
+ fix_tool="debug_fix", # Uses clear_catalog to clear dataSrc
132
+ requires_user_input=False, # Auto-clears invalid reference
133
+ batch_fixable=True,
134
+ ),
135
+ # LOW SEVERITY - Minor display or configuration issues
136
+ "component_content_moustache": IssueTypeInfo(
137
+ name="component_content_moustache",
138
+ description="Content block references missing form field",
139
+ severity=Severity.LOW,
140
+ fix_strategy=FixStrategy.MANUAL_REVIEW,
141
+ investigation_tool="form_component_get",
142
+ fix_tool="form_component_update",
143
+ requires_user_input=True,
144
+ batch_fixable=False,
145
+ ),
146
+ "component_html_moustache": IssueTypeInfo(
147
+ name="component_html_moustache",
148
+ description="HTML component references missing form field",
149
+ severity=Severity.LOW,
150
+ fix_strategy=FixStrategy.MANUAL_REVIEW,
151
+ investigation_tool="form_component_get",
152
+ fix_tool="form_component_update",
153
+ requires_user_input=True,
154
+ batch_fixable=False,
155
+ ),
156
+ "component_label_missing_moustache": IssueTypeInfo(
157
+ name="component_label_missing_moustache",
158
+ description="Component label references missing variable",
159
+ severity=Severity.LOW,
160
+ fix_strategy=FixStrategy.MANUAL_REVIEW,
161
+ investigation_tool="form_component_get",
162
+ fix_tool="form_component_update",
163
+ requires_user_input=True,
164
+ batch_fixable=False,
165
+ ),
166
+ "component_linking_requirement_orphan_reference": IssueTypeInfo(
167
+ name="component_linking_requirement_orphan_reference",
168
+ description="Requirement link references non-existent component",
169
+ severity=Severity.LOW,
170
+ fix_strategy=FixStrategy.MANUAL_REVIEW,
171
+ investigation_tool="documentrequirement_list",
172
+ fix_tool="documentrequirement_update",
173
+ requires_user_input=True,
174
+ batch_fixable=False,
175
+ ),
176
+ "copyValueFrom": IssueTypeInfo(
177
+ name="copyValueFrom",
178
+ description="Copy-value action references non-existent field",
179
+ severity=Severity.LOW,
180
+ fix_strategy=FixStrategy.CLEAR_COPY_VALUE,
181
+ investigation_tool="form_component_get",
182
+ fix_tool="form_component_update",
183
+ requires_user_input=False,
184
+ batch_fixable=True,
185
+ ),
186
+ "duplicate_component_on_form": IssueTypeInfo(
187
+ name="duplicate_component_on_form",
188
+ description="Duplicate component keys detected on form",
189
+ severity=Severity.LOW,
190
+ fix_strategy=FixStrategy.REMOVE_DUPLICATE,
191
+ investigation_tool="form_get",
192
+ fix_tool="form_component_remove",
193
+ requires_user_input=True, # Need to choose which to keep
194
+ batch_fixable=False,
195
+ ),
196
+ "message_moustache": IssueTypeInfo(
197
+ name="message_moustache",
198
+ description="Message template references missing form field",
199
+ severity=Severity.LOW,
200
+ fix_strategy=FixStrategy.MANUAL_REVIEW,
201
+ investigation_tool="message_get",
202
+ fix_tool="message_update",
203
+ requires_user_input=True,
204
+ batch_fixable=False,
205
+ ),
206
+ "role_registration_missing": IssueTypeInfo(
207
+ name="role_registration_missing",
208
+ description="Role references non-existent registration",
209
+ severity=Severity.LOW,
210
+ fix_strategy=FixStrategy.FIX_ROLE_REGISTRATION,
211
+ investigation_tool="role_get",
212
+ fix_tool="role_update",
213
+ requires_user_input=True, # Need to select correct registration
214
+ batch_fixable=False,
215
+ ),
216
+ }
217
+
218
+
219
+ def get_issue_info(object_type: str) -> IssueTypeInfo | None:
220
+ """Get information about an issue type.
221
+
222
+ Args:
223
+ object_type: The objectType value from BPA debug response.
224
+
225
+ Returns:
226
+ IssueTypeInfo if known, None otherwise.
227
+ """
228
+ return ISSUE_TYPES.get(object_type)
229
+
230
+
231
+ def group_issues_by_type(issues: IssueList) -> dict[str, IssueList]:
232
+ """Group issues by their objectType.
233
+
234
+ Args:
235
+ issues: List of issue dicts from BPA debug endpoint.
236
+
237
+ Returns:
238
+ Dict mapping objectType to list of issues.
239
+ """
240
+ grouped: dict[str, IssueList] = {}
241
+ for issue in issues:
242
+ obj_type = issue.get("objectType", "unknown")
243
+ if obj_type not in grouped:
244
+ grouped[obj_type] = []
245
+ grouped[obj_type].append(issue)
246
+ return grouped
247
+
248
+
249
+ def group_issues_by_severity(issues: IssueList) -> dict[str, IssueList]:
250
+ """Group issues by severity level.
251
+
252
+ Args:
253
+ issues: List of issue dicts from BPA debug endpoint.
254
+
255
+ Returns:
256
+ Dict mapping severity to list of issues.
257
+ """
258
+ grouped: dict[str, IssueList] = {"high": [], "medium": [], "low": [], "unknown": []}
259
+ for issue in issues:
260
+ obj_type = issue.get("objectType", "unknown")
261
+ info = get_issue_info(obj_type)
262
+ if info:
263
+ grouped[info.severity.value].append(issue)
264
+ else:
265
+ grouped["unknown"].append(issue)
266
+ return grouped
267
+
268
+
269
+ def prioritize_issues(issues: IssueList) -> IssueList:
270
+ """Sort issues by fix priority.
271
+
272
+ Priority order:
273
+ 1. High severity, batch-fixable first (effects_determinant, determinant)
274
+ 2. Medium severity, batch-fixable
275
+ 3. High severity, requires user input
276
+ 4. Medium severity, requires user input
277
+ 5. Low severity
278
+
279
+ Args:
280
+ issues: List of issue dicts from BPA debug endpoint.
281
+
282
+ Returns:
283
+ Sorted list of issues.
284
+ """
285
+ severity_order: dict[str, int] = {"high": 0, "medium": 1, "low": 2, "unknown": 3}
286
+
287
+ def sort_key(issue: IssueDict) -> tuple[int, int, str]:
288
+ obj_type = issue.get("objectType", "unknown")
289
+ info = get_issue_info(obj_type)
290
+ if info:
291
+ severity_rank = severity_order.get(info.severity.value, 3)
292
+ # Batch-fixable items come first within same severity
293
+ batch_rank = 0 if info.batch_fixable else 1
294
+ else:
295
+ severity_rank = 3
296
+ batch_rank = 1
297
+ return (severity_rank, batch_rank, obj_type)
298
+
299
+ return sorted(issues, key=sort_key)
300
+
301
+
302
+ def get_fix_summary(issues: IssueList) -> dict[str, Any]:
303
+ """Generate a summary of issues for user approval.
304
+
305
+ Args:
306
+ issues: List of issue dicts from BPA debug endpoint.
307
+
308
+ Returns:
309
+ Summary dict with counts, severity breakdown, and recommended actions.
310
+ """
311
+ by_type = group_issues_by_type(issues)
312
+ by_severity = group_issues_by_severity(issues)
313
+
314
+ # Count batch-fixable vs manual
315
+ batch_fixable_count = 0
316
+ manual_count = 0
317
+ for issue in issues:
318
+ info = get_issue_info(issue.get("objectType", ""))
319
+ if info and info.batch_fixable:
320
+ batch_fixable_count += 1
321
+ else:
322
+ manual_count += 1
323
+
324
+ type_summary: list[dict[str, Any]] = []
325
+ for obj_type, type_issues in by_type.items():
326
+ info = get_issue_info(obj_type)
327
+ type_summary.append(
328
+ {
329
+ "type": obj_type,
330
+ "count": len(type_issues),
331
+ "severity": info.severity.value if info else "unknown",
332
+ "fix_strategy": info.fix_strategy.value if info else "manual_review",
333
+ "batch_fixable": info.batch_fixable if info else False,
334
+ }
335
+ )
336
+
337
+ # Sort by count descending
338
+ type_summary.sort(key=lambda x: int(x["count"]), reverse=True)
339
+
340
+ return {
341
+ "total_issues": len(issues),
342
+ "by_severity": {
343
+ "high": len(by_severity["high"]),
344
+ "medium": len(by_severity["medium"]),
345
+ "low": len(by_severity["low"]),
346
+ "unknown": len(by_severity["unknown"]),
347
+ },
348
+ "batch_fixable": batch_fixable_count,
349
+ "requires_manual": manual_count,
350
+ "by_type": type_summary,
351
+ }