Files
anyway/frontend/lib/data/datasources/trip_remote_datasource.dart

143 lines
4.5 KiB
Dart

import 'dart:convert';
import 'dart:developer';
import 'package:dio/dio.dart';
abstract class TripRemoteDataSource {
/// Fetch available landmarks for the provided preferences/start payload.
Future<List<Map<String, dynamic>>> fetchLandmarks(Map<String, dynamic> body);
/// Create a new trip using the optimizer payload (that includes landmarks).
Future<Map<String, dynamic>> createTrip(Map<String, dynamic> body);
/// Fetch an existing trip by UUID
Future<Map<String, dynamic>> fetchTrip(String uuid);
}
class TripRemoteDataSourceImpl implements TripRemoteDataSource {
final Dio dio;
TripRemoteDataSourceImpl({required this.dio});
@override
Future<List<Map<String, dynamic>>> fetchLandmarks(Map<String, dynamic> body) async {
final response = await dio.post('/get/landmarks', data: body);
if (response.statusCode != 200) {
throw Exception('Server error fetching landmarks: ${response.statusCode}');
}
if (response.data is! List) {
throw Exception('Unexpected landmarks response format');
}
return _normalizeLandmarks(List<dynamic>.from(response.data as List));
}
@override
Future<Map<String, dynamic>> createTrip(Map<String, dynamic> body) async {
log('Creating trip with body: $body');
// convert body to actual json
String bodyJson = jsonEncode(body);
log('Trip request JSON: $bodyJson');
final response = await dio.post('/optimize/trip', data: body);
if (response.statusCode != 200) {
throw Exception('Server error: ${response.statusCode}');
}
if (response.data is! Map<String, dynamic>) {
throw Exception('Unexpected response format');
}
final Map<String, dynamic> json = Map<String, dynamic>.from(response.data as Map);
_ensureLandmarks(json);
if (json.containsKey('landmarks') && json['landmarks'] is List) {
return json;
}
final String? firstUuid = json['first_landmark_uuid'] as String?;
if (firstUuid == null) {
return json;
}
final List<Map<String, dynamic>> landmarks = [];
String? next = firstUuid;
while (next != null) {
final lm = await _fetchLandmarkByUuid(next);
landmarks.add(lm);
final dynamic nxt = lm['next_uuid'];
next = (nxt is String) ? nxt : null;
}
json['landmarks'] = landmarks;
return json;
}
@override
Future<Map<String, dynamic>> fetchTrip(String uuid) async {
final response = await dio.get('/trip/$uuid');
if (response.statusCode != 200) {
throw Exception('Server error: ${response.statusCode}');
}
if (response.data is! Map<String, dynamic>) {
throw Exception('Unexpected response format');
}
final Map<String, dynamic> json = Map<String, dynamic>.from(response.data as Map);
// Normalize same as createTrip: if trip contains first_landmark_uuid, expand chain
if (json.containsKey('landmarks') && json['landmarks'] is List) {
_ensureLandmarks(json);
return json;
}
final String? firstUuid = json['first_landmark_uuid'] as String?;
if (firstUuid == null) return json;
final List<Map<String, dynamic>> landmarks = [];
String? next = firstUuid;
while (next != null) {
final lm = await _fetchLandmarkByUuid(next);
landmarks.add(lm);
final dynamic nxt = lm['next_uuid'];
next = (nxt is String) ? nxt : null;
}
json['landmarks'] = landmarks;
return json;
}
// Fetch a single landmark by uuid via the /landmark/{landmark_uuid} endpoint
Future<Map<String, dynamic>> _fetchLandmarkByUuid(String uuid) async {
final response = await dio.get('/landmark/$uuid');
if (response.statusCode != 200) {
throw Exception('Failed to fetch landmark $uuid: ${response.statusCode}');
}
if (response.data is! Map<String, dynamic>) {
throw Exception('Unexpected landmark response format');
}
return _normalizeLandmark(Map<String, dynamic>.from(response.data as Map));
}
static void _ensureLandmarks(Map<String, dynamic> json) {
final raw = json['landmarks'];
if (raw is List) {
json['landmarks'] = _normalizeLandmarks(raw);
}
}
static List<Map<String, dynamic>> _normalizeLandmarks(List<dynamic> rawList) {
return rawList
.map((item) => _normalizeLandmark(Map<String, dynamic>.from(item as Map)))
.toList();
}
static Map<String, dynamic> _normalizeLandmark(Map<String, dynamic> source) {
final normalized = Map<String, dynamic>.from(source);
final typeValue = normalized['type'];
if (typeValue is String) {
normalized['type'] = {'type': typeValue};
}
return normalized;
}
}