You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
215 lines
5.8 KiB
Dart
215 lines
5.8 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:http/http.dart' as http;
|
|
|
|
/// Response model for paginated results from PayloadCMS
|
|
class PayloadPaginatedResponse<T> {
|
|
final List<T> docs;
|
|
final int totalDocs;
|
|
final int limit;
|
|
final int page;
|
|
final int totalPages;
|
|
final bool hasNextPage;
|
|
final bool hasPrevPage;
|
|
final int? nextPage;
|
|
final int? prevPage;
|
|
|
|
PayloadPaginatedResponse({
|
|
required this.docs,
|
|
required this.totalDocs,
|
|
required this.limit,
|
|
required this.page,
|
|
required this.totalPages,
|
|
required this.hasNextPage,
|
|
required this.hasPrevPage,
|
|
this.nextPage,
|
|
this.prevPage,
|
|
});
|
|
|
|
factory PayloadPaginatedResponse.fromJson(
|
|
Map<String, dynamic> json,
|
|
T Function(Map<String, dynamic>) fromJsonT,
|
|
) {
|
|
return PayloadPaginatedResponse<T>(
|
|
docs: (json['docs'] as List<dynamic>)
|
|
.map((e) => fromJsonT(e as Map<String, dynamic>))
|
|
.toList(),
|
|
totalDocs: json['totalDocs'] as int,
|
|
limit: json['limit'] as int,
|
|
page: json['page'] as int,
|
|
totalPages: json['totalPages'] as int,
|
|
hasNextPage: json['hasNextPage'] as bool,
|
|
hasPrevPage: json['hasPrevPage'] as bool,
|
|
nextPage: json['nextPage'] as int?,
|
|
prevPage: json['prevPage'] as int?,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Generic PayloadCMS client for CRUD operations
|
|
class PayloadClient {
|
|
final String baseUrl;
|
|
final String accessToken;
|
|
final String basePath = "/cms/api";
|
|
|
|
PayloadClient({
|
|
required this.baseUrl,
|
|
required this.accessToken,
|
|
});
|
|
|
|
Map<String, String> get _headers {
|
|
final headers = {
|
|
'Content-Type': 'application/json',
|
|
'Accept': 'application/json',
|
|
};
|
|
headers['Authorization'] = 'Bearer $accessToken';
|
|
return headers;
|
|
}
|
|
|
|
/// Generic GET request
|
|
Future<http.Response> get(String endpoint) async {
|
|
final url = Uri.parse('$baseUrl$endpoint');
|
|
final response = await http.get(url, headers: _headers);
|
|
return response;
|
|
}
|
|
|
|
/// Generic POST request
|
|
Future<http.Response> post(String endpoint, Map<String, dynamic> body) async {
|
|
final url = Uri.parse('$baseUrl$endpoint');
|
|
final response = await http.post(
|
|
url,
|
|
headers: _headers,
|
|
body: jsonEncode(body),
|
|
);
|
|
return response;
|
|
}
|
|
|
|
/// Generic PATCH request
|
|
Future<http.Response> patch(
|
|
String endpoint,
|
|
Map<String, dynamic> body,
|
|
) async {
|
|
final url = Uri.parse('$baseUrl$endpoint');
|
|
final response = await http.patch(
|
|
url,
|
|
headers: _headers,
|
|
body: jsonEncode(body),
|
|
);
|
|
return response;
|
|
}
|
|
|
|
/// Generic DELETE request
|
|
Future<http.Response> delete(String endpoint) async {
|
|
final url = Uri.parse('$baseUrl$endpoint');
|
|
final response = await http.delete(url, headers: _headers);
|
|
return response;
|
|
}
|
|
|
|
/// Find documents with pagination
|
|
Future<PayloadPaginatedResponse<T>> find<T>(
|
|
String collection,
|
|
T Function(Map<String, dynamic>) fromJson, {
|
|
int? page,
|
|
int? limit,
|
|
Map<String, dynamic>? where,
|
|
String? sort,
|
|
}) async {
|
|
final Map<String, dynamic> queryParams = {};
|
|
|
|
if (page != null) queryParams['page'] = page.toString();
|
|
if (limit != null) queryParams['limit'] = limit.toString();
|
|
if (where != null && where.isNotEmpty) queryParams['where'] = where;
|
|
if (sort != null) queryParams['sort'] = sort;
|
|
|
|
final endpoint =
|
|
'$basePath/$collection${queryParams.isNotEmpty ? '?${queryStringify(queryParams)}' : ''}';
|
|
final response = await get(endpoint);
|
|
final json = jsonDecode(response.body) as Map<String, dynamic>;
|
|
|
|
return PayloadPaginatedResponse.fromJson(json, fromJson);
|
|
}
|
|
|
|
/// Find a single document by ID
|
|
Future<T> findById<T>(
|
|
String collection,
|
|
String id,
|
|
T Function(Map<String, dynamic>) fromJson,
|
|
) async {
|
|
final endpoint = '$basePath/$collection/$id';
|
|
final response = await get(endpoint);
|
|
final json = jsonDecode(response.body) as Map<String, dynamic>;
|
|
return fromJson(json);
|
|
}
|
|
|
|
/// Create a new document
|
|
Future<T> createDocument<T>(
|
|
String collection,
|
|
Map<String, dynamic> data,
|
|
T Function(Map<String, dynamic>) fromJson,
|
|
) async {
|
|
final endpoint = '$basePath/$collection';
|
|
final response = await post(endpoint, data);
|
|
final json = jsonDecode(response.body) as Map<String, dynamic>;
|
|
return fromJson(json);
|
|
}
|
|
|
|
/// Update an existing document
|
|
Future<T> updateDocument<T>(
|
|
String collection,
|
|
String id,
|
|
Map<String, dynamic> data,
|
|
T Function(Map<String, dynamic>) fromJson,
|
|
) async {
|
|
final endpoint = '$basePath/$collection/$id';
|
|
final response = await patch(endpoint, data);
|
|
final json = jsonDecode(response.body) as Map<String, dynamic>;
|
|
return fromJson(json);
|
|
}
|
|
|
|
/// Delete a document
|
|
Future<T> deleteDocument<T>(
|
|
String collection,
|
|
String id,
|
|
T Function(Map<String, dynamic>) fromJson,
|
|
) async {
|
|
final endpoint = '$basePath/$collection/$id';
|
|
final response = await delete(endpoint);
|
|
final json = jsonDecode(response.body) as Map<String, dynamic>;
|
|
return fromJson(json);
|
|
}
|
|
|
|
static String queryStringify(
|
|
Map<String, dynamic> params, {
|
|
bool encode = true,
|
|
}) {
|
|
final List<String> parts = [];
|
|
|
|
void build(String prefix, dynamic value) {
|
|
if (value == null) return;
|
|
|
|
if (value is Map) {
|
|
value.forEach((k, v) {
|
|
build('$prefix[$k]', v);
|
|
});
|
|
} else if (value is List) {
|
|
for (var i = 0; i < value.length; i++) {
|
|
build('$prefix[$i]', value[i]);
|
|
}
|
|
} else {
|
|
final String encodedKey =
|
|
encode ? Uri.encodeQueryComponent(prefix) : prefix;
|
|
final String encodedVal = encode
|
|
? Uri.encodeQueryComponent(value.toString())
|
|
: value.toString();
|
|
parts.add('$encodedKey=$encodedVal');
|
|
}
|
|
}
|
|
|
|
params.forEach((key, value) {
|
|
build(key, value);
|
|
});
|
|
|
|
return parts.join('&');
|
|
}
|
|
}
|