Browse Source

Merge pull request #418 from provokateurin/refactor/dynamite-runtime

Refactor/dynamite runtime
pull/424/head
Kate 1 year ago committed by GitHub
parent
commit
2bbd4cd112
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 136
      packages/dynamite/dynamite/lib/src/openapi_builder.dart
  2. 28
      packages/dynamite/dynamite_runtime/lib/src/http_client.dart
  3. 2
      packages/neon/neon/lib/src/blocs/user_statuses.dart
  4. 2
      packages/neon/neon/lib/src/utils/request_manager.dart
  5. 2
      packages/neon/neon/lib/src/widgets/exception.dart
  6. 2
      packages/neon/neon_notes/lib/utils/exception_handler.dart
  7. 2
      packages/nextcloud/lib/src/client.dart
  8. 323
      packages/nextcloud/lib/src/nextcloud.openapi.dart
  9. 2
      packages/nextcloud/test/core_test.dart
  10. 2
      packages/nextcloud/test/notes_test.dart
  11. 4
      packages/nextcloud/test/webdav_test.dart

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

@ -173,137 +173,6 @@ class OpenAPIBuilder implements Builder {
).accept(emitter).toString(),
];
if (spec.components?.securitySchemes != null) {
for (final name in spec.components!.securitySchemes!.keys) {
final securityScheme = spec.components!.securitySchemes![name]!;
switch (securityScheme.type) {
case 'http':
switch (securityScheme.scheme) {
case 'basic':
output.add(
Class(
(final b) {
final fields = ['username', 'password'];
b
..name = '${prefix}HttpBasicAuthentication'
..extend = refer('DynamiteAuthentication')
..constructors.add(
Constructor(
(final b) => b
..optionalParameters.addAll(
fields.map(
(final name) => Parameter(
(final b) => b
..name = name
..toThis = true
..named = true
..required = true,
),
),
),
),
)
..fields.addAll(
fields.map(
(final name) => Field(
(final b) => b
..name = name
..type = refer('String')
..modifier = FieldModifier.final$,
),
),
)
..methods.addAll([
Method(
(final b) => b
..name = 'id'
..annotations.add(refer('override'))
..type = MethodType.getter
..lambda = true
..returns = refer('String')
..body = Code("'$name'"),
),
Method(
(final b) => b
..name = 'headers'
..annotations.add(refer('override'))
..type = MethodType.getter
..returns = refer('Map<String, String>')
..lambda = true
..body = const Code(r'''
{
'Authorization': 'Basic ${base64.encode(utf8.encode('$username:$password'))}',
}
'''),
),
]);
},
).accept(emitter).toString(),
);
continue;
case 'bearer':
output.add(
Class(
(final b) {
b
..name = '${prefix}HttpBearerAuthentication'
..extend = refer('DynamiteAuthentication')
..constructors.add(
Constructor(
(final b) => b
..optionalParameters.add(
Parameter(
(final b) => b
..name = 'token'
..toThis = true
..named = true
..required = true,
),
),
),
)
..fields.addAll([
Field(
(final b) => b
..name = 'token'
..type = refer('String')
..modifier = FieldModifier.final$,
),
])
..methods.addAll([
Method(
(final b) => b
..name = 'id'
..annotations.add(refer('override'))
..type = MethodType.getter
..lambda = true
..returns = refer('String')
..body = Code("'$name'"),
),
Method(
(final b) => b
..name = 'headers'
..annotations.add(refer('override'))
..type = MethodType.getter
..returns = refer('Map<String, String>')
..lambda = true
..body = const Code(r'''
{
'Authorization': 'Bearer $token',
}
'''),
),
]);
},
).accept(emitter).toString(),
);
continue;
}
}
throw Exception('Can not work with security scheme ${securityScheme.toJson()}');
}
}
for (final tag in tags) {
final isRootClient = tag == null;
final paths = <String, PathItem>{};
@ -484,9 +353,10 @@ class OpenAPIBuilder implements Builder {
if (requirement.keys.isEmpty) {
continue;
}
final securityScheme = spec.components!.securitySchemes![requirement.keys.single]!;
code.write('''
if (${isRootClient ? '' : 'rootClient.'}authentications.map((final a) => a.id).contains('${requirement.keys.single}')) {
headers.addAll(${isRootClient ? '' : 'rootClient.'}authentications.singleWhere((final a) => a.id == '${requirement.keys.single}').headers);
if (${isRootClient ? '' : 'rootClient.'}authentications.where((final a) => a.type == '${securityScheme.type}' && a.scheme == '${securityScheme.scheme}').isNotEmpty) {
headers.addAll(${isRootClient ? '' : 'rootClient.'}authentications.singleWhere((final a) => a.type == '${securityScheme.type}' && a.scheme == '${securityScheme.scheme}').headers);
}
''');
if (!isOptionalSecurity ||

28
packages/dynamite/dynamite_runtime/lib/src/http_client.dart

@ -59,7 +59,8 @@ class DynamiteApiException implements Exception {
}
abstract class DynamiteAuthentication {
String get id;
String get type;
String get scheme;
Map<String, String> get headers;
}
@ -74,13 +75,36 @@ class DynamiteHttpBasicAuthentication extends DynamiteAuthentication {
final String password;
@override
String get id => 'basic_auth';
String type = 'http';
@override
String scheme = 'basic';
@override
Map<String, String> get headers => {
'Authorization': 'Basic ${base64.encode(utf8.encode('$username:$password'))}',
};
}
class DynamiteHttpBearerAuthentication extends DynamiteAuthentication {
DynamiteHttpBearerAuthentication({
required this.token,
});
final String token;
@override
String type = 'http';
@override
String scheme = 'bearer';
@override
Map<String, String> get headers => {
'Authorization': 'Bearer $token',
};
}
class DynamiteClient {
DynamiteClient(
this.baseURL, {

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

@ -63,7 +63,7 @@ class UserStatusesBloc extends InteractiveBloc implements UserStatusesBlocEvents
_updateStatus(username, Result.success(data));
} catch (e, s) {
if (e is NextcloudApiException && (e.statusCode == 404 || e.statusCode == 204)) {
if (e is DynamiteApiException && (e.statusCode == 404 || e.statusCode == 204)) {
_updateStatus(username, Result.success(null));
return;
}

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

@ -95,7 +95,7 @@ class RequestManager {
} catch (e, s) {
debugPrint(e.toString());
debugPrint(s.toString());
if (e is NextcloudApiException && e.statusCode >= 500 && retries < 3) {
if (e is DynamiteApiException && e.statusCode >= 500 && retries < 3) {
debugPrint('Retrying...');
await _wrap(
clientID,

2
packages/neon/neon/lib/src/widgets/exception.dart

@ -127,7 +127,7 @@ class NeonException extends StatelessWidget {
);
}
if (exception is NextcloudApiException) {
if (exception is DynamiteApiException) {
if (exception.statusCode == 401) {
return _ExceptionDetails(
text: AppLocalizations.of(context).errorCredentialsForAccountNoLongerMatch,

2
packages/neon/neon_notes/lib/utils/exception_handler.dart

@ -1,7 +1,7 @@
part of '../neon_notes.dart';
void handleNotesException(final BuildContext context, final Object error) {
if (error is NextcloudApiException && error.statusCode == 412) {
if (error is DynamiteApiException && error.statusCode == 412) {
NeonException.showSnackbar(context, AppLocalizations.of(context).errorChangedOnServer);
} else {
NeonException.showSnackbar(context, error);

2
packages/nextcloud/lib/src/client.dart

@ -22,7 +22,7 @@ class NextcloudClient extends openapi.NextcloudClient {
userAgent: userAgentOverride ?? appType.userAgent,
authentications: [
if (loginName != null && password != null) ...[
openapi.NextcloudHttpBasicAuthentication(
openapi.DynamiteHttpBasicAuthentication(
username: loginName,
password: password,
),

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

@ -54,24 +54,6 @@ class NextcloudApiException extends DynamiteApiException {
String toString() => 'NextcloudApiException(statusCode: $statusCode, headers: $headers, body: $body)';
}
class NextcloudHttpBasicAuthentication extends DynamiteAuthentication {
NextcloudHttpBasicAuthentication({
required this.username,
required this.password,
});
final String username;
final String password;
@override
String get id => 'basic_auth';
@override
Map<String, String> get headers => {
'Authorization': 'Basic ${base64.encode(utf8.encode('$username:$password'))}',
};
}
class NextcloudClient extends DynamiteClient {
NextcloudClient(
super.baseURL, {
@ -125,8 +107,9 @@ class NextcloudCoreClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -152,8 +135,9 @@ class NextcloudCoreClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -231,8 +215,9 @@ class NextcloudCoreClient {
'Accept': 'image/png',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -276,8 +261,9 @@ class NextcloudCoreClient {
'Accept': 'image/png',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -305,8 +291,9 @@ class NextcloudCoreClient {
'Accept': 'image/png',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -338,8 +325,9 @@ class NextcloudCoreClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -375,8 +363,9 @@ class NextcloudCoreClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -405,8 +394,9 @@ class NextcloudNewsClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -432,8 +422,9 @@ class NextcloudNewsClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -459,8 +450,9 @@ class NextcloudNewsClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -488,8 +480,9 @@ class NextcloudNewsClient {
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -512,8 +505,9 @@ class NextcloudNewsClient {
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -538,8 +532,9 @@ class NextcloudNewsClient {
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -564,8 +559,9 @@ class NextcloudNewsClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -594,8 +590,9 @@ class NextcloudNewsClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -623,8 +620,9 @@ class NextcloudNewsClient {
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -649,8 +647,9 @@ class NextcloudNewsClient {
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -678,8 +677,9 @@ class NextcloudNewsClient {
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -705,8 +705,9 @@ class NextcloudNewsClient {
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -738,8 +739,9 @@ class NextcloudNewsClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -787,8 +789,9 @@ class NextcloudNewsClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -821,8 +824,9 @@ class NextcloudNewsClient {
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -844,8 +848,9 @@ class NextcloudNewsClient {
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -867,8 +872,9 @@ class NextcloudNewsClient {
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -890,8 +896,9 @@ class NextcloudNewsClient {
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -928,8 +935,9 @@ class NextcloudNotesClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -979,8 +987,9 @@ class NextcloudNotesClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1023,8 +1032,9 @@ class NextcloudNotesClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1063,8 +1073,9 @@ class NextcloudNotesClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1107,8 +1118,9 @@ class NextcloudNotesClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1132,8 +1144,9 @@ class NextcloudNotesClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1159,8 +1172,9 @@ class NextcloudNotesClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1198,8 +1212,9 @@ class NextcloudNotificationsClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1225,8 +1240,9 @@ class NextcloudNotificationsClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1249,8 +1265,9 @@ class NextcloudNotificationsClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1277,8 +1294,9 @@ class NextcloudNotificationsClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1307,8 +1325,9 @@ class NextcloudNotificationsClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1337,8 +1356,9 @@ class NextcloudNotificationsClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1365,8 +1385,9 @@ class NextcloudNotificationsClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1401,8 +1422,9 @@ class NextcloudProvisioningApiClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1428,8 +1450,9 @@ class NextcloudProvisioningApiClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1463,8 +1486,9 @@ class NextcloudUnifiedPushProviderClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1495,8 +1519,9 @@ class NextcloudUnifiedPushProviderClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1526,8 +1551,9 @@ class NextcloudUnifiedPushProviderClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1559,8 +1585,9 @@ class NextcloudUnifiedPushProviderClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1590,8 +1617,9 @@ class NextcloudUnifiedPushProviderClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1622,8 +1650,9 @@ class NextcloudUnifiedPushProviderClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1654,8 +1683,9 @@ class NextcloudUnifiedPushProviderClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1685,8 +1715,9 @@ class NextcloudUnifiedPushProviderClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1714,8 +1745,9 @@ class NextcloudUnifiedPushProviderClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1743,8 +1775,9 @@ class NextcloudUnifiedPushProviderClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1771,8 +1804,9 @@ class NextcloudUnifiedPushProviderClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1804,8 +1838,9 @@ class NextcloudUserStatusClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1831,8 +1866,9 @@ class NextcloudUserStatusClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1859,8 +1895,9 @@ class NextcloudUserStatusClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1886,8 +1923,9 @@ class NextcloudUserStatusClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1917,8 +1955,9 @@ class NextcloudUserStatusClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1952,8 +1991,9 @@ class NextcloudUserStatusClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -1986,8 +2026,9 @@ class NextcloudUserStatusClient {
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -2010,8 +2051,9 @@ class NextcloudUserStatusClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}
@ -2037,8 +2079,9 @@ class NextcloudUserStatusClient {
'Accept': 'application/json',
};
Uint8List? body;
if (rootClient.authentications.map((final a) => a.id).contains('basic_auth')) {
headers.addAll(rootClient.authentications.singleWhere((final a) => a.id == 'basic_auth').headers);
if (rootClient.authentications.where((final a) => a.type == 'http' && a.scheme == 'basic').isNotEmpty) {
headers
.addAll(rootClient.authentications.singleWhere((final a) => a.type == 'http' && a.scheme == 'basic').headers);
} else {
throw Exception('Missing authentication for basic_auth'); // coverage:ignore-line
}

2
packages/nextcloud/test/core_test.dart

@ -123,7 +123,7 @@ Future run(final DockerImage image) async {
await client.core.deleteAppPassword();
expect(
() => client.core.getCapabilities(),
throwsA(predicate((final e) => (e! as NextcloudApiException).statusCode == 401)),
throwsA(predicate((final e) => (e! as DynamiteApiException).statusCode == 401)),
);
});
});

2
packages/nextcloud/test/notes_test.dart

@ -96,7 +96,7 @@ Future run(final DockerImage image) async {
title: 'c',
ifMatch: '"${response.etag}"',
),
throwsA(predicate((final e) => (e! as NextcloudApiException).statusCode == 412)),
throwsA(predicate((final e) => (e! as DynamiteApiException).statusCode == 412)),
);
});

4
packages/nextcloud/test/webdav_test.dart

@ -170,7 +170,7 @@ Future run(final DockerImage image) async {
expect(
() => client.webdav.copy('1.txt', '2.txt'),
throwsA(predicate((final e) => (e! as NextcloudApiException).statusCode == 412)),
throwsA(predicate((final e) => (e! as DynamiteApiException).statusCode == 412)),
);
});
@ -203,7 +203,7 @@ Future run(final DockerImage image) async {
expect(
() => client.webdav.move('1.txt', '2.txt'),
throwsA(predicate((final e) => (e! as NextcloudApiException).statusCode == 412)),
throwsA(predicate((final e) => (e! as DynamiteApiException).statusCode == 412)),
);
});

Loading…
Cancel
Save