Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,33 @@ Returns a list of HEK events in the :ref:`helioviewer-event-format`.
+===========+==========+========+======================+============================================================+
| startTime | Required | string | 2023-01-01T00:00:00Z | Specific time to get predictions for. |
+-----------+----------+--------+----------------------+------------------------------------------------------------+
| sources | Optional | string | HEK,DONKI | | Specify the external data sources to use for the request |
| sources | Optional | string | HEK,CCMC | | Specify the external data sources to use for the request |
| | | | | | If not provided, all sources will be queried |
| | | | | | Current options are HEK and DONKI. |
| | | | | | Allowed values: HEK, CCMC, RHESSI. |
+-----------+----------+--------+----------------------+------------------------------------------------------------+
| format | Optional | string | flat | | Output shape. ``tree`` (default) returns the legacy |
| | | | | | nested category/group structure described in |
| | | | | | :ref:`helioviewer-event-format`. ``flat`` returns the |
| | | | | | new v1 per-source response (one object per event with |
| | | | | | no category nesting). Allowed values: ``tree``, |
| | | | | | ``flat``. |
+-----------+----------+--------+----------------------+------------------------------------------------------------+

See :ref:`helioviewer-event-format` for the response format.
See :ref:`helioviewer-event-format` for the response format when ``format=tree``
(the default). When ``format=flat`` is requested, the response is the raw v1
events payload for each source, concatenated -- no nesting, no group keys.

Event specific data conforms to the `HEK Event Specification <https://www.lmsal.com/hek/VOEvent_Spec.html>`_

Example: Get HEK Events for 2023-03-30
Example: Get HEK Events for 2023-03-30 (tree, default)

.. code-block::
:caption: Example Query
:caption: Example Query (format=tree)

https://api.helioviewer.org/v2/events/?date=2023-03-30T00:00:00Z
https://api.helioviewer.org/v2/events/?startTime=2023-03-30T00:00:00Z&sources=HEK

.. code-block::
:caption: Example Response
:caption: Example Response (format=tree)

[
{
Expand Down Expand Up @@ -63,4 +72,38 @@ Example: Get HEK Events for 2023-03-30
]
},
...
]

Example: Same request but with the new flat shape

.. code-block::
:caption: Example Query (format=flat)

https://api.helioviewer.org/v2/events/?startTime=2023-03-30T00:00:00Z&sources=HEK&format=flat

.. code-block::
:caption: Example Response (format=flat)

[
{
"absnetcurrenthelicity": null,
"active": "true",
"area_atdiskcenter": 213057280,
"area_atdiskcenteruncert": null,
"area_raw": null,
"area_uncert": null,
"area_unit": "km2",
"ar_axislength": null,
"ar_compactnesscls": "",
"ar_lengthunit": "",
"ar_mcintoshcls": "HAX",
"ar_mtwilsoncls": "ALPHA",
"ar_neutrallength": null,
"concept": "Active Region",
"frm_name": "NOAA SWPC Observer",
"frm_institute": "NOAA-SWPC",
"pin": "AR",
...
},
...
]
25 changes: 25 additions & 0 deletions src/Event/Api/EventsApi.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
11 changes: 11 additions & 0 deletions src/Event/Api/EventsApiInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -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
*
Expand Down
20 changes: 16 additions & 4 deletions src/Module/SolarEvents.php
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)) {
Expand All @@ -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);
Expand Down Expand Up @@ -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:
Expand Down
5 changes: 3 additions & 2 deletions src/Validation/InputValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -506,8 +506,9 @@ public static function checkLegacyEventString($required, &$params)
if (isset($params[$req])) {
$value = $params[$req];

// Empty string is valid (no events selected)
if ($value === '') {
// Empty string OR empty brackets is valid (no events selected).
// Frontends that wrap selections in [...] send '[]' to mean none.
if ($value === '' || $value === '[]') {
continue;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand All @@ -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)));
Expand Down
6 changes: 3 additions & 3 deletions tests/unit_tests/validation/LegacyEventStringTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -305,10 +305,10 @@ public static function legacyEventStringProvider(): array
false,
'Nested brackets'
],
'invalid_only_brackets' => [
'valid_empty_brackets' => [
'[]',
false,
'Empty brackets'
true,
'Empty brackets are accepted as "no events selected" (frontends wrap selections in [...] and send [] for none)'
],
];
}
Expand Down
Loading