From d599b4aa62e6c4dc487785942628841426b4b3c4 Mon Sep 17 00:00:00 2001 From: Kasim Necdet Percinel Date: Wed, 10 Jun 2026 15:38:37 +0000 Subject: [PATCH 1/2] bring new format field to events endpoint tobe able to eventsapi simple event search will simplify our system , step to eventually remove tree format event responses, which brings unneccessary complication to our codes --- src/Event/Api/EventsApi.php | 25 +++++++++++++++++++++++++ src/Event/Api/EventsApiInterface.php | 11 +++++++++++ src/Module/SolarEvents.php | 20 ++++++++++++++++---- 3 files changed, 52 insertions(+), 4 deletions(-) diff --git a/src/Event/Api/EventsApi.php b/src/Event/Api/EventsApi.php index 78265246..0e7907c8 100644 --- a/src/Event/Api/EventsApi.php +++ b/src/Event/Api/EventsApi.php @@ -129,6 +129,31 @@ public function getEventsForSourceLegacy(DateTimeInterface $observationTime, str } } + /** {@inheritdoc} */ + public function getEventsForSource(DateTimeInterface $observationTime, string $source): array + { + $formattedTime = $observationTime->format('Y-m-d\TH:i:s\Z'); + $encodedTime = urlencode($formattedTime); + + $url = "/api/v1/events/{$source}/observation/{$encodedTime}"; + + $this->sentry->setContext('EventsApi', [ + 'endpoint' => $url, + ]); + + try { + $response = $this->client->request('GET', $url); + return $this->parseResponse($response); + } catch (\Throwable $e) { + $this->sentry->setContext('EventsApi', [ + 'error' => $e->getMessage(), + ]); + $exception = new EventsApiException("Failed to fetch events for source: " . $e->getMessage(), 0, $e); + $this->sentry->capture($exception); + throw $exception; + } + } + /** {@inheritdoc} */ public function getEventsInRange(int $fromTimestamp, int $toTimestamp, array $paths): array { diff --git a/src/Event/Api/EventsApiInterface.php b/src/Event/Api/EventsApiInterface.php index 3664caa8..b3c98f4c 100644 --- a/src/Event/Api/EventsApiInterface.php +++ b/src/Event/Api/EventsApiInterface.php @@ -16,6 +16,17 @@ interface EventsApiInterface { */ public function getEventsForSourceLegacy(DateTimeInterface $observationTime, string $source): array; + /** + * Get events for a specific source via the v1 endpoint + * (events/{source}/observation/{ISO-8601-Z time}). + * + * @param DateTimeInterface $observationTime The observation time + * @param string $source The data source (e.g. "HEK", "CCMC", "RHESSI") + * @return array Array of event data + * @throws EventsApiException on API errors or unexpected responses + */ + public function getEventsForSource(DateTimeInterface $observationTime, string $source): array; + /** * Get events within a time range for given selection paths * diff --git a/src/Module/SolarEvents.php b/src/Module/SolarEvents.php index e22dc7ec..91647da5 100644 --- a/src/Module/SolarEvents.php +++ b/src/Module/SolarEvents.php @@ -15,6 +15,7 @@ use Helioviewer\Api\Module\BaseModule; use Helioviewer\Api\Module\ModuleInterface; use Helioviewer\Api\Sentry\Sentry; +use Helioviewer\Api\Event\Api\EventsApi; use Helioviewer\Api\Event\Api\EventsApiException; class Module_SolarEvents extends BaseModule implements ModuleInterface { @@ -191,8 +192,16 @@ public function importEvents() { public function events() { $observationTime = new DateTimeImmutable($this->_params['startTime']); + // Output format selector: 'tree' (legacy nested categories, default) + // or 'flat' (new v1 per-source endpoint). Anything else falls back to + // 'tree' for backwards compatibility. + $format = $this->_options['format'] ?? 'tree'; + if ($format !== 'flat') { + $format = 'tree'; + } + // All event sources supported by the Events API - $allSources = ['CCMC', 'HEK', 'RHESSI']; + $allSources = EventsApi::VALID_SOURCES; // Determine which sources to query if (array_key_exists('sources', $this->_options)) { @@ -206,7 +215,9 @@ public function events() { foreach ($sources as $source) { try { - $sourceData = $this->eventsApi()->getEventsForSourceLegacy($observationTime, $source); + $sourceData = ($format === 'flat') + ? $this->eventsApi()->getEventsForSource($observationTime, $source) + : $this->eventsApi()->getEventsForSourceLegacy($observationTime, $source); $data = array_merge($data, $sourceData); } catch (EventsApiException $e) { return $this->_sendResponse(500, 'Internal Server Error', 'Failed to fetch events from ' . $source); @@ -268,10 +279,11 @@ public function getValidationRules(): array { $expected = array( 'required' => array('startTime'), 'optional' => array('eventType', 'cacheOnly', 'force', - 'ar_filter', 'sources'), + 'ar_filter', 'sources', 'format'), 'bools' => array('cacheOnly','force','ar_filter'), 'dates' => array('startTime'), - 'alphanumlist' => array('eventType', 'sources') + 'alphanumlist' => array('eventType', 'sources'), + 'choices' => array('format' => ['tree', 'flat']), ); break; default: From 7819653458d72b2fc652a9f072df28c80bf4a889 Mon Sep 17 00:00:00 2001 From: Kasim Necdet Percinel Date: Wed, 10 Jun 2026 20:26:20 +0000 Subject: [PATCH 2/2] fix config fragile test --- .../GetEventsForFramesWithSelectionsTest.php | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/tests/unit_tests/events/api/GetEventsForFramesWithSelectionsTest.php b/tests/unit_tests/events/api/GetEventsForFramesWithSelectionsTest.php index 53bc802d..64d3a962 100644 --- a/tests/unit_tests/events/api/GetEventsForFramesWithSelectionsTest.php +++ b/tests/unit_tests/events/api/GetEventsForFramesWithSelectionsTest.php @@ -59,9 +59,17 @@ public function testItShouldReturnEmptyForEmptyTimestamps(): void public function testItShouldFallBackToConfiguredChunkSizeWhenCallerPassesLessThanOne(): void { - // 60 timestamps with chunkSize=0 -> defined config or fallback 50 -> 2 chunks (50 + 10) + // Generate enough timestamps to force 2 chunks at whatever the + // configured fallback chunksize is. If HV_EVENTS_API_EVENTS_PER_FRAME_CHUNKSIZE + // is defined (via Config.ini) the fallback uses it; otherwise 50. + $fallbackChunk = defined('HV_EVENTS_API_EVENTS_PER_FRAME_CHUNKSIZE') + ? (int) HV_EVENTS_API_EVENTS_PER_FRAME_CHUNKSIZE + : 50; + $tail = 10; + $total = $fallbackChunk + $tail; + $timestamps = []; - for ($i = 0; $i < 60; $i++) { + for ($i = 0; $i < $total; $i++) { $timestamps[] = "2024-01-15 " . sprintf('%02d:%02d:00', intdiv($i, 60), $i % 60); } @@ -70,11 +78,11 @@ public function testItShouldFallBackToConfiguredChunkSizeWhenCallerPassesLessTha $this->mockClient->expects($this->exactly(2)) ->method('request') ->withConsecutive( - ['POST', '/helioviewer/events/frames_with_selections', $this->callback(function ($options) { - return count($options['json']['timestamps']) === 50; + ['POST', '/helioviewer/events/frames_with_selections', $this->callback(function ($options) use ($fallbackChunk) { + return count($options['json']['timestamps']) === $fallbackChunk; })], - ['POST', '/helioviewer/events/frames_with_selections', $this->callback(function ($options) { - return count($options['json']['timestamps']) === 10; + ['POST', '/helioviewer/events/frames_with_selections', $this->callback(function ($options) use ($tail) { + return count($options['json']['timestamps']) === $tail; })] ) ->willReturn(new Response(200, [], json_encode($emptyResponse)));