julee 0.1.4__py3-none-any.whl → 0.1.6__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.
- julee/__init__.py +1 -1
- julee/api/tests/routers/test_assembly_specifications.py +2 -0
- julee/api/tests/routers/test_documents.py +2 -0
- julee/api/tests/routers/test_knowledge_service_configs.py +2 -0
- julee/api/tests/routers/test_knowledge_service_queries.py +2 -0
- julee/api/tests/routers/test_system.py +2 -0
- julee/api/tests/routers/test_workflows.py +2 -0
- julee/api/tests/test_app.py +2 -0
- julee/api/tests/test_dependencies.py +2 -0
- julee/api/tests/test_requests.py +2 -0
- julee/contrib/polling/__init__.py +22 -19
- julee/contrib/polling/apps/__init__.py +17 -0
- julee/contrib/polling/apps/worker/__init__.py +17 -0
- julee/contrib/polling/apps/worker/pipelines.py +288 -0
- julee/contrib/polling/domain/__init__.py +7 -9
- julee/contrib/polling/domain/models/__init__.py +6 -7
- julee/contrib/polling/domain/models/polling_config.py +18 -1
- julee/contrib/polling/domain/services/__init__.py +6 -5
- julee/contrib/polling/domain/services/poller.py +1 -1
- julee/contrib/polling/infrastructure/__init__.py +9 -8
- julee/contrib/polling/infrastructure/services/__init__.py +6 -5
- julee/contrib/polling/infrastructure/services/polling/__init__.py +6 -5
- julee/contrib/polling/infrastructure/services/polling/http/__init__.py +6 -5
- julee/contrib/polling/infrastructure/services/polling/http/http_poller_service.py +5 -2
- julee/contrib/polling/infrastructure/temporal/__init__.py +12 -12
- julee/contrib/polling/infrastructure/temporal/activities.py +1 -1
- julee/contrib/polling/infrastructure/temporal/manager.py +291 -0
- julee/contrib/polling/infrastructure/temporal/proxies.py +1 -1
- julee/contrib/polling/tests/unit/apps/worker/test_pipelines.py +580 -0
- julee/contrib/polling/tests/unit/infrastructure/services/polling/http/test_http_poller_service.py +40 -2
- julee/contrib/polling/tests/unit/infrastructure/temporal/__init__.py +7 -0
- julee/contrib/polling/tests/unit/infrastructure/temporal/test_manager.py +475 -0
- julee/docs/sphinx_hcd/__init__.py +146 -13
- julee/docs/sphinx_hcd/domain/__init__.py +5 -0
- julee/docs/sphinx_hcd/domain/models/__init__.py +32 -0
- julee/docs/sphinx_hcd/domain/models/accelerator.py +152 -0
- julee/docs/sphinx_hcd/domain/models/app.py +151 -0
- julee/docs/sphinx_hcd/domain/models/code_info.py +121 -0
- julee/docs/sphinx_hcd/domain/models/epic.py +79 -0
- julee/docs/sphinx_hcd/domain/models/integration.py +230 -0
- julee/docs/sphinx_hcd/domain/models/journey.py +222 -0
- julee/docs/sphinx_hcd/domain/models/persona.py +106 -0
- julee/docs/sphinx_hcd/domain/models/story.py +128 -0
- julee/docs/sphinx_hcd/domain/repositories/__init__.py +25 -0
- julee/docs/sphinx_hcd/domain/repositories/accelerator.py +98 -0
- julee/docs/sphinx_hcd/domain/repositories/app.py +57 -0
- julee/docs/sphinx_hcd/domain/repositories/base.py +89 -0
- julee/docs/sphinx_hcd/domain/repositories/code_info.py +69 -0
- julee/docs/sphinx_hcd/domain/repositories/epic.py +62 -0
- julee/docs/sphinx_hcd/domain/repositories/integration.py +79 -0
- julee/docs/sphinx_hcd/domain/repositories/journey.py +106 -0
- julee/docs/sphinx_hcd/domain/repositories/story.py +68 -0
- julee/docs/sphinx_hcd/domain/use_cases/__init__.py +64 -0
- julee/docs/sphinx_hcd/domain/use_cases/derive_personas.py +166 -0
- julee/docs/sphinx_hcd/domain/use_cases/resolve_accelerator_references.py +236 -0
- julee/docs/sphinx_hcd/domain/use_cases/resolve_app_references.py +144 -0
- julee/docs/sphinx_hcd/domain/use_cases/resolve_story_references.py +121 -0
- julee/docs/sphinx_hcd/parsers/__init__.py +48 -0
- julee/docs/sphinx_hcd/parsers/ast.py +150 -0
- julee/docs/sphinx_hcd/parsers/gherkin.py +155 -0
- julee/docs/sphinx_hcd/parsers/yaml.py +184 -0
- julee/docs/sphinx_hcd/repositories/__init__.py +4 -0
- julee/docs/sphinx_hcd/repositories/memory/__init__.py +25 -0
- julee/docs/sphinx_hcd/repositories/memory/accelerator.py +86 -0
- julee/docs/sphinx_hcd/repositories/memory/app.py +45 -0
- julee/docs/sphinx_hcd/repositories/memory/base.py +106 -0
- julee/docs/sphinx_hcd/repositories/memory/code_info.py +59 -0
- julee/docs/sphinx_hcd/repositories/memory/epic.py +54 -0
- julee/docs/sphinx_hcd/repositories/memory/integration.py +70 -0
- julee/docs/sphinx_hcd/repositories/memory/journey.py +96 -0
- julee/docs/sphinx_hcd/repositories/memory/story.py +63 -0
- julee/docs/sphinx_hcd/sphinx/__init__.py +28 -0
- julee/docs/sphinx_hcd/sphinx/adapters.py +116 -0
- julee/docs/sphinx_hcd/sphinx/context.py +163 -0
- julee/docs/sphinx_hcd/sphinx/directives/__init__.py +160 -0
- julee/docs/sphinx_hcd/sphinx/directives/accelerator.py +576 -0
- julee/docs/sphinx_hcd/sphinx/directives/app.py +349 -0
- julee/docs/sphinx_hcd/sphinx/directives/base.py +211 -0
- julee/docs/sphinx_hcd/sphinx/directives/epic.py +434 -0
- julee/docs/sphinx_hcd/sphinx/directives/integration.py +220 -0
- julee/docs/sphinx_hcd/sphinx/directives/journey.py +642 -0
- julee/docs/sphinx_hcd/sphinx/directives/persona.py +345 -0
- julee/docs/sphinx_hcd/sphinx/directives/story.py +575 -0
- julee/docs/sphinx_hcd/sphinx/event_handlers/__init__.py +16 -0
- julee/docs/sphinx_hcd/sphinx/event_handlers/builder_inited.py +31 -0
- julee/docs/sphinx_hcd/sphinx/event_handlers/doctree_read.py +27 -0
- julee/docs/sphinx_hcd/sphinx/event_handlers/doctree_resolved.py +43 -0
- julee/docs/sphinx_hcd/sphinx/event_handlers/env_purge_doc.py +42 -0
- julee/docs/sphinx_hcd/sphinx/initialization.py +139 -0
- julee/docs/sphinx_hcd/tests/__init__.py +9 -0
- julee/docs/sphinx_hcd/tests/conftest.py +6 -0
- julee/docs/sphinx_hcd/tests/domain/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/domain/models/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_accelerator.py +266 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_app.py +258 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_code_info.py +231 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_epic.py +163 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_integration.py +327 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_journey.py +249 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_persona.py +172 -0
- julee/docs/sphinx_hcd/tests/domain/models/test_story.py +216 -0
- julee/docs/sphinx_hcd/tests/domain/use_cases/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/domain/use_cases/test_derive_personas.py +314 -0
- julee/docs/sphinx_hcd/tests/domain/use_cases/test_resolve_accelerator_references.py +476 -0
- julee/docs/sphinx_hcd/tests/domain/use_cases/test_resolve_app_references.py +265 -0
- julee/docs/sphinx_hcd/tests/domain/use_cases/test_resolve_story_references.py +229 -0
- julee/docs/sphinx_hcd/tests/integration/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/parsers/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/parsers/test_ast.py +298 -0
- julee/docs/sphinx_hcd/tests/parsers/test_gherkin.py +282 -0
- julee/docs/sphinx_hcd/tests/parsers/test_yaml.py +496 -0
- julee/docs/sphinx_hcd/tests/repositories/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/repositories/test_accelerator.py +298 -0
- julee/docs/sphinx_hcd/tests/repositories/test_app.py +218 -0
- julee/docs/sphinx_hcd/tests/repositories/test_base.py +151 -0
- julee/docs/sphinx_hcd/tests/repositories/test_code_info.py +253 -0
- julee/docs/sphinx_hcd/tests/repositories/test_epic.py +237 -0
- julee/docs/sphinx_hcd/tests/repositories/test_integration.py +268 -0
- julee/docs/sphinx_hcd/tests/repositories/test_journey.py +294 -0
- julee/docs/sphinx_hcd/tests/repositories/test_story.py +236 -0
- julee/docs/sphinx_hcd/tests/sphinx/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/sphinx/directives/__init__.py +1 -0
- julee/docs/sphinx_hcd/tests/sphinx/directives/test_base.py +160 -0
- julee/docs/sphinx_hcd/tests/sphinx/test_adapters.py +176 -0
- julee/docs/sphinx_hcd/tests/sphinx/test_context.py +257 -0
- julee/domain/models/assembly/tests/test_assembly.py +2 -0
- julee/domain/models/assembly_specification/tests/test_assembly_specification.py +2 -0
- julee/domain/models/assembly_specification/tests/test_knowledge_service_query.py +2 -0
- julee/domain/models/custom_fields/tests/test_custom_fields.py +2 -0
- julee/domain/models/document/tests/test_document.py +2 -0
- julee/domain/models/policy/tests/test_document_policy_validation.py +2 -0
- julee/domain/models/policy/tests/test_policy.py +2 -0
- julee/domain/use_cases/tests/test_extract_assemble_data.py +2 -0
- julee/domain/use_cases/tests/test_initialize_system_data.py +2 -0
- julee/domain/use_cases/tests/test_validate_document.py +2 -0
- julee/maintenance/release.py +10 -5
- julee/repositories/memory/tests/test_document.py +2 -0
- julee/repositories/memory/tests/test_document_policy_validation.py +2 -0
- julee/repositories/memory/tests/test_policy.py +2 -0
- julee/repositories/minio/tests/test_assembly.py +2 -0
- julee/repositories/minio/tests/test_assembly_specification.py +2 -0
- julee/repositories/minio/tests/test_client_protocol.py +3 -0
- julee/repositories/minio/tests/test_document.py +2 -0
- julee/repositories/minio/tests/test_document_policy_validation.py +2 -0
- julee/repositories/minio/tests/test_knowledge_service_config.py +2 -0
- julee/repositories/minio/tests/test_knowledge_service_query.py +2 -0
- julee/repositories/minio/tests/test_policy.py +2 -0
- julee/services/knowledge_service/anthropic/tests/test_knowledge_service.py +2 -0
- julee/services/knowledge_service/memory/test_knowledge_service.py +2 -0
- julee/services/knowledge_service/test_factory.py +2 -0
- julee/util/tests/test_decorators.py +2 -0
- julee-0.1.6.dist-info/METADATA +104 -0
- julee-0.1.6.dist-info/RECORD +288 -0
- julee/docs/sphinx_hcd/accelerators.py +0 -1175
- julee/docs/sphinx_hcd/apps.py +0 -518
- julee/docs/sphinx_hcd/epics.py +0 -453
- julee/docs/sphinx_hcd/integrations.py +0 -310
- julee/docs/sphinx_hcd/journeys.py +0 -797
- julee/docs/sphinx_hcd/personas.py +0 -457
- julee/docs/sphinx_hcd/stories.py +0 -960
- julee-0.1.4.dist-info/METADATA +0 -197
- julee-0.1.4.dist-info/RECORD +0 -196
- {julee-0.1.4.dist-info → julee-0.1.6.dist-info}/WHEEL +0 -0
- {julee-0.1.4.dist-info → julee-0.1.6.dist-info}/licenses/LICENSE +0 -0
- {julee-0.1.4.dist-info → julee-0.1.6.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Unit tests for PollingManager.
|
|
3
|
+
|
|
4
|
+
This module tests the PollingManager class using mocked Temporal client,
|
|
5
|
+
as the test environment doesn't support schedule operations. We mock the
|
|
6
|
+
Temporal client to test the manager's business logic and error handling
|
|
7
|
+
without requiring actual Temporal infrastructure.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from unittest.mock import AsyncMock, MagicMock
|
|
11
|
+
|
|
12
|
+
import pytest
|
|
13
|
+
from temporalio.client import Client, ScheduleAlreadyRunningError
|
|
14
|
+
|
|
15
|
+
from julee.contrib.polling.domain.models.polling_config import (
|
|
16
|
+
PollingConfig,
|
|
17
|
+
PollingProtocol,
|
|
18
|
+
)
|
|
19
|
+
from julee.contrib.polling.infrastructure.temporal.manager import PollingManager
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@pytest.fixture
|
|
23
|
+
def mock_temporal_client():
|
|
24
|
+
"""Provide a mocked Temporal client."""
|
|
25
|
+
client = AsyncMock(spec=Client)
|
|
26
|
+
|
|
27
|
+
# Mock schedule handle for operations with stateful pause behavior
|
|
28
|
+
mock_schedule_handle = AsyncMock()
|
|
29
|
+
client.get_schedule_handle.return_value = mock_schedule_handle
|
|
30
|
+
|
|
31
|
+
# Create a stateful mock for pause/resume behavior
|
|
32
|
+
paused_state = {"paused": False}
|
|
33
|
+
|
|
34
|
+
async def mock_pause():
|
|
35
|
+
paused_state["paused"] = True
|
|
36
|
+
|
|
37
|
+
async def mock_unpause():
|
|
38
|
+
paused_state["paused"] = False
|
|
39
|
+
|
|
40
|
+
def mock_describe():
|
|
41
|
+
mock_description = MagicMock()
|
|
42
|
+
mock_description.schedule.state.paused = paused_state["paused"]
|
|
43
|
+
return mock_description
|
|
44
|
+
|
|
45
|
+
mock_schedule_handle.pause.side_effect = mock_pause
|
|
46
|
+
mock_schedule_handle.unpause.side_effect = mock_unpause
|
|
47
|
+
mock_schedule_handle.describe.side_effect = mock_describe
|
|
48
|
+
|
|
49
|
+
return client
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@pytest.fixture
|
|
53
|
+
def polling_manager(mock_temporal_client):
|
|
54
|
+
"""Provide a PollingManager with mocked client."""
|
|
55
|
+
return PollingManager(mock_temporal_client)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@pytest.fixture
|
|
59
|
+
def sample_config():
|
|
60
|
+
"""Provide a sample PollingConfig for testing."""
|
|
61
|
+
return PollingConfig(
|
|
62
|
+
endpoint_identifier="test-api",
|
|
63
|
+
polling_protocol=PollingProtocol.HTTP,
|
|
64
|
+
connection_params={"url": "https://api.example.com/data"},
|
|
65
|
+
timeout_seconds=30,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class TestPollingManagerInitialization:
|
|
70
|
+
"""Test PollingManager initialization."""
|
|
71
|
+
|
|
72
|
+
def test_init_with_client(self, mock_temporal_client):
|
|
73
|
+
"""Test initialization with temporal client."""
|
|
74
|
+
manager = PollingManager(mock_temporal_client)
|
|
75
|
+
assert manager._temporal_client is not None
|
|
76
|
+
assert manager._active_polls == {}
|
|
77
|
+
|
|
78
|
+
def test_init_without_client(self):
|
|
79
|
+
"""Test initialization without temporal client."""
|
|
80
|
+
manager = PollingManager()
|
|
81
|
+
assert manager._temporal_client is None
|
|
82
|
+
assert manager._active_polls == {}
|
|
83
|
+
|
|
84
|
+
def test_init_with_custom_task_queue(self, mock_temporal_client):
|
|
85
|
+
"""Test initialization with custom task queue."""
|
|
86
|
+
custom_queue = "custom-task-queue"
|
|
87
|
+
manager = PollingManager(mock_temporal_client, task_queue=custom_queue)
|
|
88
|
+
assert manager._temporal_client is not None
|
|
89
|
+
assert manager._task_queue == custom_queue
|
|
90
|
+
assert manager._active_polls == {}
|
|
91
|
+
|
|
92
|
+
def test_init_with_default_task_queue(self, mock_temporal_client):
|
|
93
|
+
"""Test initialization with default task queue."""
|
|
94
|
+
manager = PollingManager(mock_temporal_client)
|
|
95
|
+
assert manager._temporal_client is not None
|
|
96
|
+
assert manager._task_queue == "julee-polling-queue"
|
|
97
|
+
assert manager._active_polls == {}
|
|
98
|
+
|
|
99
|
+
@pytest.mark.asyncio
|
|
100
|
+
async def test_start_polling_with_custom_task_queue(
|
|
101
|
+
self, mock_temporal_client, sample_config
|
|
102
|
+
):
|
|
103
|
+
"""Test that custom task queue is used in schedule creation."""
|
|
104
|
+
custom_queue = "custom-polling-queue"
|
|
105
|
+
manager = PollingManager(mock_temporal_client, task_queue=custom_queue)
|
|
106
|
+
|
|
107
|
+
# Capture the schedule created
|
|
108
|
+
schedule_id = await manager.start_polling("test-endpoint", sample_config, 60)
|
|
109
|
+
|
|
110
|
+
# Verify create_schedule was called
|
|
111
|
+
mock_temporal_client.create_schedule.assert_called_once()
|
|
112
|
+
call_args = mock_temporal_client.create_schedule.call_args
|
|
113
|
+
|
|
114
|
+
# Verify the schedule uses the custom task queue
|
|
115
|
+
schedule_obj = call_args[1]["schedule"] # kwargs['schedule']
|
|
116
|
+
assert schedule_obj.action.task_queue == custom_queue
|
|
117
|
+
assert schedule_id == "poll-test-endpoint"
|
|
118
|
+
|
|
119
|
+
@pytest.mark.asyncio
|
|
120
|
+
async def test_update_existing_schedule(self, mock_temporal_client, sample_config):
|
|
121
|
+
"""Test updating existing schedule when one already exists."""
|
|
122
|
+
manager = PollingManager(mock_temporal_client)
|
|
123
|
+
|
|
124
|
+
# Mock create_schedule to raise ScheduleAlreadyRunningError
|
|
125
|
+
mock_temporal_client.create_schedule.side_effect = ScheduleAlreadyRunningError()
|
|
126
|
+
|
|
127
|
+
# Mock schedule handle for update
|
|
128
|
+
mock_schedule_handle = AsyncMock()
|
|
129
|
+
mock_temporal_client.get_schedule_handle.return_value = mock_schedule_handle
|
|
130
|
+
|
|
131
|
+
schedule_id = await manager.start_polling("test-endpoint", sample_config, 60)
|
|
132
|
+
|
|
133
|
+
assert schedule_id == "poll-test-endpoint"
|
|
134
|
+
assert "test-endpoint" in manager._active_polls
|
|
135
|
+
|
|
136
|
+
# Verify update was called on the existing schedule
|
|
137
|
+
mock_schedule_handle.update.assert_called_once()
|
|
138
|
+
# Verify create_schedule was called once (and failed)
|
|
139
|
+
mock_temporal_client.create_schedule.assert_called_once()
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class TestPollingManagerStartPolling:
|
|
143
|
+
"""Test PollingManager start_polling method."""
|
|
144
|
+
|
|
145
|
+
@pytest.mark.asyncio
|
|
146
|
+
async def test_start_polling_success(self, polling_manager, sample_config):
|
|
147
|
+
"""Test successful polling start creates schedule and tracks state."""
|
|
148
|
+
schedule_id = await polling_manager.start_polling(
|
|
149
|
+
"test-endpoint", sample_config, 60
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# Verify return value
|
|
153
|
+
assert schedule_id == "poll-test-endpoint"
|
|
154
|
+
|
|
155
|
+
# Verify internal tracking
|
|
156
|
+
assert "test-endpoint" in polling_manager._active_polls
|
|
157
|
+
poll_info = polling_manager._active_polls["test-endpoint"]
|
|
158
|
+
assert poll_info["schedule_id"] == "poll-test-endpoint"
|
|
159
|
+
assert poll_info["config"] == sample_config
|
|
160
|
+
assert poll_info["interval_seconds"] == 60
|
|
161
|
+
assert poll_info["downstream_pipeline"] is None
|
|
162
|
+
|
|
163
|
+
@pytest.mark.asyncio
|
|
164
|
+
async def test_start_polling_with_downstream_pipeline(
|
|
165
|
+
self, polling_manager, sample_config
|
|
166
|
+
):
|
|
167
|
+
"""Test polling start with downstream pipeline."""
|
|
168
|
+
schedule_id = await polling_manager.start_polling(
|
|
169
|
+
"test-endpoint", sample_config, 30, "custom-pipeline"
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
assert schedule_id == "poll-test-endpoint"
|
|
173
|
+
poll_info = polling_manager._active_polls["test-endpoint"]
|
|
174
|
+
assert poll_info["downstream_pipeline"] == "custom-pipeline"
|
|
175
|
+
|
|
176
|
+
@pytest.mark.asyncio
|
|
177
|
+
async def test_start_polling_duplicate_endpoint_raises_error(
|
|
178
|
+
self, polling_manager, sample_config
|
|
179
|
+
):
|
|
180
|
+
"""Test starting polling for duplicate endpoint raises ValueError."""
|
|
181
|
+
# Start polling first time
|
|
182
|
+
await polling_manager.start_polling("test-endpoint", sample_config, 60)
|
|
183
|
+
|
|
184
|
+
# Attempt to start again should raise error
|
|
185
|
+
with pytest.raises(
|
|
186
|
+
ValueError, match="Endpoint test-endpoint is already being polled"
|
|
187
|
+
):
|
|
188
|
+
await polling_manager.start_polling("test-endpoint", sample_config, 60)
|
|
189
|
+
|
|
190
|
+
@pytest.mark.asyncio
|
|
191
|
+
async def test_start_polling_no_client_raises_error(self, sample_config):
|
|
192
|
+
"""Test starting polling without client raises RuntimeError."""
|
|
193
|
+
manager = PollingManager() # No client
|
|
194
|
+
|
|
195
|
+
with pytest.raises(RuntimeError, match="Temporal client not available"):
|
|
196
|
+
await manager.start_polling("test-endpoint", sample_config, 60)
|
|
197
|
+
|
|
198
|
+
@pytest.mark.asyncio
|
|
199
|
+
async def test_start_polling_multiple_endpoints(
|
|
200
|
+
self, polling_manager, sample_config
|
|
201
|
+
):
|
|
202
|
+
"""Test starting polling for multiple endpoints."""
|
|
203
|
+
# Create different configs for different endpoints
|
|
204
|
+
config2 = PollingConfig(
|
|
205
|
+
endpoint_identifier="test-api-2",
|
|
206
|
+
polling_protocol=PollingProtocol.HTTP,
|
|
207
|
+
connection_params={"url": "https://api2.example.com/data"},
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
# Start polling for multiple endpoints
|
|
211
|
+
schedule_id1 = await polling_manager.start_polling(
|
|
212
|
+
"endpoint-1", sample_config, 60
|
|
213
|
+
)
|
|
214
|
+
schedule_id2 = await polling_manager.start_polling("endpoint-2", config2, 30)
|
|
215
|
+
|
|
216
|
+
# Verify both are tracked
|
|
217
|
+
assert schedule_id1 == "poll-endpoint-1"
|
|
218
|
+
assert schedule_id2 == "poll-endpoint-2"
|
|
219
|
+
assert "endpoint-1" in polling_manager._active_polls
|
|
220
|
+
assert "endpoint-2" in polling_manager._active_polls
|
|
221
|
+
assert len(polling_manager._active_polls) == 2
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
class TestPollingManagerStopPolling:
|
|
225
|
+
"""Test PollingManager stop_polling method."""
|
|
226
|
+
|
|
227
|
+
@pytest.mark.asyncio
|
|
228
|
+
async def test_stop_polling_success(self, polling_manager, sample_config):
|
|
229
|
+
"""Test successful polling stop removes schedule and tracking."""
|
|
230
|
+
# Start polling first
|
|
231
|
+
await polling_manager.start_polling("test-endpoint", sample_config, 60)
|
|
232
|
+
assert "test-endpoint" in polling_manager._active_polls
|
|
233
|
+
|
|
234
|
+
# Stop polling
|
|
235
|
+
result = await polling_manager.stop_polling("test-endpoint")
|
|
236
|
+
|
|
237
|
+
# Verify success and cleanup
|
|
238
|
+
assert result is True
|
|
239
|
+
assert "test-endpoint" not in polling_manager._active_polls
|
|
240
|
+
|
|
241
|
+
@pytest.mark.asyncio
|
|
242
|
+
async def test_stop_polling_nonexistent_endpoint(self, polling_manager):
|
|
243
|
+
"""Test stopping polling for non-existent endpoint returns False."""
|
|
244
|
+
result = await polling_manager.stop_polling("nonexistent-endpoint")
|
|
245
|
+
assert result is False
|
|
246
|
+
|
|
247
|
+
@pytest.mark.asyncio
|
|
248
|
+
async def test_stop_polling_no_client_raises_error(self, sample_config):
|
|
249
|
+
"""Test stopping polling without client raises RuntimeError."""
|
|
250
|
+
manager = PollingManager() # No client
|
|
251
|
+
# Manually add to tracking to test the error condition
|
|
252
|
+
manager._active_polls["test-endpoint"] = {
|
|
253
|
+
"schedule_id": "poll-test-endpoint",
|
|
254
|
+
"config": sample_config,
|
|
255
|
+
"interval_seconds": 60,
|
|
256
|
+
"downstream_pipeline": None,
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
with pytest.raises(RuntimeError, match="Temporal client not available"):
|
|
260
|
+
await manager.stop_polling("test-endpoint")
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
class TestPollingManagerListActivePolling:
|
|
264
|
+
"""Test PollingManager list_active_polling method."""
|
|
265
|
+
|
|
266
|
+
@pytest.mark.asyncio
|
|
267
|
+
async def test_list_active_polling_empty(self, polling_manager):
|
|
268
|
+
"""Test listing active polls when none exist."""
|
|
269
|
+
active_polls = await polling_manager.list_active_polling()
|
|
270
|
+
assert active_polls == []
|
|
271
|
+
|
|
272
|
+
@pytest.mark.asyncio
|
|
273
|
+
async def test_list_active_polling_with_data(self, polling_manager, sample_config):
|
|
274
|
+
"""Test listing active polls with existing data."""
|
|
275
|
+
# Start some polling operations
|
|
276
|
+
await polling_manager.start_polling("endpoint-1", sample_config, 60)
|
|
277
|
+
await polling_manager.start_polling(
|
|
278
|
+
"endpoint-2", sample_config, 30, "pipeline-2"
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
# List active polling
|
|
282
|
+
active_polls = await polling_manager.list_active_polling()
|
|
283
|
+
|
|
284
|
+
# Verify results
|
|
285
|
+
assert len(active_polls) == 2
|
|
286
|
+
|
|
287
|
+
# Find polls by endpoint_id (order not guaranteed)
|
|
288
|
+
endpoint1_poll = next(
|
|
289
|
+
p for p in active_polls if p["endpoint_id"] == "endpoint-1"
|
|
290
|
+
)
|
|
291
|
+
endpoint2_poll = next(
|
|
292
|
+
p for p in active_polls if p["endpoint_id"] == "endpoint-2"
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
# Verify endpoint-1 details
|
|
296
|
+
assert endpoint1_poll["schedule_id"] == "poll-endpoint-1"
|
|
297
|
+
assert endpoint1_poll["interval_seconds"] == 60
|
|
298
|
+
assert endpoint1_poll["endpoint_identifier"] == "test-api"
|
|
299
|
+
assert endpoint1_poll["polling_protocol"] == "http"
|
|
300
|
+
assert endpoint1_poll["downstream_pipeline"] is None
|
|
301
|
+
|
|
302
|
+
# Verify endpoint-2 details
|
|
303
|
+
assert endpoint2_poll["schedule_id"] == "poll-endpoint-2"
|
|
304
|
+
assert endpoint2_poll["interval_seconds"] == 30
|
|
305
|
+
assert endpoint2_poll["downstream_pipeline"] == "pipeline-2"
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
class TestPollingManagerGetPollingStatus:
|
|
309
|
+
"""Test PollingManager get_polling_status method."""
|
|
310
|
+
|
|
311
|
+
@pytest.mark.asyncio
|
|
312
|
+
async def test_get_polling_status_nonexistent_endpoint(self, polling_manager):
|
|
313
|
+
"""Test getting status for non-existent endpoint returns None."""
|
|
314
|
+
status = await polling_manager.get_polling_status("nonexistent-endpoint")
|
|
315
|
+
assert status is None
|
|
316
|
+
|
|
317
|
+
@pytest.mark.asyncio
|
|
318
|
+
async def test_get_polling_status_no_client_raises_error(self, sample_config):
|
|
319
|
+
"""Test getting status without client raises RuntimeError."""
|
|
320
|
+
manager = PollingManager() # No client
|
|
321
|
+
# Manually add to tracking to test the error condition
|
|
322
|
+
manager._active_polls["test-endpoint"] = {
|
|
323
|
+
"schedule_id": "poll-test-endpoint",
|
|
324
|
+
"config": sample_config,
|
|
325
|
+
"interval_seconds": 60,
|
|
326
|
+
"downstream_pipeline": None,
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
with pytest.raises(RuntimeError, match="Temporal client not available"):
|
|
330
|
+
await manager.get_polling_status("test-endpoint")
|
|
331
|
+
|
|
332
|
+
@pytest.mark.asyncio
|
|
333
|
+
async def test_get_polling_status_success(self, polling_manager, sample_config):
|
|
334
|
+
"""Test getting status for existing endpoint."""
|
|
335
|
+
# Start polling
|
|
336
|
+
await polling_manager.start_polling(
|
|
337
|
+
"test-endpoint", sample_config, 60, "test-pipeline"
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
# Get status
|
|
341
|
+
status = await polling_manager.get_polling_status("test-endpoint")
|
|
342
|
+
|
|
343
|
+
# Verify status details
|
|
344
|
+
assert status is not None
|
|
345
|
+
assert status["endpoint_id"] == "test-endpoint"
|
|
346
|
+
assert status["schedule_id"] == "poll-test-endpoint"
|
|
347
|
+
assert status["interval_seconds"] == 60
|
|
348
|
+
assert status["downstream_pipeline"] == "test-pipeline"
|
|
349
|
+
# Should not be paused initially
|
|
350
|
+
assert status["is_paused"] is False
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
class TestPollingManagerPauseResumePolling:
|
|
354
|
+
"""Test PollingManager pause_polling and resume_polling methods."""
|
|
355
|
+
|
|
356
|
+
@pytest.mark.asyncio
|
|
357
|
+
async def test_pause_polling_success(self, polling_manager, sample_config):
|
|
358
|
+
"""Test successful polling pause."""
|
|
359
|
+
# Start polling
|
|
360
|
+
await polling_manager.start_polling("test-endpoint", sample_config, 60)
|
|
361
|
+
|
|
362
|
+
# Pause polling
|
|
363
|
+
result = await polling_manager.pause_polling("test-endpoint")
|
|
364
|
+
assert result is True
|
|
365
|
+
|
|
366
|
+
@pytest.mark.asyncio
|
|
367
|
+
async def test_pause_polling_nonexistent_endpoint(self, polling_manager):
|
|
368
|
+
"""Test pausing non-existent endpoint returns False."""
|
|
369
|
+
result = await polling_manager.pause_polling("nonexistent-endpoint")
|
|
370
|
+
assert result is False
|
|
371
|
+
|
|
372
|
+
@pytest.mark.asyncio
|
|
373
|
+
async def test_pause_polling_no_client_raises_error(self, sample_config):
|
|
374
|
+
"""Test pausing polling without client raises RuntimeError."""
|
|
375
|
+
manager = PollingManager() # No client
|
|
376
|
+
# Manually add to tracking to test the error condition
|
|
377
|
+
manager._active_polls["test-endpoint"] = {
|
|
378
|
+
"schedule_id": "poll-test-endpoint",
|
|
379
|
+
"config": sample_config,
|
|
380
|
+
"interval_seconds": 60,
|
|
381
|
+
"downstream_pipeline": None,
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
with pytest.raises(RuntimeError, match="Temporal client not available"):
|
|
385
|
+
await manager.pause_polling("test-endpoint")
|
|
386
|
+
|
|
387
|
+
@pytest.mark.asyncio
|
|
388
|
+
async def test_resume_polling_success(self, polling_manager, sample_config):
|
|
389
|
+
"""Test successful polling resume."""
|
|
390
|
+
# Start and pause polling
|
|
391
|
+
await polling_manager.start_polling("test-endpoint", sample_config, 60)
|
|
392
|
+
await polling_manager.pause_polling("test-endpoint")
|
|
393
|
+
|
|
394
|
+
# Resume polling
|
|
395
|
+
result = await polling_manager.resume_polling("test-endpoint")
|
|
396
|
+
assert result is True
|
|
397
|
+
|
|
398
|
+
@pytest.mark.asyncio
|
|
399
|
+
async def test_resume_polling_nonexistent_endpoint(self, polling_manager):
|
|
400
|
+
"""Test resuming non-existent endpoint returns False."""
|
|
401
|
+
result = await polling_manager.resume_polling("nonexistent-endpoint")
|
|
402
|
+
assert result is False
|
|
403
|
+
|
|
404
|
+
@pytest.mark.asyncio
|
|
405
|
+
async def test_resume_polling_no_client_raises_error(self, sample_config):
|
|
406
|
+
"""Test resuming polling without client raises RuntimeError."""
|
|
407
|
+
manager = PollingManager() # No client
|
|
408
|
+
# Manually add to tracking to test the error condition
|
|
409
|
+
manager._active_polls["test-endpoint"] = {
|
|
410
|
+
"schedule_id": "poll-test-endpoint",
|
|
411
|
+
"config": sample_config,
|
|
412
|
+
"interval_seconds": 60,
|
|
413
|
+
"downstream_pipeline": None,
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
with pytest.raises(RuntimeError, match="Temporal client not available"):
|
|
417
|
+
await manager.resume_polling("test-endpoint")
|
|
418
|
+
|
|
419
|
+
@pytest.mark.asyncio
|
|
420
|
+
async def test_pause_resume_workflow(self, polling_manager, sample_config):
|
|
421
|
+
"""Test complete pause/resume workflow."""
|
|
422
|
+
# Start polling
|
|
423
|
+
await polling_manager.start_polling("test-endpoint", sample_config, 60)
|
|
424
|
+
|
|
425
|
+
# Pause polling
|
|
426
|
+
await polling_manager.pause_polling("test-endpoint")
|
|
427
|
+
|
|
428
|
+
# Verify paused status
|
|
429
|
+
status = await polling_manager.get_polling_status("test-endpoint")
|
|
430
|
+
assert status["is_paused"] is True
|
|
431
|
+
|
|
432
|
+
# Resume polling
|
|
433
|
+
await polling_manager.resume_polling("test-endpoint")
|
|
434
|
+
|
|
435
|
+
# Verify resumed status
|
|
436
|
+
status = await polling_manager.get_polling_status("test-endpoint")
|
|
437
|
+
assert status["is_paused"] is False
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
class TestPollingManagerIntegration:
|
|
441
|
+
"""Integration tests for PollingManager full workflows."""
|
|
442
|
+
|
|
443
|
+
@pytest.mark.asyncio
|
|
444
|
+
async def test_full_polling_lifecycle(self, polling_manager, sample_config):
|
|
445
|
+
"""Test complete polling lifecycle: start -> pause -> resume -> stop."""
|
|
446
|
+
endpoint_id = "lifecycle-test"
|
|
447
|
+
|
|
448
|
+
# Start polling
|
|
449
|
+
schedule_id = await polling_manager.start_polling(
|
|
450
|
+
endpoint_id, sample_config, 45
|
|
451
|
+
)
|
|
452
|
+
assert schedule_id == f"poll-{endpoint_id}"
|
|
453
|
+
|
|
454
|
+
# Verify it's in active polls
|
|
455
|
+
active_polls = await polling_manager.list_active_polling()
|
|
456
|
+
assert len(active_polls) == 1
|
|
457
|
+
assert active_polls[0]["endpoint_id"] == endpoint_id
|
|
458
|
+
|
|
459
|
+
# Pause polling
|
|
460
|
+
assert await polling_manager.pause_polling(endpoint_id) is True
|
|
461
|
+
status = await polling_manager.get_polling_status(endpoint_id)
|
|
462
|
+
assert status["is_paused"] is True
|
|
463
|
+
|
|
464
|
+
# Resume polling
|
|
465
|
+
assert await polling_manager.resume_polling(endpoint_id) is True
|
|
466
|
+
status = await polling_manager.get_polling_status(endpoint_id)
|
|
467
|
+
assert status["is_paused"] is False
|
|
468
|
+
|
|
469
|
+
# Stop polling
|
|
470
|
+
assert await polling_manager.stop_polling(endpoint_id) is True
|
|
471
|
+
|
|
472
|
+
# Verify cleanup
|
|
473
|
+
assert await polling_manager.get_polling_status(endpoint_id) is None
|
|
474
|
+
active_polls = await polling_manager.list_active_polling()
|
|
475
|
+
assert len(active_polls) == 0
|
|
@@ -37,35 +37,168 @@ Usage in conf.py::
|
|
|
37
37
|
|
|
38
38
|
from sphinx.util import logging
|
|
39
39
|
|
|
40
|
-
from .config import
|
|
40
|
+
from .config import init_config
|
|
41
41
|
|
|
42
42
|
logger = logging.getLogger(__name__)
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
def setup(app):
|
|
46
46
|
"""Set up all HCD extensions for Sphinx."""
|
|
47
|
+
from .sphinx.directives import (
|
|
48
|
+
AcceleratorDependencyDiagramDirective,
|
|
49
|
+
AcceleratorDependencyDiagramPlaceholder,
|
|
50
|
+
AcceleratorIndexDirective,
|
|
51
|
+
AcceleratorIndexPlaceholder,
|
|
52
|
+
AcceleratorsForAppDirective,
|
|
53
|
+
AcceleratorsForAppPlaceholder,
|
|
54
|
+
AcceleratorStatusDirective,
|
|
55
|
+
AppIndexDirective,
|
|
56
|
+
AppIndexPlaceholder,
|
|
57
|
+
AppsForPersonaDirective,
|
|
58
|
+
AppsForPersonaPlaceholder,
|
|
59
|
+
# Accelerator directives
|
|
60
|
+
DefineAcceleratorDirective,
|
|
61
|
+
DefineAcceleratorPlaceholder,
|
|
62
|
+
# App directives
|
|
63
|
+
DefineAppDirective,
|
|
64
|
+
DefineAppPlaceholder,
|
|
65
|
+
# Epic directives
|
|
66
|
+
DefineEpicDirective,
|
|
67
|
+
# Integration directives
|
|
68
|
+
DefineIntegrationDirective,
|
|
69
|
+
DefineIntegrationPlaceholder,
|
|
70
|
+
# Journey directives
|
|
71
|
+
DefineJourneyDirective,
|
|
72
|
+
DependentAcceleratorsDirective,
|
|
73
|
+
DependentAcceleratorsPlaceholder,
|
|
74
|
+
EpicIndexDirective,
|
|
75
|
+
EpicIndexPlaceholder,
|
|
76
|
+
EpicsForPersonaDirective,
|
|
77
|
+
EpicsForPersonaPlaceholder,
|
|
78
|
+
EpicStoryDirective,
|
|
79
|
+
GherkinAppStoriesDirective,
|
|
80
|
+
GherkinStoriesDirective,
|
|
81
|
+
GherkinStoriesForAppDirective,
|
|
82
|
+
GherkinStoriesForPersonaDirective,
|
|
83
|
+
GherkinStoriesIndexDirective,
|
|
84
|
+
# Story deprecated aliases
|
|
85
|
+
GherkinStoryDirective,
|
|
86
|
+
IntegrationIndexDirective,
|
|
87
|
+
IntegrationIndexPlaceholder,
|
|
88
|
+
JourneyDependencyGraphDirective,
|
|
89
|
+
JourneyDependencyGraphPlaceholder,
|
|
90
|
+
JourneyIndexDirective,
|
|
91
|
+
JourneysForPersonaDirective,
|
|
92
|
+
# Persona directives
|
|
93
|
+
PersonaDiagramDirective,
|
|
94
|
+
PersonaDiagramPlaceholder,
|
|
95
|
+
PersonaIndexDiagramDirective,
|
|
96
|
+
PersonaIndexDiagramPlaceholder,
|
|
97
|
+
StepEpicDirective,
|
|
98
|
+
StepPhaseDirective,
|
|
99
|
+
StepStoryDirective,
|
|
100
|
+
StoriesDirective,
|
|
101
|
+
# Story directives
|
|
102
|
+
StoryAppDirective,
|
|
103
|
+
StoryIndexDirective,
|
|
104
|
+
StoryListForAppDirective,
|
|
105
|
+
StoryListForPersonaDirective,
|
|
106
|
+
StoryRefDirective,
|
|
107
|
+
StorySeeAlsoPlaceholder,
|
|
108
|
+
)
|
|
109
|
+
from .sphinx.event_handlers import (
|
|
110
|
+
on_builder_inited,
|
|
111
|
+
on_doctree_read,
|
|
112
|
+
on_doctree_resolved,
|
|
113
|
+
on_env_purge_doc,
|
|
114
|
+
)
|
|
115
|
+
|
|
47
116
|
# Register configuration value first
|
|
48
117
|
app.add_config_value("sphinx_hcd", {}, "env")
|
|
49
118
|
|
|
50
119
|
# Initialize config when builder starts (after conf.py is loaded)
|
|
51
120
|
app.connect("builder-inited", _init_config_handler, priority=0)
|
|
52
121
|
|
|
53
|
-
#
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
122
|
+
# Connect event handlers
|
|
123
|
+
app.connect("builder-inited", on_builder_inited, priority=100)
|
|
124
|
+
app.connect("doctree-read", on_doctree_read)
|
|
125
|
+
app.connect("doctree-resolved", on_doctree_resolved)
|
|
126
|
+
app.connect("env-purge-doc", on_env_purge_doc)
|
|
127
|
+
|
|
128
|
+
# Register story directives
|
|
129
|
+
app.add_directive("story", StoryRefDirective)
|
|
130
|
+
app.add_directive("stories", StoriesDirective)
|
|
131
|
+
app.add_directive("story-list-for-persona", StoryListForPersonaDirective)
|
|
132
|
+
app.add_directive("story-list-for-app", StoryListForAppDirective)
|
|
133
|
+
app.add_directive("story-index", StoryIndexDirective)
|
|
134
|
+
app.add_directive("story-app", StoryAppDirective)
|
|
135
|
+
app.add_node(StorySeeAlsoPlaceholder)
|
|
136
|
+
|
|
137
|
+
# Register deprecated story aliases
|
|
138
|
+
app.add_directive("gherkin-story", GherkinStoryDirective)
|
|
139
|
+
app.add_directive("gherkin-stories", GherkinStoriesDirective)
|
|
140
|
+
app.add_directive("gherkin-stories-for-persona", GherkinStoriesForPersonaDirective)
|
|
141
|
+
app.add_directive("gherkin-stories-for-app", GherkinStoriesForAppDirective)
|
|
142
|
+
app.add_directive("gherkin-stories-index", GherkinStoriesIndexDirective)
|
|
143
|
+
app.add_directive("gherkin-app-stories", GherkinAppStoriesDirective)
|
|
144
|
+
|
|
145
|
+
# Register journey directives
|
|
146
|
+
app.add_directive("define-journey", DefineJourneyDirective)
|
|
147
|
+
app.add_directive("step-story", StepStoryDirective)
|
|
148
|
+
app.add_directive("step-epic", StepEpicDirective)
|
|
149
|
+
app.add_directive("step-phase", StepPhaseDirective)
|
|
150
|
+
app.add_directive("journey-index", JourneyIndexDirective)
|
|
151
|
+
app.add_directive("journey-dependency-graph", JourneyDependencyGraphDirective)
|
|
152
|
+
app.add_directive("journeys-for-persona", JourneysForPersonaDirective)
|
|
153
|
+
app.add_node(JourneyDependencyGraphPlaceholder)
|
|
154
|
+
|
|
155
|
+
# Register epic directives
|
|
156
|
+
app.add_directive("define-epic", DefineEpicDirective)
|
|
157
|
+
app.add_directive("epic-story", EpicStoryDirective)
|
|
158
|
+
app.add_directive("epic-index", EpicIndexDirective)
|
|
159
|
+
app.add_directive("epics-for-persona", EpicsForPersonaDirective)
|
|
160
|
+
app.add_node(EpicIndexPlaceholder)
|
|
161
|
+
app.add_node(EpicsForPersonaPlaceholder)
|
|
162
|
+
|
|
163
|
+
# Register app directives
|
|
164
|
+
app.add_directive("define-app", DefineAppDirective)
|
|
165
|
+
app.add_directive("app-index", AppIndexDirective)
|
|
166
|
+
app.add_directive("apps-for-persona", AppsForPersonaDirective)
|
|
167
|
+
app.add_node(DefineAppPlaceholder)
|
|
168
|
+
app.add_node(AppIndexPlaceholder)
|
|
169
|
+
app.add_node(AppsForPersonaPlaceholder)
|
|
170
|
+
|
|
171
|
+
# Register accelerator directives
|
|
172
|
+
app.add_directive("define-accelerator", DefineAcceleratorDirective)
|
|
173
|
+
app.add_directive("accelerator-index", AcceleratorIndexDirective)
|
|
174
|
+
app.add_directive("accelerators-for-app", AcceleratorsForAppDirective)
|
|
175
|
+
app.add_directive("dependent-accelerators", DependentAcceleratorsDirective)
|
|
176
|
+
app.add_directive(
|
|
177
|
+
"accelerator-dependency-diagram", AcceleratorDependencyDiagramDirective
|
|
178
|
+
)
|
|
179
|
+
app.add_directive("accelerator-status", AcceleratorStatusDirective)
|
|
180
|
+
app.add_node(DefineAcceleratorPlaceholder)
|
|
181
|
+
app.add_node(AcceleratorIndexPlaceholder)
|
|
182
|
+
app.add_node(AcceleratorsForAppPlaceholder)
|
|
183
|
+
app.add_node(DependentAcceleratorsPlaceholder)
|
|
184
|
+
app.add_node(AcceleratorDependencyDiagramPlaceholder)
|
|
185
|
+
|
|
186
|
+
# Register integration directives
|
|
187
|
+
app.add_directive("define-integration", DefineIntegrationDirective)
|
|
188
|
+
app.add_directive("integration-index", IntegrationIndexDirective)
|
|
189
|
+
app.add_node(DefineIntegrationPlaceholder)
|
|
190
|
+
app.add_node(IntegrationIndexPlaceholder)
|
|
191
|
+
|
|
192
|
+
# Register persona directives
|
|
193
|
+
app.add_directive("persona-diagram", PersonaDiagramDirective)
|
|
194
|
+
app.add_directive("persona-index-diagram", PersonaIndexDiagramDirective)
|
|
195
|
+
app.add_node(PersonaDiagramPlaceholder)
|
|
196
|
+
app.add_node(PersonaIndexDiagramPlaceholder)
|
|
64
197
|
|
|
65
198
|
logger.info("Loaded julee.docs.sphinx_hcd extensions")
|
|
66
199
|
|
|
67
200
|
return {
|
|
68
|
-
"version": "
|
|
201
|
+
"version": "2.0",
|
|
69
202
|
"parallel_read_safe": False,
|
|
70
203
|
"parallel_write_safe": True,
|
|
71
204
|
}
|