feat(audit): full-history JSONL export in the actions menu
Some checks failed
Security / Security check (push) Failing after 1s

The menu so far only exported the current view (type + search
filter over the page 100-event window). A second action now fetches
the complete event history of the active project scope in one
EventLog call (the RPC has no cursor and no server-side cap) and
writes it as JSONL. Serialisation extracted to a top-level function
with unit tests; a stale comment advertising the never-shipped
"chain audit export" command now names "chain admin events --json".
Studio 0.73.0.

Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
flemming-it 2026-07-18 01:49:20 +02:00
parent ae8fdcc762
commit b47d8c4646
10 changed files with 180 additions and 23 deletions

View file

@ -1,5 +1,8 @@
// Free-text audit search the match function behind the audit
// page's search field and JSONL export ("current view" semantics).
// Free-text audit search and JSONL serialisation the functions
// behind the audit page's search field and both export actions
// ("current view" and full history).
import 'dart:convert';
import 'package:flutter_test/flutter_test.dart';
@ -46,4 +49,47 @@ void main() {
expect(matchesAuditQuery(_event(), 'nichtvorhanden'), isFalse);
expect(matchesAuditQuery(_event(flow: null, error: null), 'flow'), isFalse);
});
test('JSONL export: one line per event, order kept, trailing newline', () {
final out = auditEventsToJsonl([
_event(flow: 'first'),
_event(flow: 'second'),
]);
expect(out.endsWith('\n'), isTrue);
final lines = out.trimRight().split('\n');
expect(lines, hasLength(2));
expect(jsonDecode(lines[0])['flow'], 'first');
expect(jsonDecode(lines[1])['flow'], 'second');
});
test('JSONL export: absent optional fields are omitted, not null', () {
final line =
jsonDecode(auditEventsToJsonl([_event()]).trimRight()) as Map;
expect(line['event_id'], 'evt-1');
expect(line['type'], 'step.completed');
expect(line['timestamp'], '2026-07-18T00:00:00.000Z');
expect(line.containsKey('flow'), isFalse);
expect(line.containsKey('error'), isFalse);
expect(line.containsKey('project'), isFalse);
expect(line.containsKey('duration_ms'), isFalse);
});
test('JSONL export: set fields appear under their wire names', () {
final line = jsonDecode(auditEventsToJsonl([
_event(
flow: 'rechnungslauf',
step: 'extract',
module: 'debug.echo',
error: 'timeout',
detail: '{"n":1}',
project: 'stromnetz',
),
]).trimRight()) as Map;
expect(line['flow'], 'rechnungslauf');
expect(line['step'], 'extract');
expect(line['module'], 'debug.echo');
expect(line['error'], 'timeout');
expect(line['detail'], '{"n":1}');
expect(line['project'], 'stromnetz');
});
}