iop/apps/client/lib/control_plane_status_client.dart
toki b77f44a706 feat: control plane status client and UI components
- Add ControlPlaneStatusClient for querying control plane state
- Add control plane status widgets for displaying status in client
- Update main.dart to integrate control plane status features
- Update widget_test.dart
- Archive completed task documents
2026-06-02 19:56:42 +09:00

220 lines
6.8 KiB
Dart

import 'dart:convert';
import 'package:http/http.dart' as http;
class EdgeRegistryView {
final String edgeId;
final String edgeName;
final String version;
final List<String> capabilities;
final String protocol;
final bool connected;
final DateTime lastSeen;
EdgeRegistryView({
required this.edgeId,
required this.edgeName,
required this.version,
required this.capabilities,
required this.protocol,
required this.connected,
required this.lastSeen,
});
factory EdgeRegistryView.fromJson(Map<String, dynamic> json) {
return EdgeRegistryView(
edgeId: json['edge_id'] as String? ?? '',
edgeName: json['edge_name'] as String? ?? '',
version: json['version'] as String? ?? '',
capabilities: List<String>.from(json['capabilities'] ?? []),
protocol: json['protocol'] as String? ?? '',
connected: json['connected'] as bool? ?? false,
lastSeen: json['last_seen'] != null
? DateTime.parse(json['last_seen'] as String)
: DateTime.now(),
);
}
}
class AdapterSummaryView {
final String type;
final bool enabled;
AdapterSummaryView({required this.type, required this.enabled});
factory AdapterSummaryView.fromJson(Map<String, dynamic> json) {
return AdapterSummaryView(
type: json['type'] as String? ?? '',
enabled: json['enabled'] as bool? ?? false,
);
}
}
class NodeConfigSummaryView {
final List<AdapterSummaryView> adapters;
final int concurrency;
NodeConfigSummaryView({required this.adapters, required this.concurrency});
factory NodeConfigSummaryView.fromJson(Map<String, dynamic> json) {
return NodeConfigSummaryView(
adapters: (json['adapters'] as List? ?? [])
.map((e) => AdapterSummaryView.fromJson(e as Map<String, dynamic>))
.toList(),
concurrency: json['concurrency'] as int? ?? 0,
);
}
}
class EdgeNodeSnapshotView {
final String nodeId;
final String alias;
final String label;
final bool connected;
final NodeConfigSummaryView? config;
EdgeNodeSnapshotView({
required this.nodeId,
required this.alias,
required this.label,
required this.connected,
this.config,
});
factory EdgeNodeSnapshotView.fromJson(Map<String, dynamic> json) {
return EdgeNodeSnapshotView(
nodeId: json['node_id'] as String? ?? '',
alias: json['alias'] as String? ?? '',
label: json['label'] as String? ?? '',
connected: json['connected'] as bool? ?? false,
config: json['config'] != null
? NodeConfigSummaryView.fromJson(json['config'] as Map<String, dynamic>)
: null,
);
}
}
class EdgeStatusResponseView {
final String requestId;
final String edgeId;
final String edgeName;
final int observedTimeUnixNano;
final List<EdgeNodeSnapshotView> nodes;
final Map<String, String> metadata;
final String error;
EdgeStatusResponseView({
required this.requestId,
required this.edgeId,
required this.edgeName,
required this.observedTimeUnixNano,
required this.nodes,
required this.metadata,
required this.error,
});
factory EdgeStatusResponseView.fromJson(Map<String, dynamic> json) {
return EdgeStatusResponseView(
requestId: json['request_id'] as String? ?? '',
edgeId: json['edge_id'] as String? ?? '',
edgeName: json['edge_name'] as String? ?? '',
observedTimeUnixNano: json['observed_time_unix_nano'] as int? ?? 0,
nodes: (json['nodes'] as List? ?? [])
.map((e) => EdgeNodeSnapshotView.fromJson(e as Map<String, dynamic>))
.toList(),
metadata: Map<String, String>.from(json['metadata'] ?? {}),
error: json['error'] as String? ?? '',
);
}
}
class EdgeNodeEventView {
final String edgeId;
final String eventId;
final String type;
final String source;
final String nodeId;
final String alias;
final String reason;
final DateTime timestamp;
final DateTime receivedAt;
final Map<String, String> metadata;
EdgeNodeEventView({
required this.edgeId,
required this.eventId,
required this.type,
required this.source,
required this.nodeId,
required this.alias,
required this.reason,
required this.timestamp,
required this.receivedAt,
required this.metadata,
});
factory EdgeNodeEventView.fromJson(Map<String, dynamic> json) {
return EdgeNodeEventView(
edgeId: json['edge_id'] as String? ?? '',
eventId: json['event_id'] as String? ?? '',
type: json['type'] as String? ?? '',
source: json['source'] as String? ?? '',
nodeId: json['node_id'] as String? ?? '',
alias: json['alias'] as String? ?? '',
reason: json['reason'] as String? ?? '',
timestamp: json['timestamp'] != null
? DateTime.parse(json['timestamp'] as String)
: DateTime.now(),
receivedAt: json['received_at'] != null
? DateTime.parse(json['received_at'] as String)
: DateTime.now(),
metadata: Map<String, String>.from(json['metadata'] ?? {}),
);
}
}
abstract class ControlPlaneStatusRepository {
Future<List<EdgeRegistryView>> fetchEdges();
Future<EdgeStatusResponseView> fetchEdgeStatus(String edgeId);
Future<List<EdgeNodeEventView>> fetchEdgeEvents(String edgeId);
}
class HttpControlPlaneStatusRepository implements ControlPlaneStatusRepository {
final String baseUrl;
final http.Client? client;
HttpControlPlaneStatusRepository({required this.baseUrl, this.client});
http.Client get _httpClient => client ?? http.Client();
@override
Future<List<EdgeRegistryView>> fetchEdges() async {
final response = await _httpClient.get(Uri.parse('$baseUrl/edges'));
if (response.statusCode != 200) {
throw Exception('Failed to fetch edges: ${response.statusCode}');
}
final body = json.decode(response.body) as Map<String, dynamic>;
final list = body['edges'] as List? ?? [];
return list.map((e) => EdgeRegistryView.fromJson(e as Map<String, dynamic>)).toList();
}
@override
Future<EdgeStatusResponseView> fetchEdgeStatus(String edgeId) async {
final response = await _httpClient.get(Uri.parse('$baseUrl/edges/$edgeId/status'));
if (response.statusCode != 200) {
throw Exception('Failed to fetch status for $edgeId: ${response.statusCode}');
}
final body = json.decode(response.body) as Map<String, dynamic>;
return EdgeStatusResponseView.fromJson(body);
}
@override
Future<List<EdgeNodeEventView>> fetchEdgeEvents(String edgeId) async {
final response = await _httpClient.get(Uri.parse('$baseUrl/edges/$edgeId/events'));
if (response.statusCode != 200) {
throw Exception('Failed to fetch events for $edgeId: ${response.statusCode}');
}
final body = json.decode(response.body) as Map<String, dynamic>;
final list = body['events'] as List? ?? [];
return list.map((e) => EdgeNodeEventView.fromJson(e as Map<String, dynamic>)).toList();
}
}