diff --git a/packages/dynamite/dynamite/lib/src/openapi_builder.dart b/packages/dynamite/dynamite/lib/src/openapi_builder.dart index 54604016..687f8bb4 100644 --- a/packages/dynamite/dynamite/lib/src/openapi_builder.dart +++ b/packages/dynamite/dynamite/lib/src/openapi_builder.dart @@ -67,48 +67,13 @@ class OpenAPIBuilder implements Builder { "import 'package:built_value/json_object.dart';", "import 'package:built_value/serializer.dart';", "import 'package:built_value/standard_json_plugin.dart';", - "import 'package:cookie_jar/cookie_jar.dart';", "import 'package:dynamite_runtime/content_string.dart';", - "import 'package:universal_io/io.dart';", + "import 'package:dynamite_runtime/http_client.dart';", '', - "export 'package:cookie_jar/cookie_jar.dart';", + "export 'package:dynamite_runtime/http_client.dart';", '', "part '${p.basename(outputId.changeExtension('.g.dart').path)}';", '', - Extension( - (final b) => b - ..name = '${prefix}HttpClientResponseBody' - ..on = refer('HttpClientResponse') - ..methods.addAll([ - Method( - (final b) => b - ..name = 'bodyBytes' - ..returns = refer('Future') - ..type = MethodType.getter - ..modifier = MethodModifier.async - ..body = const Code( - ''' - final chunks = await toList(); - if (chunks.isEmpty) { - return Uint8List(0); - } - return Uint8List.fromList(chunks.reduce((final value, final element) => [...value, ...element])); - ''', - ), - ), - Method( - (final b) => b - ..name = 'body' - ..returns = refer('Future') - ..type = MethodType.getter - ..modifier = MethodModifier.async - ..lambda = true - ..body = const Code( - 'utf8.decode(await bodyBytes)', - ), - ), - ]), - ).accept(emitter).toString(), Class( (final b) => b ..name = '${prefix}Response' @@ -116,20 +81,7 @@ class OpenAPIBuilder implements Builder { refer('T'), refer('U'), ]) - ..fields.addAll([ - Field( - (final b) => b - ..name = 'data' - ..type = refer('T') - ..modifier = FieldModifier.final$, - ), - Field( - (final b) => b - ..name = 'headers' - ..type = refer('U') - ..modifier = FieldModifier.final$, - ), - ]) + ..extend = refer('DynamiteResponse') ..constructors.add( Constructor( (final b) => b @@ -138,7 +90,7 @@ class OpenAPIBuilder implements Builder { (final name) => Parameter( (final b) => b ..name = name - ..toThis = true, + ..toSuper = true, ), ), ), @@ -157,61 +109,10 @@ class OpenAPIBuilder implements Builder { ), ), ).accept(emitter).toString(), - Class( - (final b) => b - ..name = 'RawResponse' - ..fields.addAll([ - Field( - (final b) => b - ..name = 'statusCode' - ..type = refer('int') - ..modifier = FieldModifier.final$, - ), - Field( - (final b) => b - ..name = 'headers' - ..type = refer('Map') - ..modifier = FieldModifier.final$, - ), - Field( - (final b) => b - ..name = 'body' - ..type = refer('Uint8List') - ..modifier = FieldModifier.final$, - ), - ]) - ..constructors.add( - Constructor( - (final b) => b - ..requiredParameters.addAll( - ['statusCode', 'headers', 'body'].map( - (final name) => Parameter( - (final b) => b - ..name = name - ..toThis = true, - ), - ), - ), - ), - ) - ..methods.add( - Method( - (final b) => b - ..name = 'toString' - ..returns = refer('String') - ..annotations.add(refer('override')) - ..lambda = true - ..body = const Code( - r"'RawResponse(statusCode: $statusCode, headers: $headers, body: ${utf8.decode(body)})'", - ), - ), - ), - ).accept(emitter).toString(), Class( (final b) => b ..name = '${prefix}ApiException' - ..extend = refer('RawResponse') - ..implements.add(refer('Exception')) + ..extend = refer('DynamiteApiException') ..constructors.addAll( [ Constructor( @@ -255,27 +156,6 @@ class OpenAPIBuilder implements Builder { ), ), ).accept(emitter).toString(), - if (hasAnySecurity) ...[ - Class( - (final b) => b - ..name = '${prefix}Authentication' - ..abstract = true - ..methods.addAll([ - Method( - (final b) => b - ..name = 'id' - ..type = MethodType.getter - ..returns = refer('String'), - ), - Method( - (final b) => b - ..name = 'headers' - ..type = MethodType.getter - ..returns = refer('Map'), - ), - ]), - ).accept(emitter).toString(), - ], ]; if (spec.components?.securitySchemes != null) { @@ -291,7 +171,7 @@ class OpenAPIBuilder implements Builder { final fields = ['username', 'password']; b ..name = '${prefix}HttpBasicAuthentication' - ..extend = refer('${prefix}Authentication') + ..extend = refer('DynamiteAuthentication') ..constructors.add( Constructor( (final b) => b @@ -352,7 +232,7 @@ class OpenAPIBuilder implements Builder { (final b) { b ..name = '${prefix}HttpBearerAuthentication' - ..extend = refer('${prefix}Authentication') + ..extend = refer('DynamiteAuthentication') ..constructors.add( Constructor( (final b) => b @@ -437,42 +317,7 @@ class OpenAPIBuilder implements Builder { (final b) { if (isRootClient) { b - ..fields.addAll([ - Field( - (final b) => b - ..name = 'baseURL' - ..type = refer('String') - ..modifier = FieldModifier.final$, - ), - Field( - (final b) => b - ..name = 'baseHeaders' - ..type = refer('Map') - ..modifier = FieldModifier.final$ - ..late = true, - ), - Field( - (final b) => b - ..name = 'httpClient' - ..type = refer('HttpClient') - ..modifier = FieldModifier.final$ - ..late = true, - ), - Field( - (final b) => b - ..name = 'cookieJar' - ..type = refer('CookieJar?') - ..modifier = FieldModifier.final$, - ), - if (hasAnySecurity) ...[ - Field( - (final b) => b - ..name = 'authentications' - ..type = refer('List<${prefix}Authentication>') - ..modifier = FieldModifier.final$, - ), - ], - ]) + ..extend = refer('DynamiteClient') ..constructors.add( Constructor( (final b) => b @@ -480,109 +325,45 @@ class OpenAPIBuilder implements Builder { Parameter( (final b) => b ..name = 'baseURL' - ..toThis = true, + ..toSuper = true, ), ) ..optionalParameters.addAll([ Parameter( (final b) => b ..name = 'baseHeaders' - ..type = refer('Map?') + ..toSuper = true ..named = true, ), Parameter( (final b) => b ..name = 'userAgent' - ..type = refer('String?') + ..toSuper = true ..named = true, ), Parameter( (final b) => b ..name = 'httpClient' - ..type = refer('HttpClient?') + ..toSuper = true ..named = true, ), Parameter( (final b) => b ..name = 'cookieJar' - ..toThis = true + ..toSuper = true ..named = true, ), if (hasAnySecurity) ...[ Parameter( (final b) => b ..name = 'authentications' - ..toThis = true - ..named = true - ..defaultTo = const Code('const []'), + ..toSuper = true + ..named = true, ), ], - ]) - ..body = const Code(''' - this.baseHeaders = { - ...?baseHeaders, - }; - this.httpClient = (httpClient ?? HttpClient())..userAgent = userAgent; - '''), + ]), ), - ) - ..methods.addAll([ - Method( - (final b) => b - ..name = 'doRequest' - ..returns = refer('Future') - ..modifier = MethodModifier.async - ..requiredParameters.addAll([ - Parameter( - (final b) => b - ..name = 'method' - ..type = refer('String'), - ), - Parameter( - (final b) => b - ..name = 'path' - ..type = refer('String'), - ), - Parameter( - (final b) => b - ..name = 'headers' - ..type = refer('Map'), - ), - Parameter( - (final b) => b - ..name = 'body' - ..type = refer('Uint8List?'), - ), - ]) - ..body = const Code(r''' - final uri = Uri.parse('$baseURL$path'); - final request = await httpClient.openUrl(method, uri); - for (final header in {...baseHeaders, ...headers}.entries) { - request.headers.add(header.key, header.value); - } - if (body != null) { - request.add(body.toList()); - } - if (cookieJar != null) { - request.cookies.addAll(await cookieJar!.loadForRequest(uri)); - } - - final response = await request.close(); - if (cookieJar != null) { - await cookieJar!.saveFromResponse(uri, response.cookies); - } - final responseHeaders = {}; - response.headers.forEach((final name, final values) { - responseHeaders[name] = values.last; - }); - return RawResponse( - response.statusCode, - responseHeaders, - await response.bodyBytes, - ); - '''), - ), - ]); + ); } else { b ..fields.add( diff --git a/packages/dynamite/dynamite_runtime/lib/http_client.dart b/packages/dynamite/dynamite_runtime/lib/http_client.dart new file mode 100644 index 00000000..1550b0eb --- /dev/null +++ b/packages/dynamite/dynamite_runtime/lib/http_client.dart @@ -0,0 +1 @@ +export 'src/http_client.dart'; diff --git a/packages/dynamite/dynamite_runtime/lib/src/http_client.dart b/packages/dynamite/dynamite_runtime/lib/src/http_client.dart new file mode 100644 index 00000000..47877528 --- /dev/null +++ b/packages/dynamite/dynamite_runtime/lib/src/http_client.dart @@ -0,0 +1,144 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:cookie_jar/cookie_jar.dart'; +import 'package:universal_io/io.dart'; + +export 'package:cookie_jar/cookie_jar.dart'; + +extension DynamiteHttpClientResponseBody on HttpClientResponse { + Future get bodyBytes async { + final chunks = await toList(); + if (chunks.isEmpty) { + return Uint8List(0); + } + return Uint8List.fromList(chunks.reduce((final value, final element) => [...value, ...element])); + } + + Future get body async => utf8.decode(await bodyBytes); +} + +class DynamiteResponse { + DynamiteResponse( + this.data, + this.headers, + ); + + final T data; + + final U headers; + + @override + String toString() => 'DynamiteResponse(data: $data, headers: $headers)'; +} + +class RawResponse { + RawResponse( + this.statusCode, + this.headers, + this.body, + ); + + final int statusCode; + + final Map headers; + + final Uint8List body; + + @override + String toString() => 'RawResponse(statusCode: $statusCode, headers: $headers, body: ${utf8.decode(body)})'; +} + +class DynamiteApiException extends RawResponse implements Exception { + DynamiteApiException( + super.statusCode, + super.headers, + super.body, + ); + + @override + String toString() => + 'DynamiteApiException(statusCode: ${super.statusCode}, headers: ${super.headers}, body: ${utf8.decode(super.body)})'; +} + +abstract class DynamiteAuthentication { + String get id; + Map get headers; +} + +class DynamiteHttpBasicAuthentication extends DynamiteAuthentication { + DynamiteHttpBasicAuthentication({ + required this.username, + required this.password, + }); + + final String username; + + final String password; + + @override + String get id => 'basic_auth'; + @override + Map get headers => { + 'Authorization': 'Basic ${base64.encode(utf8.encode('$username:$password'))}', + }; +} + +class DynamiteClient { + DynamiteClient( + this.baseURL, { + final Map? baseHeaders, + final String? userAgent, + final HttpClient? httpClient, + this.cookieJar, + this.authentications = const [], + }) { + this.baseHeaders = { + ...?baseHeaders, + }; + this.httpClient = (httpClient ?? HttpClient())..userAgent = userAgent; + } + + final String baseURL; + + late final Map baseHeaders; + + late final HttpClient httpClient; + + final CookieJar? cookieJar; + + final List authentications; + + Future doRequest( + final String method, + final String path, + final Map headers, + final Uint8List? body, + ) async { + final uri = Uri.parse('$baseURL$path'); + final request = await httpClient.openUrl(method, uri); + for (final header in {...baseHeaders, ...headers}.entries) { + request.headers.add(header.key, header.value); + } + if (body != null) { + request.add(body.toList()); + } + if (cookieJar != null) { + request.cookies.addAll(await cookieJar!.loadForRequest(uri)); + } + + final response = await request.close(); + if (cookieJar != null) { + await cookieJar!.saveFromResponse(uri, response.cookies); + } + final responseHeaders = {}; + response.headers.forEach((final name, final values) { + responseHeaders[name] = values.last; + }); + return RawResponse( + response.statusCode, + responseHeaders, + await response.bodyBytes, + ); + } +} diff --git a/packages/dynamite/dynamite_runtime/pubspec.yaml b/packages/dynamite/dynamite_runtime/pubspec.yaml index 19882ef8..addb6b13 100644 --- a/packages/dynamite/dynamite_runtime/pubspec.yaml +++ b/packages/dynamite/dynamite_runtime/pubspec.yaml @@ -7,6 +7,8 @@ environment: dependencies: built_value: ^8.5.0 + cookie_jar: ^4.0.5 + universal_io: ^2.2.2 dev_dependencies: build_runner: ^2.4.2 diff --git a/packages/nextcloud/lib/src/nextcloud.openapi.dart b/packages/nextcloud/lib/src/nextcloud.openapi.dart index 72dc15b4..d7dd424c 100644 --- a/packages/nextcloud/lib/src/nextcloud.openapi.dart +++ b/packages/nextcloud/lib/src/nextcloud.openapi.dart @@ -8,58 +8,24 @@ import 'package:built_value/built_value.dart'; import 'package:built_value/json_object.dart'; import 'package:built_value/serializer.dart'; import 'package:built_value/standard_json_plugin.dart'; -import 'package:cookie_jar/cookie_jar.dart'; import 'package:dynamite_runtime/content_string.dart'; -import 'package:universal_io/io.dart'; +import 'package:dynamite_runtime/http_client.dart'; -export 'package:cookie_jar/cookie_jar.dart'; +export 'package:dynamite_runtime/http_client.dart'; part 'nextcloud.openapi.g.dart'; -extension NextcloudHttpClientResponseBody on HttpClientResponse { - Future get bodyBytes async { - final chunks = await toList(); - if (chunks.isEmpty) { - return Uint8List(0); - } - return Uint8List.fromList(chunks.reduce((final value, final element) => [...value, ...element])); - } - - Future get body async => utf8.decode(await bodyBytes); -} - -class NextcloudResponse { +class NextcloudResponse extends DynamiteResponse { NextcloudResponse( - this.data, - this.headers, + super.data, + super.headers, ); - final T data; - - final U headers; - @override String toString() => 'NextcloudResponse(data: $data, headers: $headers)'; } -class RawResponse { - RawResponse( - this.statusCode, - this.headers, - this.body, - ); - - final int statusCode; - - final Map headers; - - final Uint8List body; - - @override - String toString() => 'RawResponse(statusCode: $statusCode, headers: $headers, body: ${utf8.decode(body)})'; -} - -class NextcloudApiException extends RawResponse implements Exception { +class NextcloudApiException extends DynamiteApiException { NextcloudApiException( super.statusCode, super.headers, @@ -77,12 +43,7 @@ class NextcloudApiException extends RawResponse implements Exception { 'NextcloudApiException(statusCode: ${super.statusCode}, headers: ${super.headers}, body: ${utf8.decode(super.body)})'; } -abstract class NextcloudAuthentication { - String get id; - Map get headers; -} - -class NextcloudHttpBasicAuthentication extends NextcloudAuthentication { +class NextcloudHttpBasicAuthentication extends DynamiteAuthentication { NextcloudHttpBasicAuthentication({ required this.username, required this.password, @@ -100,63 +61,15 @@ class NextcloudHttpBasicAuthentication extends NextcloudAuthentication { }; } -class NextcloudClient { +class NextcloudClient extends DynamiteClient { NextcloudClient( - this.baseURL, { - final Map? baseHeaders, - final String? userAgent, - final HttpClient? httpClient, - this.cookieJar, - this.authentications = const [], - }) { - this.baseHeaders = { - ...?baseHeaders, - }; - this.httpClient = (httpClient ?? HttpClient())..userAgent = userAgent; - } - - final String baseURL; - - late final Map baseHeaders; - - late final HttpClient httpClient; - - final CookieJar? cookieJar; - - final List authentications; - - Future doRequest( - final String method, - final String path, - final Map headers, - final Uint8List? body, - ) async { - final uri = Uri.parse('$baseURL$path'); - final request = await httpClient.openUrl(method, uri); - for (final header in {...baseHeaders, ...headers}.entries) { - request.headers.add(header.key, header.value); - } - if (body != null) { - request.add(body.toList()); - } - if (cookieJar != null) { - request.cookies.addAll(await cookieJar!.loadForRequest(uri)); - } - - final response = await request.close(); - if (cookieJar != null) { - await cookieJar!.saveFromResponse(uri, response.cookies); - } - final responseHeaders = {}; - response.headers.forEach((final name, final values) { - responseHeaders[name] = values.last; - }); - return RawResponse( - response.statusCode, - responseHeaders, - await response.bodyBytes, - ); - } + super.baseURL, { + super.baseHeaders, + super.userAgent, + super.httpClient, + super.cookieJar, + super.authentications, + }); NextcloudCoreClient get core => NextcloudCoreClient(this); NextcloudNewsClient get news => NextcloudNewsClient(this); diff --git a/packages/nextcloud/pubspec.yaml b/packages/nextcloud/pubspec.yaml index 18885338..3babab32 100644 --- a/packages/nextcloud/pubspec.yaml +++ b/packages/nextcloud/pubspec.yaml @@ -8,7 +8,6 @@ environment: dependencies: built_collection: ^5.1.1 built_value: ^8.5.0 - cookie_jar: ^4.0.2 crypto: ^3.0.3 crypton: ^2.0.5 dynamite_runtime: