From 4936d64465813b5af54cba1f6816b4aa94da63e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Juhos?= Date: Wed, 4 Nov 2020 12:02:25 +0100 Subject: [PATCH 01/11] Update converters.md with wording and formatting fixes --- converters/converters.md | 38 +++++++++++++++++++++----------------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/converters/converters.md b/converters/converters.md index 14905058..69485d6c 100644 --- a/converters/converters.md +++ b/converters/converters.md @@ -1,55 +1,59 @@ # Converters -Converter are used to apply transformation to a request or response body, for example transforming a Dart object to a `Map` +Converters are used to apply transformations on request and/or response bodies, for example, transforming a Dart object to a `Map` or vice versa. Both `converter` and `errorConverter` are called before request and response interceptors. ```dart -final chopper = new ChopperClient( +final chopper = ChopperClient( converter: JsonConverter(), errorConverter: JsonConverter() ); ``` {% hint style="info" %} -`errorConverter` is called only on error response \(statusCode < 200 \|\| statusCode >= 300\) +The `errorConverter` is called only on error responses \(statusCode < 200 \|\| statusCode >= 300\). {% endhint %} -## JSON +## The built-in JSON converter -Chopper integrate a `JsonConverter` that take care of encoding data to JSON and decoding JSON string. It will also apply the correct header to the request \(application/json\). +Chopper provides a `JsonConverter` that is able to encode data to JSON and decode JSON strings. It will also apply the correct header to the request \(application/json\). -However, if content type header is overrided using`@Post(headers: {'content-type': '...'})`The converter won't add json header and won't apply json.encode if content type is not JSON. +However, if content type header is modified (for example by using `@Post(headers: {'content-type': '...'})`), `JsonConverter` won't add the header and it won't call json.encode if content type is not JSON. {% hint style="danger" %} -`JsonConverter` won't convert a Dart object into a `Map` or a `List`, but it will convert the `Map` into a JSON string. +`JsonConverter` itself won't convert a Dart object into a `Map` or a `List`, but it will convert a `Map` into a JSON string. {% endhint %} -## Custom converter +## Implementing custom converters -You can implement a converter by implementing the `Converter` class. +You can implement custom converters by implementing the `Converter` class. ```dart class MyConverter implements Converter { @override - Response convertResponse(Response res) { - return res; + Response convertResponse(Response response) { + var body = response.body + // Convert body to BodyType however you like + response.copyWith(body: body); } @override - Request convertRequest(Request req) { - return req; + Request convertRequest(Request request) { + var body = request.body + // Convert body to String however you like + return request.copyWith(body: body); } } ``` -`BodyType`is the expected type of your response \(ex: `String` or `CustomObject)` +`BodyType`is the expected type of the response body \(e.g., `String` or `CustomObject)`. - In the case of `BodyType` is a `List` or `BuildList`, `InnerType` will be the type of the generic \(ex: `convertResponse, CustomObject>(response)` \) +If `BodyType` is a `List` or a `BuiltList`, `InnerType` is the type of the generic parameter \(e.g., `convertResponse, CustomObject>(response)`). -## Factory Converter +## Using different converters for specific endpoints -In case you want to apply a converter to a single endpoint, you can use a `FactoryConverter` +If you want to apply specific converters only to a single endpoint, you can do so by using the `@FactoryConverter` annotation: ```dart @ChopperApi(baseUrl: "/todos") From fb1cf30d74d074d32beca819bd525c59cb782861 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Juhos?= Date: Wed, 4 Nov 2020 17:30:26 +0100 Subject: [PATCH 02/11] Update the documentation of classes and functions in interceptor.dart --- chopper/lib/src/interceptor.dart | 104 +++++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 26 deletions(-) diff --git a/chopper/lib/src/interceptor.dart b/chopper/lib/src/interceptor.dart index f8c4a9d8..9010f877 100644 --- a/chopper/lib/src/interceptor.dart +++ b/chopper/lib/src/interceptor.dart @@ -8,20 +8,24 @@ import 'response.dart'; import 'utils.dart'; import 'constants.dart'; -/// Interface to implements a response interceptor. -/// Not recommended to modify body inside interceptor, see [Converter] to decode body response. +/// An interface for implementing response interceptors. /// -/// [ResponseInterceptor] are call after [Converter.convertResponse]. +/// [ResponseInterceptor]s are called after [Converter.convertResponse]. /// -/// See builtin interceptor [HttpLoggingInterceptor] +/// While [ResponseInterceptor]s *can* modify the body of responses, +/// converting (decoding) the response body should be handled by [Converter]s. +/// +/// See built-in [HttpLoggingInterceptor] for a fully functional example implementation. +/// +/// A short example for extracting a header value from a response: /// /// ```dart -/// class MyResponseInterceptor implements ResponseInterceptor { +/// class MyResponseInterceptor implements ResponseInterceptor { /// String _token; /// /// @override /// FutureOr onResponse(Response response) { -/// _token ??= response.headers['auth_token']; +/// _token = response.headers['auth_token']; /// return response; /// } /// } @@ -31,12 +35,17 @@ abstract class ResponseInterceptor { FutureOr onResponse(Response response); } -/// Interface to implements a request interceptor. -/// Not recommended to modify body inside interceptor, see [Converter] to encode body request. +/// An interface for implementing request interceptors. +/// +/// [RequestInterceptor]s are called after [Converter.convertRequest]. /// -/// [RequestInterceptor] are call after [Converter.convertRequest] +/// While [RequestInterceptor]s *can* modify the body of requests, +/// converting (encoding) the request body should be handled by [Converter]s. /// -/// See builtin interceptor [CurlInterceptor], [HttpLoggingInterceptor] +/// See built-in [CurlInterceptor] and [HttpLoggingInterceptor] for fully +/// functional example implementations. +/// +/// A short example for adding an authentication token to every request: /// /// ```dart /// class MyRequestInterceptor implements ResponseInterceptor { @@ -46,36 +55,57 @@ abstract class ResponseInterceptor { /// } /// } /// ``` +/// +/// (See [applyHeader(request, name, value)] and [applyHeaders(request, headers)].) @immutable abstract class RequestInterceptor { FutureOr onRequest(Request request); } -/// [Converter] is is used to convert body of Request or Response -/// [convertRequest] is call before [RequestInterceptor] -/// and [convertResponse] just after the http response, before [ResponseInterceptor]. +/// An interface for implementing request and response converters. /// -/// See [JsonConverter], [FormUrlEncodedConverter] +/// [Converter]s convert objects to and from their representation in HTTP. +/// +/// [convertRequest] is called before [RequestInterceptor]s +/// and [convertResponse] is called just after the HTTP response, +/// before [ResponseInterceptor]s. +/// +/// See [JsonConverter] and [FormUrlEncodedConverter] for example implementations. @immutable abstract class Converter { + + /// Converts the received [Request] to a [Request] which has a body with the + /// HTTP representation of the original body. FutureOr convertRequest(Request request); - /// [BodyType] is the expected type of your response - /// ex: `String` or `CustomObject` + /// Converts the received [Response] to a [Response] which has a body of the + /// type [BodyType]. + /// + /// `BodyType` is the expected type of the resulting `Response`'s body + /// \(e.g., `String` or `CustomObject)`. /// - /// In the case of [BodyType] is a `List` or `BuildList` - /// [InnerType] will be the type of the generic - /// ex: `convertResponse, CustomObject>(response)` + /// If `BodyType` is a `List` or a `BuiltList`, `InnerType` is the type of the + /// generic parameter \(e.g., `convertResponse, CustomObject>(response)` \). FutureOr> convertResponse( Response response, ); } +/// An interface for implementing error response converters. +/// +/// An `ErrorConverter` is called only on error responses +/// \(statusCode < 200 || statusCode >= 300\) and before any [ResponseInterceptor]s. abstract class ErrorConverter { + + /// Converts the received [Response] to a [Response] which has a body with the + /// HTTP representation of the original body. FutureOr convertError(Response response); } -/// Add [headers] to each request +/// A [RequestInterceptor] that adds [headers] to every request. +/// +/// Note that this interceptor will overwrite existing headers having the same +/// keys as [headers]. @immutable class HeadersInterceptor implements RequestInterceptor { final Map headers; @@ -100,8 +130,10 @@ typedef DynamicResponseInterceptorFunc = FutureOr Function( ); typedef RequestInterceptorFunc = FutureOr Function(Request request); -/// Interceptor that print a curl request -/// thanks @edwardaux +/// A [RequestInterceptor] implementation that prints a curl request equivalent +/// to the network call channeled through it for debugging purposes. +/// +/// Thanks, @edwardaux @immutable class CurlInterceptor implements RequestInterceptor { @override @@ -130,6 +162,13 @@ class CurlInterceptor implements RequestInterceptor { } } +/// A [RequestInterceptor] and [ResponseInterceptor] implementation which logs +/// HTTP request and response data. +/// +/// **Warning:** Log messages written by this interceptor have the potential to +/// leak sensitive information, such as `Authorization` headers and user data +/// in response bodies. This interceptor should only be used in a controlled way +/// or in a non-production environment. @immutable class HttpLoggingInterceptor implements RequestInterceptor, ResponseInterceptor { @@ -173,11 +212,19 @@ class HttpLoggingInterceptor } } -/// [json.encode] on [Request] and [json.decode] on [Response] -/// Also add `application/json` header to each request +/// A [Converter] implementation that calls [json.encode] on [Request]s and +/// [json.decode] on [Response]s using the [dart:convert](https://api.dart.dev/stable/2.10.3/dart-convert/dart-convert-library.html) +/// package's [utf8] and [json] utilities. +/// +/// See the official documentation on +/// [Serializing JSON manually using dart:convert](https://flutter.dev/docs/development/data-and-backend/json#serializing-json-manually-using-dartconvert) +/// to learn more about when and how this `Converter` works as intended. /// -/// If content type header overrided using @Post(headers: {'content-type': '...'}) -/// The converter won't add json header and won't apply json.encode if content type is not JSON +/// This `Converter` also adds the `content-type: application/json` header to each request. +/// +/// If content type header is modified (for example by using +/// `@Post(headers: {'content-type': '...'})`), `JsonConverter` won't add the +/// header and it won't call json.encode if content type is not JSON. @immutable class JsonConverter implements Converter, ErrorConverter { const JsonConverter(); @@ -255,6 +302,11 @@ class JsonConverter implements Converter, ErrorConverter { } } +/// A [Converter] implementation that converts only [Request]s having a [Map] as their body. +/// +/// This `Converter` also adds the `content-type: application/x-www-form-urlencoded` +/// header to each request, but only if the `content-type` header is not set in +/// the original request. @immutable class FormUrlEncodedConverter implements Converter, ErrorConverter { const FormUrlEncodedConverter(); From 06bb676ca198b3213a379d4cc02f6fad253c7a47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Juhos?= Date: Thu, 5 Nov 2020 11:12:43 +0100 Subject: [PATCH 03/11] Update the documentation of classes and functions in base.dart --- chopper/lib/src/base.dart | 148 +++++++++++++++++++++++--------------- 1 file changed, 90 insertions(+), 58 deletions(-) diff --git a/chopper/lib/src/base.dart b/chopper/lib/src/base.dart index 316c1711..33714baf 100644 --- a/chopper/lib/src/base.dart +++ b/chopper/lib/src/base.dart @@ -21,22 +21,25 @@ final allowedInterceptorsType = [ DynamicResponseInterceptorFunc, ]; -/// ChopperClient is the main class of the Chopper API -/// Used to manager services, encode data, intercept request, response and error. +/// ChopperClient is the main class of the Chopper API. +/// +/// It manages registered services, encodes and decodes data, and intercepts +/// requests and responses. class ChopperClient { - /// Base url of each request to your api - /// hostname of your api for example + /// Base URL of each request of the registered services. + /// E.g., the hostname of your service. final String baseUrl; - /// Http client used to do request - /// from `package:http/http.dart` + /// The [http.Client] used to make network calls. final http.Client httpClient; - /// Converter call before request interceptor - /// and before interceptor of successful response + /// The [Converter] that handles request and response transformation before + /// the request and response interceptors are called respectively. final Converter converter; - /// Converter call on error request + /// The [ErrorConverter] that handles response transformation before the + /// response interceptors are called, but only on error responses + /// \(statusCode < 200 || statusCode >= 300\). final ErrorConverter errorConverter; final Map _services = {}; @@ -47,37 +50,53 @@ class ChopperClient { final bool _clientIsInternal; - /// Inject any service using the [services] parameter. - /// See [ChopperApi] annotation to define a service. + /// Creates and configures a [ChopperClient]. + /// + /// The base URL of each request of the registered services can be defined + /// with the [baseUrl] parameter. + /// E.g., the hostname of your service. + /// + /// A custom HTTP client can be passed as the [client] parameter to be used + /// with the created [ChopperClient]. + /// If not provided, a default [http.Client] will be used. + /// + /// [ChopperService]s can be added to the client with the [services] parameter. + /// See the [ChopperApi] annotation to learn more about creating services. /// /// ```dart /// final chopper = ChopperClient( - /// baseUrl: 'localhost:8000', - /// services: [ - /// // inject the generated service - /// TodosListService.create() - /// ], - /// ); + /// baseUrl: 'localhost:8000', + /// services: [ + /// // Add a generated service + /// TodosListService.create() + /// ], + /// ); /// ``` /// - /// Interceptors can be use to apply modification or log infos for each - /// requests and responsed happening in that client. - /// See [ResponseInterceptor] and [RequestInterceptor] + /// [RequestInterceptor]s and [ResponseInterceptor]s can be added to the client + /// with the [interceptors] parameter. + /// + /// See [RequestInterceptor], [ResponseInterceptor], [HttpLoggingInterceptor], + /// [HeadersInterceptor], [CurlInterceptor] /// /// ```dart /// final chopper = ChopperClient( - /// ... - /// interceptors: [ - /// HttpLoggingInterceptor(), - /// ] - /// ); + /// ... + /// interceptors: [ + /// HttpLoggingInterceptor(), + /// ] + /// ); /// ``` /// - /// [Converter] is used to apply modification on the body of a request/response - /// like converting an object to json (or json to object). - /// A different converter is used when an error is happening ([Response.isSuccessful] == false) - /// See [JsonConverter] + /// [Converter]s can be added to the client with the [converter] + /// parameter. + /// [Converter]s are used to convert the body of requests and responses to and + /// from the HTTP format. /// + /// A different converter can be used to handle error responses + /// (when [Response.isSuccessful] == false)) with the [errorConverter] parameter. + /// + /// See [Converter], [JsonConverter] /// /// ```dart /// final chopper = ChopperClient( @@ -123,16 +142,16 @@ class ChopperClient { bool _isAnInterceptor(value) => _isResponseInterceptor(value) || _isRequestInterceptor(value); - /// Retrieve any service injected into the [ChopperClient] + /// Retrieve any service included in the [ChopperClient] /// /// ```dart /// final chopper = ChopperClient( - /// baseUrl: 'localhost:8000', - /// services: [ - /// // inject the generated service - /// TodosListService.create() - /// ], - /// ); + /// baseUrl: 'localhost:8000', + /// services: [ + /// // Add a generated service + /// TodosListService.create() + /// ], + /// ); /// /// final todoService = chopper.getService(); /// ``` @@ -268,13 +287,14 @@ class ChopperClient { return request; } - /// Send request function that apply all interceptors and converters + /// Sends a pre-build [Request], applying all provided [Interceptor]s and + /// [Converter]s. /// - /// [BodyType] is the expected type of your response - /// ex: `String` or `CustomObject` + /// [BodyType] should be the expected type of the response body + /// (e.g., `String` or `CustomObject)`. /// - /// In the case of [BodyType] is a `List` or `BuildList` - /// [InnerType] will be the type of the generic + /// If `BodyType` is a `List` or a `BuiltList`, [InnerType] should be type of the + /// generic parameter (e.g., `convertResponse, CustomObject>(response)`). /// /// ```dart /// Response> res = await send, CustomObject>(request); @@ -312,7 +332,7 @@ class ChopperClient { return res; } - /// Http GET request using [send] function + /// Makes a HTTP GET request using the [send] function. Future> get( String url, { Map headers, @@ -329,7 +349,7 @@ class ChopperClient { ), ); - /// Http POST request using [send] function + /// Makes a HTTP POST request using the [send] function Future> post( String url, { dynamic body, @@ -352,7 +372,7 @@ class ChopperClient { ), ); - /// Http PUT request using [send] function + /// Makes a HTTP PUT request using the [send] function. Future> put( String url, { dynamic body, @@ -375,7 +395,7 @@ class ChopperClient { ), ); - /// Http PATCH request using [send] function + /// Makes a HTTP PATCH request using the [send] function. Future> patch( String url, { dynamic body, @@ -398,7 +418,7 @@ class ChopperClient { ), ); - /// Http DELETE request using [send] function + /// Makes a HTTP DELETE request using the [send] function. Future> delete( String url, { Map headers, @@ -415,7 +435,7 @@ class ChopperClient { ), ); - /// Http Head request using [send] function + /// Makes a HTTP HEAD request using the [send] function. Future> head( String url, { Map headers, @@ -432,9 +452,11 @@ class ChopperClient { ), ); - /// dispose [ChopperClient] to clean memory. + /// Disposes this [ChopperClient] to clean up memory. /// - /// It won't close the http client if you provided it in the [ChopperClient] constructor. + /// **Warning**: If a custom [http.Client] was provided while creating this `ChopperClient`, + /// this method ***will not*** close it. In that case, closing the client is + /// its creator's responsibility. @mustCallSuper void dispose() { _requestController.close(); @@ -451,25 +473,35 @@ class ChopperClient { } } - /// Event stream of request just before http call - /// all converters and interceptors have been run + /// A stream of processed [Request]s, as in after all [Converter]s, and + /// [RequestInterceptor]s have been run. Stream get onRequest => _requestController.stream; - /// Event stream of response - /// all converters and interceptors have been run + /// A stream of processed [Response]s, as in after all [Converter]s and + /// [ResponseInterceptor]s have been run. Stream get onResponse => _responseController.stream; } -/// Used by generator to generate apis +/// A marker and helper class used by `chopper_generator` to generate network +/// call implementations. +/// +///```dart +///@ChopperApi(baseUrl: "/todos") +///abstract class TodosListService extends ChopperService { +/// +/// // A helper method that helps instantiating your service +/// static TodosListService create([ChopperClient client]) => +/// _$TodosListService(client); +///} +///``` abstract class ChopperService { ChopperClient client; - /// Internally use to retrieve service from [ChopperClient] - /// FIXME: use runtimeType ? + /// Used internally to retrieve the service from [ChopperClient]. + /// TODO: use runtimeType Type get definitionType; - /// Cleanup memory - /// Should be call when the service is not used anymore + /// Disposes this [ChopperService] to clean up memory. @mustCallSuper void dispose() { client = null; From 82d283e6f2cf209896e4afbd9507f207aa9bcde7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Juhos?= Date: Sat, 7 Nov 2020 10:44:04 +0100 Subject: [PATCH 04/11] Update the documentation of public functions in utils.dart --- chopper/lib/src/utils.dart | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/chopper/lib/src/utils.dart b/chopper/lib/src/utils.dart index 7d40b891..b59e68aa 100644 --- a/chopper/lib/src/utils.dart +++ b/chopper/lib/src/utils.dart @@ -1,11 +1,15 @@ import 'package:chopper/chopper.dart'; import 'package:logging/logging.dart'; +import 'package:meta/meta.dart'; -/// Apply [value] to the header field [name] of the [request] -/// if [override] is true, it will erase already present headers with the new value +/// Creates a new [Request] by copying [request] and adding a header with the +/// provided key [name] and value [value] to the result. +/// +/// If [request] already has a header with the key [name] and [override] is true +/// (default), the existing header value will be replaced with [value] in the resulting [Request]. /// /// ```dart -/// final newRequest = applyHeader(request, 'foo', 'bar'); +/// final newRequest = applyHeader(request, 'Authorization', 'Bearer '); /// ``` Request applyHeader( Request request, @@ -19,11 +23,17 @@ Request applyHeader( override: override, ); -/// Apply given [headers] to the [request] -/// if [override] is true, it will erase already present headers with the new value +/// Creates a new [Request] by copying [request] and adding the provided [headers] +/// to the result. +/// +/// If [request] already has headers with keys provided in [headers] and [override] +/// is true (default), the conflicting headers will be replaced. /// /// ```dart -/// final newRequest = applyHeaders(request, {'foo': 'bar'}); +/// final newRequest = applyHeaders(request, { +/// 'Authorization': 'Bearer ', +/// 'Content-Type': 'application/json', +/// }); /// ``` Request applyHeaders( Request request, @@ -42,8 +52,9 @@ Request applyHeaders( final chopperLogger = Logger('Chopper'); -/// transform {'foo': 'bar', 'ints': [ 1337, 42 ] } -/// to 'foo=bar&ints=1337&ints=42' +/// Creates a valid URI query string from [map]. +/// +/// E.g., `{'foo': 'bar', 'ints': [ 1337, 42 ] }` will become 'foo=bar&ints=1337&ints=42'. String mapToQuery(Map map) => _mapToQuery(map).join('&'); Iterable<_Pair> _mapToQuery( From e618da9d07b4b2664b2a9d9a1bf173963599127d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Juhos?= Date: Sat, 7 Nov 2020 11:30:00 +0100 Subject: [PATCH 05/11] Update the documentation of public classes and functions in request.dart --- chopper/lib/src/request.dart | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/chopper/lib/src/request.dart b/chopper/lib/src/request.dart index ceb3108c..393f067c 100644 --- a/chopper/lib/src/request.dart +++ b/chopper/lib/src/request.dart @@ -6,7 +6,7 @@ import 'package:http/http.dart' as http; import 'utils.dart'; import 'constants.dart'; -/// HTTP request generated by Chopper +/// This class represents an HTTP request that can be made with Chopper. @immutable class Request { final String method; @@ -56,8 +56,7 @@ class Request { baseUrl: baseUrl, ); - /// Make a copy of the current request - /// And replace with given values + /// Makes a copy of this request, replacing original values with the given ones. Request copyWith({ HttpMethod method, String url, @@ -84,14 +83,14 @@ class Request { Map _buildHeaders() => Map.from(headers); - /// Convert chopper request to a [http.BaseRequest] + /// Converts this Chopper Request into a [http.BaseRequest]. /// - /// Apply all [parameters] and [headers] + /// All [parameters] and [headers] are conserved. /// - /// Depending on the request type it returns: + /// Depending on the request type the returning object will be: /// - [http.StreamedRequest] if body is a [Stream>] /// - [http.MultipartRequest] if [multipart] is true - /// - or a simple [http.Request] + /// - or a [http.Request] Future toBaseRequest() async { final uri = _buildUri(); final heads = _buildHeaders(); @@ -122,6 +121,7 @@ class Request { } } +/// Represents a part in a multipart request. @immutable class PartValue { final T value; @@ -136,6 +136,7 @@ class PartValue { PartValue replace({String name, NewType value}) => copyWith(name: name, value: value); + /// Makes a copy of this PartValue, replacing original values with the given ones. PartValue copyWith({String name, NewType value}) => PartValue( name ?? this.name, @@ -143,16 +144,20 @@ class PartValue { ); } +/// Represents a file part in a multipart request. @immutable class PartValueFile extends PartValue { PartValueFile(String name, T value) : super(name, value); } +/// Builds a valid URI from [baseUrl], [url] and [parameters]. +/// +/// If [url] starts with 'http://' or 'https://', baseUrl is ignored. Uri buildUri(String baseUrl, String url, Map parameters) { var uri; if (url.startsWith('http://') || url.startsWith('https://')) { - // if the request's url is already a fully qualified URL, we can use - // as-is and ignore the baseUrl + // If the request's url is already a fully qualified URL, we can use it + // as-is and ignore the baseUrl. uri = Uri.parse(url); } else { if (!baseUrl.endsWith('/') && !url.startsWith('/')) { From b807d7e6903a3ec671e1bca03fef367a1e4459a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Juhos?= Date: Mon, 9 Nov 2020 14:03:21 +0100 Subject: [PATCH 06/11] Update the documentation of public classes and functions in response.dart --- chopper/lib/src/request.dart | 1 + chopper/lib/src/response.dart | 42 ++++++++++++++++++++--------------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/chopper/lib/src/request.dart b/chopper/lib/src/request.dart index 393f067c..c6be2470 100644 --- a/chopper/lib/src/request.dart +++ b/chopper/lib/src/request.dart @@ -137,6 +137,7 @@ class PartValue { copyWith(name: name, value: value); /// Makes a copy of this PartValue, replacing original values with the given ones. + /// This method can also alter the type of the request body. PartValue copyWith({String name, NewType value}) => PartValue( name ?? this.name, diff --git a/chopper/lib/src/response.dart b/chopper/lib/src/response.dart index 36cb68fb..6a02620b 100644 --- a/chopper/lib/src/response.dart +++ b/chopper/lib/src/response.dart @@ -3,28 +3,30 @@ import 'dart:typed_data'; import 'package:http/http.dart' as http; import 'package:meta/meta.dart'; -/// Wrapper arround [http.BaseResponse] +/// A [http.BaseResponse] wrapper representing a response of a Chopper network call. /// -/// Return by every function generated by Chopper. -/// -/// Example: /// ```dart /// @Get(path: '/something') /// Future fetchSomething(); /// ``` +/// +/// ```dart +/// @Get(path: '/items/{id}') +/// Future> fetchItem(); +/// ``` @immutable class Response { - /// Base response return by `package:http` + /// The [http.BaseResponse] from `package:http` that this [Response] wraps. final http.BaseResponse base; - /// Body of response converted by Chopper - /// see [Converter] for more infos + /// The body of the response after conversion by Chopper + /// See [Converter] for more on body conversion. /// - /// Can be null if [isSuccessful] is not true - /// Use [error] to get error body + /// Can be null if [isSuccessful] is not true. + /// Use [error] to get error body. final BodyType body; - /// Body of response if [isSuccessful] is false + /// The body of the response if [isSuccessful] is false. final Object error; Response(this.base, this.body, {this.error}); @@ -41,8 +43,8 @@ class Response { bodyError: bodyError, ); - /// Make a copy of the current response - /// And replace with given values + /// Makes a copy of this Response, replacing original values with the given ones. + /// This method can also alter the type of the response body. Response copyWith({ http.BaseResponse base, NewBodyType body, @@ -54,21 +56,25 @@ class Response { error: bodyError ?? error, ); - /// HTTP status code of the response + /// The HTTP status code of the response. int get statusCode => base.statusCode; - /// true if status code is >= 200 && <3 - /// if false, [error] will contains the response + /// Whether the network call was successful or not. + /// + /// `true` if the result code of the network call is >= 200 && <300 + /// If false, [error] will contain the converted error response body. bool get isSuccessful => statusCode >= 200 && statusCode < 300; - /// HTTP headers of the response + /// HTTP headers of the response. Map get headers => base.headers; - /// Return body as bytes ([Uint8List]) if available + /// Returns the response body as bytes ([Uint8List]) provided the network + /// call was successful, else this will be `null`. Uint8List get bodyBytes => base is http.Response ? (base as http.Response).bodyBytes : null; - /// Return body as [String] if available + /// Returns the response body as a String provided the network + /// call was successful, else this will be `null`. String get bodyString => base is http.Response ? (base as http.Response).body : null; } From d55699383787a7549f30374c15ef100d964934ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Juhos?= Date: Mon, 9 Nov 2020 14:21:57 +0100 Subject: [PATCH 07/11] Update the documentation of ConvertRequest, ConvertResponse, and FactoryConverter in annotations.dart --- chopper/lib/src/annotations.dart | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/chopper/lib/src/annotations.dart b/chopper/lib/src/annotations.dart index 3b592a22..d2840277 100644 --- a/chopper/lib/src/annotations.dart +++ b/chopper/lib/src/annotations.dart @@ -262,9 +262,14 @@ class Head extends Method { ); } +/// A function that should convert the body of a [Request] to the HTTP representation. typedef ConvertRequest = FutureOr Function(Request request); +/// A function that should convert the body of a [Response] from the HTTP +/// representation to a Dart object. typedef ConvertResponse = FutureOr Function(Response response); +/// A helper class for wrapping a request and a response converter. +/// See [ConvertRequest], [ConvertResponse]. @immutable class FactoryConverter { final ConvertRequest request; @@ -276,7 +281,7 @@ class FactoryConverter { }); } -/// Define a field for a `x-www-form-urlencoded` request. +/// Defines a field for a `x-www-form-urlencoded` request. /// Automatically binds to the name of the method parameter. /// /// ```dart From 5a0630b001cfb4ba8ae1e3b1af1bc78668240a18 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Juhos?= Date: Mon, 9 Nov 2020 14:27:14 +0100 Subject: [PATCH 08/11] Fix typos in the documentation in annotations.dart --- chopper/lib/src/annotations.dart | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/chopper/lib/src/annotations.dart b/chopper/lib/src/annotations.dart index d2840277..6c5a7423 100644 --- a/chopper/lib/src/annotations.dart +++ b/chopper/lib/src/annotations.dart @@ -247,7 +247,7 @@ class Patch extends Method { ); } -/// Defined a method as an HTTP HEAD request. +/// Defines a method as an HTTP HEAD request. @immutable class Head extends Method { const Head({ @@ -316,9 +316,9 @@ class Multipart { const Multipart(); } -/// Use it to define a part of a [Multipart] request. +/// Use [Part] to define a part of a [Multipart] request. /// -/// All values will be converted to [String] using the [toString] method. +/// All values will be converted to [String] using their [toString] method. /// /// Also accepts `MultipartFile` (from package:http). @immutable @@ -327,7 +327,7 @@ class Part { const Part([this.name]); } -/// Use it to define a file field for a [Multipart] request. +/// Use [PartFile] to define a file field for a [Multipart] request. /// /// ``` /// @Post(path: 'file') @@ -359,7 +359,7 @@ class PartFile { /// - [String] (path of your file) /// - `MultipartFile` (from package:http) @immutable -@Deprecated('use PartFile') +@Deprecated('Use PartFile instead') class FileField extends PartFile { const FileField([String name]) : super(name); } From 2f43770ee6b61e419561b12c8b9f93f59c9d3d9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Juhos?= Date: Sun, 15 Nov 2020 10:11:11 +0100 Subject: [PATCH 09/11] Remove TODO from ChopperService.definitionType's documentation --- chopper/lib/src/base.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chopper/lib/src/base.dart b/chopper/lib/src/base.dart index 33714baf..fadede90 100644 --- a/chopper/lib/src/base.dart +++ b/chopper/lib/src/base.dart @@ -494,11 +494,11 @@ class ChopperClient { /// _$TodosListService(client); ///} ///``` -abstract class ChopperService { +abstract class ChopperServic1e { ChopperClient client; /// Used internally to retrieve the service from [ChopperClient]. - /// TODO: use runtimeType + // TODO: use runtimeType Type get definitionType; /// Disposes this [ChopperService] to clean up memory. From 1e9237c88f230250c47074b4a6baf942fcea35fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Juhos?= Date: Mon, 23 Nov 2020 11:29:52 +0100 Subject: [PATCH 10/11] Add example usage to FactoryConverter's documentation --- chopper/lib/src/annotations.dart | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/chopper/lib/src/annotations.dart b/chopper/lib/src/annotations.dart index 6c5a7423..b556e4ec 100644 --- a/chopper/lib/src/annotations.dart +++ b/chopper/lib/src/annotations.dart @@ -268,8 +268,34 @@ typedef ConvertRequest = FutureOr Function(Request request); /// representation to a Dart object. typedef ConvertResponse = FutureOr Function(Response response); -/// A helper class for wrapping a request and a response converter. +/// Defines custom [Converter] methods for a single network API endpoint. /// See [ConvertRequest], [ConvertResponse]. +/// +/// ```dart +/// @ChopperApi(baseUrl: '/todos') +/// abstract class TodosListService extends ChopperService { +/// static TodosListService create([ChopperClient client]) => +/// _$TodosListService(client); +/// +/// static FutureOr customRequestConverter(Request request) { +/// return request.copyWith( +/// body: // Convert request.body the way your API needs it. See [JsonConverter.encodeJson] for an example. +/// ); +/// } +/// +/// static FutureOr customResponseConverter(Response response) { +/// return response.copyWith( +/// body: // Convert response.body the way your API needs it. See [JsonConverter.decodeJson] for an example. +/// ); +/// } +/// +/// @Get(path: "/{id}") +/// @FactoryConverter( +/// request: customRequestConverter, +/// response: customResponseConverter +/// ) +/// Future> getTodo(@Path("id")); +/// } @immutable class FactoryConverter { final ConvertRequest request; From 71b927af1890e8b681cc60e90a9ae87223243215 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Istv=C3=A1n=20Juhos?= Date: Mon, 23 Nov 2020 11:39:49 +0100 Subject: [PATCH 11/11] Fix accidental typo in ChopperService's class name --- chopper/lib/src/base.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/chopper/lib/src/base.dart b/chopper/lib/src/base.dart index fadede90..8f59e761 100644 --- a/chopper/lib/src/base.dart +++ b/chopper/lib/src/base.dart @@ -94,7 +94,7 @@ class ChopperClient { /// from the HTTP format. /// /// A different converter can be used to handle error responses - /// (when [Response.isSuccessful] == false)) with the [errorConverter] parameter. + /// (when [Response.isSuccessful] == false)) with tche [errorConverter] parameter. /// /// See [Converter], [JsonConverter] /// @@ -494,7 +494,7 @@ class ChopperClient { /// _$TodosListService(client); ///} ///``` -abstract class ChopperServic1e { +abstract class ChopperService { ChopperClient client; /// Used internally to retrieve the service from [ChopperClient].