import 'dart:convert'; import 'package:http/http.dart' as http; import 'models.dart'; class APIError implements Exception { final int statusCode; final String message; APIError(this.statusCode, this.message); @override String toString() => 'APIError $statusCode: $message'; } class APIClient { final String baseUrl; APIClient({this.baseUrl = 'http://localhost:8000'}); String _url(String path) => '$baseUrl$path'; static dynamic _decodeBody(http.Response response) { return jsonDecode(response.body); } static Map? _asMap(dynamic d) { if (d is Map) return d; if (d is Map) return d.cast(); return null; } static void _checkError(http.Response response) { if (response.statusCode >= 400) { final body = _decodeBody(response); final map = _asMap(body) ?? {}; throw APIError( response.statusCode, map['error'] as String? ?? 'Request failed', ); } } Future> listPipelines() async { final res = await http.get(Uri.parse(_url('/api/pipelines'))); _checkError(res); final data = _decodeBody(res); final arr = data is List ? data : []; final result = []; for (final e in arr) { final m = _asMap(e); if (m != null) result.add(Pipeline.fromJson(m)); } return result; } Future getPipeline(String id) async { final res = await http.get(Uri.parse(_url('/api/pipelines/$id'))); _checkError(res); final data = _decodeBody(res); final map = _asMap(data) ?? {}; final inner = _asMap(map['data']) ?? map; return Pipeline.fromJson(inner); } Future createPipeline(Pipeline pipeline) async { final res = await http.post( Uri.parse(_url('/api/pipelines')), headers: {'Content-Type': 'application/json'}, body: jsonEncode(pipeline.toJson()), ); _checkError(res); final data = _decodeBody(res); final map = _asMap(data) ?? {}; final inner = _asMap(map['data']) ?? map; return Pipeline.fromJson(inner); } Future deletePipeline(String id) async { final res = await http.delete(Uri.parse(_url('/api/pipelines/$id'))); _checkError(res); } Future> startRun(String pipelineId) async { final res = await http.post( Uri.parse(_url('/api/runs')), headers: {'Content-Type': 'application/json'}, body: jsonEncode({'pipeline_id': pipelineId}), ); _checkError(res); final data = _decodeBody(res); final map = _asMap(data) ?? {}; return _asMap(map['data']) ?? map; } Future getRunStatus(String runId) async { final res = await http.get(Uri.parse(_url('/api/runs/$runId/status'))); _checkError(res); final data = _decodeBody(res); final map = _asMap(data) ?? {}; final inner = _asMap(map['data']) ?? map; return RunState.fromJson(inner); } Future getRunLog(String runId) async { final res = await http.get(Uri.parse(_url('/api/runs/$runId/log'))); _checkError(res); return res.body; } }