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.
- mcp_eregistrations_bpa/__init__.py +121 -0
- mcp_eregistrations_bpa/__main__.py +6 -0
- mcp_eregistrations_bpa/arazzo/__init__.py +21 -0
- mcp_eregistrations_bpa/arazzo/expression.py +379 -0
- mcp_eregistrations_bpa/audit/__init__.py +56 -0
- mcp_eregistrations_bpa/audit/context.py +66 -0
- mcp_eregistrations_bpa/audit/logger.py +236 -0
- mcp_eregistrations_bpa/audit/models.py +131 -0
- mcp_eregistrations_bpa/auth/__init__.py +64 -0
- mcp_eregistrations_bpa/auth/callback.py +391 -0
- mcp_eregistrations_bpa/auth/cas.py +409 -0
- mcp_eregistrations_bpa/auth/oidc.py +252 -0
- mcp_eregistrations_bpa/auth/permissions.py +162 -0
- mcp_eregistrations_bpa/auth/token_manager.py +348 -0
- mcp_eregistrations_bpa/bpa_client/__init__.py +84 -0
- mcp_eregistrations_bpa/bpa_client/client.py +740 -0
- mcp_eregistrations_bpa/bpa_client/endpoints.py +193 -0
- mcp_eregistrations_bpa/bpa_client/errors.py +276 -0
- mcp_eregistrations_bpa/bpa_client/models.py +203 -0
- mcp_eregistrations_bpa/config.py +349 -0
- mcp_eregistrations_bpa/db/__init__.py +21 -0
- mcp_eregistrations_bpa/db/connection.py +64 -0
- mcp_eregistrations_bpa/db/migrations.py +168 -0
- mcp_eregistrations_bpa/exceptions.py +39 -0
- mcp_eregistrations_bpa/py.typed +0 -0
- mcp_eregistrations_bpa/rollback/__init__.py +19 -0
- mcp_eregistrations_bpa/rollback/manager.py +616 -0
- mcp_eregistrations_bpa/server.py +152 -0
- mcp_eregistrations_bpa/tools/__init__.py +372 -0
- mcp_eregistrations_bpa/tools/actions.py +155 -0
- mcp_eregistrations_bpa/tools/analysis.py +352 -0
- mcp_eregistrations_bpa/tools/audit.py +399 -0
- mcp_eregistrations_bpa/tools/behaviours.py +1042 -0
- mcp_eregistrations_bpa/tools/bots.py +627 -0
- mcp_eregistrations_bpa/tools/classifications.py +575 -0
- mcp_eregistrations_bpa/tools/costs.py +765 -0
- mcp_eregistrations_bpa/tools/debug_strategies.py +351 -0
- mcp_eregistrations_bpa/tools/debugger.py +1230 -0
- mcp_eregistrations_bpa/tools/determinants.py +2235 -0
- mcp_eregistrations_bpa/tools/document_requirements.py +670 -0
- mcp_eregistrations_bpa/tools/export.py +899 -0
- mcp_eregistrations_bpa/tools/fields.py +162 -0
- mcp_eregistrations_bpa/tools/form_errors.py +36 -0
- mcp_eregistrations_bpa/tools/formio_helpers.py +971 -0
- mcp_eregistrations_bpa/tools/forms.py +1269 -0
- mcp_eregistrations_bpa/tools/jsonlogic_builder.py +466 -0
- mcp_eregistrations_bpa/tools/large_response.py +163 -0
- mcp_eregistrations_bpa/tools/messages.py +523 -0
- mcp_eregistrations_bpa/tools/notifications.py +241 -0
- mcp_eregistrations_bpa/tools/registration_institutions.py +680 -0
- mcp_eregistrations_bpa/tools/registrations.py +897 -0
- mcp_eregistrations_bpa/tools/role_status.py +447 -0
- mcp_eregistrations_bpa/tools/role_units.py +400 -0
- mcp_eregistrations_bpa/tools/roles.py +1236 -0
- mcp_eregistrations_bpa/tools/rollback.py +335 -0
- mcp_eregistrations_bpa/tools/services.py +674 -0
- mcp_eregistrations_bpa/tools/workflows.py +2487 -0
- mcp_eregistrations_bpa/tools/yaml_transformer.py +991 -0
- mcp_eregistrations_bpa/workflows/__init__.py +28 -0
- mcp_eregistrations_bpa/workflows/loader.py +440 -0
- mcp_eregistrations_bpa/workflows/models.py +336 -0
- mcp_eregistrations_bpa-0.8.5.dist-info/METADATA +965 -0
- mcp_eregistrations_bpa-0.8.5.dist-info/RECORD +66 -0
- mcp_eregistrations_bpa-0.8.5.dist-info/WHEEL +4 -0
- mcp_eregistrations_bpa-0.8.5.dist-info/entry_points.txt +2 -0
- 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
|
+
}
|