mirror of
https://github.com/foss42/apidash.git
synced 2025-06-03 16:27:06 +08:00
Adding http services along with provider + model modifications
This commit is contained in:
@ -1,4 +1,8 @@
|
||||
import 'dart:io';
|
||||
import 'dart:convert';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart';
|
||||
import 'kvrow_model.dart';
|
||||
import '../consts.dart';
|
||||
|
||||
@ -13,6 +17,9 @@ class RequestModel {
|
||||
this.requestParams,
|
||||
this.requestBodyContentType = DEFAULT_BODY_CONTENT_TYPE,
|
||||
this.requestBody,
|
||||
this.responseStatus,
|
||||
this.message,
|
||||
this.responseModel,
|
||||
});
|
||||
|
||||
final String id;
|
||||
@ -23,6 +30,9 @@ class RequestModel {
|
||||
final List<KVRow>? requestParams;
|
||||
final ContentType requestBodyContentType;
|
||||
final dynamic requestBody;
|
||||
final int? responseStatus;
|
||||
final String? message;
|
||||
final ResponseModel? responseModel;
|
||||
|
||||
RequestModel duplicate({
|
||||
required String id,
|
||||
@ -47,6 +57,9 @@ class RequestModel {
|
||||
List<KVRow>? requestParams,
|
||||
ContentType? requestBodyContentType,
|
||||
dynamic requestBody,
|
||||
int? responseStatus,
|
||||
String? message,
|
||||
ResponseModel? responseModel,
|
||||
}) {
|
||||
return RequestModel(
|
||||
id: id ?? this.id,
|
||||
@ -58,6 +71,9 @@ class RequestModel {
|
||||
requestBodyContentType:
|
||||
requestBodyContentType ?? this.requestBodyContentType,
|
||||
requestBody: requestBody ?? this.requestBody,
|
||||
responseStatus: responseStatus ?? this.responseStatus,
|
||||
message: message ?? this.message,
|
||||
responseModel: responseModel ?? this.responseModel,
|
||||
);
|
||||
}
|
||||
|
||||
@ -72,6 +88,59 @@ class RequestModel {
|
||||
"Request Params: ${requestParams.toString()}",
|
||||
"Request Body Content Type: ${requestBodyContentType.toString()}",
|
||||
"Request Body: ${requestBody.toString()}",
|
||||
"Response Status: $responseStatus",
|
||||
"Response Message: $message",
|
||||
"Response: ${responseModel.toString()}"
|
||||
].join("\n");
|
||||
}
|
||||
}
|
||||
|
||||
@immutable
|
||||
class ResponseModel {
|
||||
const ResponseModel({
|
||||
this.statusCode,
|
||||
this.headers,
|
||||
this.requestHeaders,
|
||||
this.contentType,
|
||||
this.body,
|
||||
this.time,
|
||||
});
|
||||
|
||||
final int? statusCode;
|
||||
final Map<String, String>? headers;
|
||||
final Map<String, String>? requestHeaders;
|
||||
final String? contentType;
|
||||
final String? body;
|
||||
final Duration? time;
|
||||
|
||||
ResponseModel fromResponse({
|
||||
required Response response,
|
||||
Duration? time,
|
||||
}) {
|
||||
var contentType = response.headers[HttpHeaders.contentTypeHeader];
|
||||
final responseHeaders = mergeMaps(
|
||||
{HttpHeaders.contentLengthHeader: response.contentLength.toString()},
|
||||
response.headers);
|
||||
return ResponseModel(
|
||||
statusCode: response.statusCode,
|
||||
headers: responseHeaders,
|
||||
requestHeaders: response.request?.headers,
|
||||
contentType: contentType,
|
||||
body: contentType == JSON_MIMETYPE
|
||||
? utf8.decode(response.bodyBytes)
|
||||
: response.body,
|
||||
time: time,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return [
|
||||
"Response Status: $statusCode",
|
||||
"Response Time: $time",
|
||||
"Response Headers: ${headers.toString()}",
|
||||
"Response Request Headers: ${requestHeaders.toString()}",
|
||||
"Response Body: $body",
|
||||
].join("\n");
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
import '../models/models.dart';
|
||||
import '../services/services.dart';
|
||||
import '../consts.dart';
|
||||
|
||||
const _uuid = Uuid();
|
||||
@ -63,6 +64,9 @@ class CollectionStateNotifier extends StateNotifier<List<RequestModel>> {
|
||||
List<KVRow>? requestParams,
|
||||
ContentType? requestBodyContentType,
|
||||
dynamic requestBody,
|
||||
int? responseStatus,
|
||||
String? message,
|
||||
ResponseModel? responseModel,
|
||||
}) {
|
||||
final idx = idxOfId(id);
|
||||
final newModel = state[idx].copyWith(
|
||||
@ -73,10 +77,38 @@ class CollectionStateNotifier extends StateNotifier<List<RequestModel>> {
|
||||
requestParams: requestParams,
|
||||
requestBodyContentType: requestBodyContentType,
|
||||
requestBody: requestBody,
|
||||
);
|
||||
responseStatus: responseStatus,
|
||||
message: message,
|
||||
responseModel: responseModel);
|
||||
//print(newModel);
|
||||
state = [...state.sublist(0, idx), newModel, ...state.sublist(idx + 1)];
|
||||
}
|
||||
|
||||
Future<void> sendRequest(String id) async {}
|
||||
Future<void> sendRequest(String id) async {
|
||||
final idx = idxOfId(id);
|
||||
RequestModel requestModel = getRequestModel(id);
|
||||
var responseRec = await request(requestModel);
|
||||
late final RequestModel newRequestModel;
|
||||
if (responseRec.$0 == null) {
|
||||
newRequestModel = requestModel.copyWith(
|
||||
responseStatus: -1,
|
||||
message: responseRec.$2,
|
||||
);
|
||||
} else {
|
||||
final responseModel = ResponseModel()
|
||||
.fromResponse(response: responseRec.$0!, time: responseRec.$1!);
|
||||
int statusCode = responseRec.$0!.statusCode;
|
||||
newRequestModel = requestModel.copyWith(
|
||||
responseStatus: statusCode,
|
||||
message: RESPONSE_CODE_REASONS[statusCode],
|
||||
responseModel: responseModel,
|
||||
);
|
||||
}
|
||||
print(newRequestModel);
|
||||
state = [
|
||||
...state.sublist(0, idx),
|
||||
newRequestModel,
|
||||
...state.sublist(idx + 1)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
130
lib/services/http_service.dart
Normal file
130
lib/services/http_service.dart
Normal file
@ -0,0 +1,130 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flutter_api_tool/consts.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:collection/collection.dart' show mergeMaps;
|
||||
import '../models/models.dart';
|
||||
|
||||
const SUPPORTED_URI_SCHEMES = [
|
||||
"https",
|
||||
"http"
|
||||
];
|
||||
|
||||
const DEFAULT_URI_SCHEME = "https://";
|
||||
|
||||
const METHODS_WITH_BODY = [HTTPVerb.post, HTTPVerb.put, HTTPVerb.patch, HTTPVerb.delete,];
|
||||
|
||||
(String?, bool) getUriScheme(Uri uri) {
|
||||
if(uri.hasScheme){
|
||||
if(SUPPORTED_URI_SCHEMES.contains(uri.scheme)){
|
||||
return (uri.scheme, true);
|
||||
}
|
||||
return (uri.scheme, false);
|
||||
}
|
||||
return (null, false);
|
||||
}
|
||||
|
||||
(Uri?, String?) getValidRequestUri(String? url, List<KVRow>? requestParams) {
|
||||
if(url == null || url.trim() == ""){
|
||||
return (null, "URL is missing!");
|
||||
}
|
||||
Uri? uri = Uri.tryParse(url);
|
||||
if(uri == null){
|
||||
return (null, "Check URL (malformed)");
|
||||
}
|
||||
(String?, bool) urlScheme = getUriScheme(uri);
|
||||
|
||||
if(urlScheme.$0 != null){
|
||||
if (!urlScheme.$1){
|
||||
return (null, "Unsupported URL Scheme (${urlScheme.$0})");
|
||||
}
|
||||
}
|
||||
else {
|
||||
url = DEFAULT_URI_SCHEME + url;
|
||||
}
|
||||
|
||||
uri = Uri.parse(url);
|
||||
if (uri.hasFragment){
|
||||
uri = uri.removeFragment();
|
||||
}
|
||||
|
||||
Map<String, String>? queryParams = rowsToMap(requestParams);
|
||||
if(queryParams != null){
|
||||
if(uri.hasQuery){
|
||||
Map<String, String> urlQueryParams = uri.queryParameters;
|
||||
queryParams = mergeMaps(urlQueryParams, queryParams);
|
||||
}
|
||||
uri = uri.replace(queryParameters: queryParams);
|
||||
}
|
||||
return (uri, null);
|
||||
}
|
||||
|
||||
Future<(http.Response?, Duration?, String?)> request(RequestModel requestModel) async {
|
||||
(Uri?, String?) uriRec = getValidRequestUri(requestModel.url,
|
||||
requestModel.requestParams);
|
||||
if(uriRec.$0 != null){
|
||||
Uri requestUrl = uriRec.$0!;
|
||||
Map<String, String> headers = rowsToMap(requestModel.requestHeaders) ?? {};
|
||||
http.Response response;
|
||||
String? body;
|
||||
try {
|
||||
if(METHODS_WITH_BODY.contains(requestModel.method)){
|
||||
if(requestModel.requestBody != null){
|
||||
var contentLength = utf8.encode(requestModel.requestBody).length;
|
||||
if (contentLength > 0){
|
||||
body = requestModel.requestBody as String;
|
||||
headers[HttpHeaders.contentLengthHeader] = contentLength.toString();
|
||||
switch(requestModel.requestBodyContentType){
|
||||
case ContentType.json:
|
||||
headers[HttpHeaders.contentTypeHeader] = 'application/json';
|
||||
break;
|
||||
case ContentType.text:
|
||||
headers[HttpHeaders.contentTypeHeader] = 'text/plain';
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Stopwatch stopwatch = Stopwatch()..start();
|
||||
switch(requestModel.method){
|
||||
case HTTPVerb.get:
|
||||
response = await http.get(requestUrl,
|
||||
headers: headers);
|
||||
break;
|
||||
case HTTPVerb.head:
|
||||
response = await http.head(requestUrl,
|
||||
headers: headers);
|
||||
break;
|
||||
case HTTPVerb.post:
|
||||
response = await http.post(requestUrl,
|
||||
headers: headers,
|
||||
body: body);
|
||||
break;
|
||||
case HTTPVerb.put:
|
||||
response = await http.put(requestUrl,
|
||||
headers: headers,
|
||||
body: body);
|
||||
break;
|
||||
case HTTPVerb.patch:
|
||||
response = await http.patch(requestUrl,
|
||||
headers: headers,
|
||||
body: body);
|
||||
break;
|
||||
case HTTPVerb.delete:
|
||||
response = await http.delete(requestUrl,
|
||||
headers: headers,
|
||||
body: body);
|
||||
break;
|
||||
}
|
||||
stopwatch.stop();
|
||||
return (response, stopwatch.elapsed, null);
|
||||
}
|
||||
catch (e) {
|
||||
return (null, null, e.toString());
|
||||
}
|
||||
}
|
||||
else {
|
||||
return (null, null, uriRec.$1);
|
||||
}
|
||||
}
|
1
lib/services/services.dart
Normal file
1
lib/services/services.dart
Normal file
@ -0,0 +1 @@
|
||||
export 'http_service.dart';
|
@ -15,6 +15,8 @@ dependencies:
|
||||
uuid: ^3.0.7
|
||||
tab_container: ^2.0.0
|
||||
davi: ^3.2.0
|
||||
http: ^0.13.5
|
||||
collection: ^1.17.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
Reference in New Issue
Block a user