Browse Source

Merge pull request #127 from provokateurin/feature/cookie-jar

dynamite,nextcloud,neon: Implement CookieJar
pull/130/head
Kate 2 years ago committed by GitHub
parent
commit
4d046a918a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 88
      packages/dynamite/lib/src/openapi_builder.dart
  2. 4
      packages/neon/integration_test/screenshot_test.dart
  3. 4
      packages/neon/lib/main.dart
  4. 2
      packages/neon/lib/src/apps/files/app.dart
  5. 2
      packages/neon/lib/src/apps/news/app.dart
  6. 2
      packages/neon/lib/src/apps/notes/app.dart
  7. 2
      packages/neon/lib/src/apps/notifications/app.dart
  8. 4
      packages/neon/lib/src/blocs/accounts.dart
  9. 2
      packages/neon/lib/src/blocs/push_notifications.dart
  10. 2
      packages/neon/lib/src/utils/account_options.dart
  11. 4
      packages/neon/lib/src/utils/app_implementation.dart
  12. 2
      packages/neon/lib/src/utils/global_options.dart
  13. 2
      packages/neon/lib/src/utils/nextcloud_app_specific_options.dart
  14. 4
      packages/neon/lib/src/utils/push_utils.dart
  15. 4
      packages/neon/lib/src/utils/storage.dart
  16. 7
      packages/neon/pubspec.lock
  17. 44
      packages/nextcloud/lib/src/nextcloud.openapi.dart
  18. 2
      packages/nextcloud/pubspec.yaml

88
packages/dynamite/lib/src/openapi_builder.dart

@ -34,13 +34,45 @@ class OpenAPIBuilder implements Builder {
final registeredJsonObjects = <String>[]; final registeredJsonObjects = <String>[];
final output = <String>[ final output = <String>[
"import 'dart:convert';", "import 'dart:convert';",
"import 'dart:io';",
"import 'dart:typed_data';", "import 'dart:typed_data';",
'', '',
"import 'package:http/http.dart' as http;", "import 'package:cookie_jar/cookie_jar.dart';",
"import 'package:json_annotation/json_annotation.dart';", "import 'package:json_annotation/json_annotation.dart';",
'', '',
"export 'package:cookie_jar/cookie_jar.dart';",
'',
"part '${p.basename(outputId.changeExtension('.g.dart').path)}';", "part '${p.basename(outputId.changeExtension('.g.dart').path)}';",
'', '',
Extension(
(final b) => b
..name = 'HttpClientResponseBody'
..on = refer('HttpClientResponse')
..methods.addAll([
Method(
(final b) => b
..name = 'bodyBytes'
..returns = refer('Future<Uint8List>')
..type = MethodType.getter
..modifier = MethodModifier.async
..lambda = true
..body = const Code(
'Uint8List.fromList((await toList()).reduce((final value, final element) => [...value, ...element]))',
),
),
Method(
(final b) => b
..name = 'body'
..returns = refer('Future<String>')
..type = MethodType.getter
..modifier = MethodModifier.async
..lambda = true
..body = const Code(
'utf8.decode(await bodyBytes)',
),
),
]),
).accept(emitter).toString(),
Class( Class(
(final b) => b (final b) => b
..name = 'Response' ..name = 'Response'
@ -707,6 +739,19 @@ class OpenAPIBuilder implements Builder {
..modifier = FieldModifier.final$ ..modifier = FieldModifier.final$
..late = true, ..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) ...[ if (hasAnySecurity) ...[
Field( Field(
(final b) => b (final b) => b
@ -733,6 +778,18 @@ class OpenAPIBuilder implements Builder {
..type = refer('Map<String, String>?') ..type = refer('Map<String, String>?')
..named = true, ..named = true,
), ),
Parameter(
(final b) => b
..name = 'httpClient'
..type = refer('HttpClient?')
..named = true,
),
Parameter(
(final b) => b
..name = 'cookieJar'
..toThis = true
..named = true,
),
if (hasAnySecurity) ...[ if (hasAnySecurity) ...[
Parameter( Parameter(
(final b) => b (final b) => b
@ -753,6 +810,7 @@ class OpenAPIBuilder implements Builder {
}, },
''' : ''} ''' : ''}
}; };
this.httpClient = httpClient ?? HttpClient();
'''), '''),
), ),
) )
@ -797,18 +855,30 @@ class OpenAPIBuilder implements Builder {
), ),
]) ])
..body = const Code(r''' ..body = const Code(r'''
final request = http.Request(method, Uri.parse('$baseURL$path')); final uri = Uri.parse('$baseURL$path');
request.headers.addAll(baseHeaders); final request = await httpClient.openUrl(method, uri);
request.headers.addAll(headers); for (final header in {...baseHeaders, ...headers}.entries) {
request.headers.add(header.key, header.value);
}
if (body != null) { if (body != null) {
request.bodyBytes = body.toList(); 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 = <String, String>{};
final response = await http.Response.fromStream(await request.send()); response.headers.forEach((final name, final values) {
responseHeaders[name] = values.last;
});
return Response( return Response(
response.statusCode, response.statusCode,
response.headers, responseHeaders,
response.bodyBytes, await response.bodyBytes,
); );
'''), '''),
), ),

4
packages/neon/integration_test/screenshot_test.dart

@ -108,7 +108,7 @@ Future pumpAppPage(
final allAppImplementations = getAppImplementations(sharedPreferences, requestManager, platform); final allAppImplementations = getAppImplementations(sharedPreferences, requestManager, platform);
final globalOptions = GlobalOptions( final globalOptions = GlobalOptions(
Storage('global', sharedPreferences), AppStorage('global', sharedPreferences),
packageInfo, packageInfo,
); );
await globalOptions.pushNotificationsEnabled.set(false); await globalOptions.pushNotificationsEnabled.set(false);
@ -116,7 +116,7 @@ Future pumpAppPage(
final accountsBloc = AccountsBloc( final accountsBloc = AccountsBloc(
requestManager, requestManager,
platform, platform,
Storage('accounts', sharedPreferences), AppStorage('accounts', sharedPreferences),
sharedPreferences, sharedPreferences,
globalOptions, globalOptions,
packageInfo, packageInfo,

4
packages/neon/lib/main.dart

@ -36,14 +36,14 @@ Future main() async {
final packageInfo = await PackageInfo.fromPlatform(); final packageInfo = await PackageInfo.fromPlatform();
final globalOptions = GlobalOptions( final globalOptions = GlobalOptions(
Storage('global', sharedPreferences), AppStorage('global', sharedPreferences),
packageInfo, packageInfo,
); );
final accountsBloc = AccountsBloc( final accountsBloc = AccountsBloc(
requestManager, requestManager,
platform, platform,
Storage('accounts', sharedPreferences), AppStorage('accounts', sharedPreferences),
sharedPreferences, sharedPreferences,
globalOptions, globalOptions,
packageInfo, packageInfo,

2
packages/neon/lib/src/apps/files/app.dart

@ -48,7 +48,7 @@ class FilesApp extends AppImplementation<FilesBloc, FilesAppSpecificOptions> {
String nameFromLocalization(final AppLocalizations localizations) => localizations.filesName; String nameFromLocalization(final AppLocalizations localizations) => localizations.filesName;
@override @override
FilesAppSpecificOptions buildOptions(final Storage storage) => FilesAppSpecificOptions(storage); FilesAppSpecificOptions buildOptions(final AppStorage storage) => FilesAppSpecificOptions(storage);
@override @override
FilesBloc buildBloc(final NextcloudClient client) => FilesBloc( FilesBloc buildBloc(final NextcloudClient client) => FilesBloc(

2
packages/neon/lib/src/apps/news/app.dart

@ -56,7 +56,7 @@ class NewsApp extends AppImplementation<NewsBloc, NewsAppSpecificOptions> {
String nameFromLocalization(final AppLocalizations localizations) => localizations.newsName; String nameFromLocalization(final AppLocalizations localizations) => localizations.newsName;
@override @override
NewsAppSpecificOptions buildOptions(final Storage storage) => NewsAppSpecificOptions(storage, platform); NewsAppSpecificOptions buildOptions(final AppStorage storage) => NewsAppSpecificOptions(storage, platform);
@override @override
NewsBloc buildBloc(final NextcloudClient client) => NewsBloc( NewsBloc buildBloc(final NextcloudClient client) => NewsBloc(

2
packages/neon/lib/src/apps/notes/app.dart

@ -45,7 +45,7 @@ class NotesApp extends AppImplementation<NotesBloc, NotesAppSpecificOptions> {
String nameFromLocalization(final AppLocalizations localizations) => localizations.notesName; String nameFromLocalization(final AppLocalizations localizations) => localizations.notesName;
@override @override
NotesAppSpecificOptions buildOptions(final Storage storage) => NotesAppSpecificOptions(storage); NotesAppSpecificOptions buildOptions(final AppStorage storage) => NotesAppSpecificOptions(storage);
@override @override
NotesBloc buildBloc(final NextcloudClient client) => NotesBloc( NotesBloc buildBloc(final NextcloudClient client) => NotesBloc(

2
packages/neon/lib/src/apps/notifications/app.dart

@ -25,7 +25,7 @@ class NotificationsApp extends AppImplementation<NotificationsBloc, Notification
String nameFromLocalization(final AppLocalizations localizations) => localizations.notificationsName; String nameFromLocalization(final AppLocalizations localizations) => localizations.notificationsName;
@override @override
NotificationsAppSpecificOptions buildOptions(final Storage storage) => NotificationsAppSpecificOptions(storage); NotificationsAppSpecificOptions buildOptions(final AppStorage storage) => NotificationsAppSpecificOptions(storage);
@override @override
NotificationsBloc buildBloc(final NextcloudClient client) => NotificationsBloc( NotificationsBloc buildBloc(final NextcloudClient client) => NotificationsBloc(

4
packages/neon/lib/src/blocs/accounts.dart

@ -123,7 +123,7 @@ class AccountsBloc extends $AccountsBloc {
} }
AccountSpecificOptions getOptions(final Account account) => _accountsOptions[account.id] ??= AccountSpecificOptions( AccountSpecificOptions getOptions(final Account account) => _accountsOptions[account.id] ??= AccountSpecificOptions(
Storage('accounts-${account.id}', _sharedPreferences), AppStorage('accounts-${account.id}', _sharedPreferences),
getAppsBloc(account), getAppsBloc(account),
); );
@ -177,7 +177,7 @@ class AccountsBloc extends $AccountsBloc {
final RequestManager _requestManager; final RequestManager _requestManager;
final NeonPlatform _platform; final NeonPlatform _platform;
final Storage _storage; final AppStorage _storage;
final SharedPreferences _sharedPreferences; final SharedPreferences _sharedPreferences;
final GlobalOptions _globalOptions; final GlobalOptions _globalOptions;
final List<AppImplementation> _allAppImplementations; final List<AppImplementation> _allAppImplementations;

2
packages/neon/lib/src/blocs/push_notifications.dart

@ -126,7 +126,7 @@ class PushNotificationsBloc extends $PushNotificationsBloc {
final AccountsBloc _accountsBloc; final AccountsBloc _accountsBloc;
final NeonPlatform _platform; final NeonPlatform _platform;
final SharedPreferences _sharedPreferences; final SharedPreferences _sharedPreferences;
late final _storage = Storage('notifications', _sharedPreferences); late final _storage = AppStorage('notifications', _sharedPreferences);
final GlobalOptions _globalOptions; final GlobalOptions _globalOptions;
final Env? _env; final Env? _env;
RSAKeypair? _keypair; RSAKeypair? _keypair;

2
packages/neon/lib/src/utils/account_options.dart

@ -17,7 +17,7 @@ class AccountSpecificOptions {
}); });
} }
final Storage _storage; final AppStorage _storage;
final AppsBloc _appsBloc; final AppsBloc _appsBloc;
final _appIDsSubject = BehaviorSubject<Map<String?, LabelBuilder>>(); final _appIDsSubject = BehaviorSubject<Map<String?, LabelBuilder>>();

4
packages/neon/lib/src/utils/app_implementation.dart

@ -18,7 +18,7 @@ abstract class AppImplementation<T extends RxBlocBase, R extends NextcloudAppSpe
this.requestManager, this.requestManager,
this.platform, this.platform,
) { ) {
final storage = Storage('app-$id', sharedPreferences); final storage = AppStorage('app-$id', sharedPreferences);
options = buildOptions(storage); options = buildOptions(storage);
} }
@ -30,7 +30,7 @@ abstract class AppImplementation<T extends RxBlocBase, R extends NextcloudAppSpe
String name(final BuildContext context) => nameFromLocalization(AppLocalizations.of(context)); String name(final BuildContext context) => nameFromLocalization(AppLocalizations.of(context));
late final R options; late final R options;
R buildOptions(final Storage storage); R buildOptions(final AppStorage storage);
T buildBloc(final NextcloudClient client); T buildBloc(final NextcloudClient client);

2
packages/neon/lib/src/utils/global_options.dart

@ -35,7 +35,7 @@ class GlobalOptions {
}); });
} }
final Storage _storage; final AppStorage _storage;
final PackageInfo _packageInfo; final PackageInfo _packageInfo;
final _themeOLEDAsDarkEnabledSubject = BehaviorSubject<bool>(); final _themeOLEDAsDarkEnabledSubject = BehaviorSubject<bool>();
final _pushNotificationsEnabledEnabledSubject = BehaviorSubject<bool>(); final _pushNotificationsEnabledEnabledSubject = BehaviorSubject<bool>();

2
packages/neon/lib/src/utils/nextcloud_app_specific_options.dart

@ -3,7 +3,7 @@ part of '../neon.dart';
abstract class NextcloudAppSpecificOptions { abstract class NextcloudAppSpecificOptions {
NextcloudAppSpecificOptions(this.storage); NextcloudAppSpecificOptions(this.storage);
final Storage storage; final AppStorage storage;
late final List<OptionsCategory> categories; late final List<OptionsCategory> categories;
late final List<Option> options; late final List<Option> options;

4
packages/neon/lib/src/utils/push_utils.dart

@ -1,7 +1,7 @@
part of '../neon.dart'; part of '../neon.dart';
class PushUtils { class PushUtils {
static Future<RSAKeypair> loadRSAKeypair(final Storage storage) async { static Future<RSAKeypair> loadRSAKeypair(final AppStorage storage) async {
const keyDevicePrivateKey = 'device-private-key'; const keyDevicePrivateKey = 'device-private-key';
late RSAKeypair keypair; late RSAKeypair keypair;
@ -51,7 +51,7 @@ class PushUtils {
); );
final sharedPreferences = await SharedPreferences.getInstance(); final sharedPreferences = await SharedPreferences.getInstance();
final keypair = await loadRSAKeypair(Storage('notifications', sharedPreferences)); final keypair = await loadRSAKeypair(AppStorage('notifications', sharedPreferences));
final data = json.decode(utf8.decode(message)) as Map<String, dynamic>; final data = json.decode(utf8.decode(message)) as Map<String, dynamic>;
final notification = NotificationsPushNotification( final notification = NotificationsPushNotification(
accountID: instance, accountID: instance,

4
packages/neon/lib/src/utils/storage.dart

@ -1,7 +1,7 @@
part of '../neon.dart'; part of '../neon.dart';
class Storage extends SettingsStorage { class AppStorage extends SettingsStorage {
Storage( AppStorage(
this._id, this._id,
this._sharedPreferences, this._sharedPreferences,
); );

7
packages/neon/pubspec.lock

@ -148,6 +148,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "3.1.1" version: "3.1.1"
cookie_jar:
dependency: transitive
description:
name: cookie_jar
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.1"
cross_file: cross_file:
dependency: transitive dependency: transitive
description: description:

44
packages/nextcloud/lib/src/nextcloud.openapi.dart

@ -1,11 +1,20 @@
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:http/http.dart' as http; import 'package:cookie_jar/cookie_jar.dart';
import 'package:json_annotation/json_annotation.dart'; import 'package:json_annotation/json_annotation.dart';
export 'package:cookie_jar/cookie_jar.dart';
part 'nextcloud.openapi.g.dart'; part 'nextcloud.openapi.g.dart';
extension HttpClientResponseBody on HttpClientResponse {
Future<Uint8List> get bodyBytes async =>
Uint8List.fromList((await toList()).reduce((final value, final element) => [...value, ...element]));
Future<String> get body async => utf8.decode(await bodyBytes);
}
class Response { class Response {
Response( Response(
this.statusCode, this.statusCode,
@ -64,6 +73,8 @@ class Client {
Client( Client(
this.baseURL, { this.baseURL, {
Map<String, String>? baseHeaders, Map<String, String>? baseHeaders,
HttpClient? httpClient,
this.cookieJar,
this.authentication, this.authentication,
}) { }) {
this.baseHeaders = { this.baseHeaders = {
@ -74,12 +85,17 @@ class Client {
...authentication!.headers, ...authentication!.headers,
}, },
}; };
this.httpClient = httpClient ?? HttpClient();
} }
final String baseURL; final String baseURL;
late final Map<String, String> baseHeaders; late final Map<String, String> baseHeaders;
late final HttpClient httpClient;
final CookieJar? cookieJar;
final Authentication? authentication; final Authentication? authentication;
CoreClient get core => CoreClient(this); CoreClient get core => CoreClient(this);
@ -94,18 +110,30 @@ class Client {
Map<String, String> headers, Map<String, String> headers,
Uint8List? body, Uint8List? body,
) async { ) async {
final request = http.Request(method, Uri.parse('$baseURL$path')); final uri = Uri.parse('$baseURL$path');
request.headers.addAll(baseHeaders); final request = await httpClient.openUrl(method, uri);
request.headers.addAll(headers); for (final header in {...baseHeaders, ...headers}.entries) {
request.headers.add(header.key, header.value);
}
if (body != null) { if (body != null) {
request.bodyBytes = body.toList(); request.add(body.toList());
}
if (cookieJar != null) {
request.cookies.addAll(await cookieJar!.loadForRequest(uri));
} }
final response = await http.Response.fromStream(await request.send()); final response = await request.close();
if (cookieJar != null) {
await cookieJar!.saveFromResponse(uri, response.cookies);
}
final responseHeaders = <String, String>{};
response.headers.forEach((final name, final values) {
responseHeaders[name] = values.last;
});
return Response( return Response(
response.statusCode, response.statusCode,
response.headers, responseHeaders,
response.bodyBytes, await response.bodyBytes,
); );
} }
} }

2
packages/nextcloud/pubspec.yaml

@ -5,9 +5,9 @@ environment:
sdk: '>=2.18.0 <3.0.0' sdk: '>=2.18.0 <3.0.0'
dependencies: dependencies:
cookie_jar: ^3.0.1
crypto: ^3.0.1 crypto: ^3.0.1
crypton: ^2.0.5 crypton: ^2.0.5
http: ^0.13.4
intl: ^0.17.0 intl: ^0.17.0
json_annotation: ^4.7.0 json_annotation: ^4.7.0
meta: ^1.7.0 meta: ^1.7.0

Loading…
Cancel
Save