Browse Source

Merge pull request #62 from jld3103/feature/extended-version-checking-support

Implement extended version checking
pull/49/head
jld3103 2 years ago committed by GitHub
parent
commit
348066760a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 8
      packages/dynamite/lib/src/openapi_builder.dart
  2. 9
      packages/neon/lib/l10n/en.arb
  3. 12
      packages/neon/lib/l10n/localizations.dart
  4. 7
      packages/neon/lib/l10n/localizations_en.dart
  5. 2
      packages/neon/lib/src/blocs/apps.dart
  6. 77
      packages/neon/lib/src/pages/home/home.dart
  7. 7
      packages/neon/pubspec.lock
  8. 2
      packages/nextcloud/lib/nextcloud.dart
  9. 5
      packages/nextcloud/lib/src/client.dart
  10. 159
      packages/nextcloud/lib/src/nextcloud.openapi.dart
  11. 8
      packages/nextcloud/lib/src/nextcloud.openapi.g.dart
  12. 28
      packages/nextcloud/lib/src/nextcloud.openapi.json
  13. 32
      packages/nextcloud/lib/src/version_supported.dart
  14. 18
      packages/nextcloud/lib/src/webdav/client.dart
  15. 1
      packages/nextcloud/pubspec.yaml
  16. 6
      packages/nextcloud/test/core_test.dart
  17. 5
      packages/nextcloud/test/news_test.dart
  18. 5
      packages/nextcloud/test/notes_test.dart
  19. 28
      specs/news.json

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

@ -732,7 +732,7 @@ class OpenAPIBuilder implements Builder {
], ],
Method( Method(
(final b) => b (final b) => b
..name = '_doRequest' ..name = 'doRequest'
..returns = refer('Future<Response>') ..returns = refer('Future<Response>')
..modifier = MethodModifier.async ..modifier = MethodModifier.async
..requiredParameters.addAll([ ..requiredParameters.addAll([
@ -779,7 +779,7 @@ class OpenAPIBuilder implements Builder {
..fields.add( ..fields.add(
Field( Field(
(final b) => b (final b) => b
..name = '_client' ..name = 'rootClient'
..type = refer('Client') ..type = refer('Client')
..modifier = FieldModifier.final$, ..modifier = FieldModifier.final$,
), ),
@ -789,7 +789,7 @@ class OpenAPIBuilder implements Builder {
(final b) => b.requiredParameters.add( (final b) => b.requiredParameters.add(
Parameter( Parameter(
(final b) => b (final b) => b
..name = '_client' ..name = 'rootClient'
..toThis = true, ..toThis = true,
), ),
), ),
@ -940,7 +940,7 @@ class OpenAPIBuilder implements Builder {
code.write( code.write(
''' '''
final response = await ${isRootClient ? '' : '_client.'}_doRequest( final response = await ${isRootClient ? '' : 'rootClient.'}doRequest(
'$httpMethod', '$httpMethod',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,

9
packages/neon/lib/l10n/en.arb

@ -29,11 +29,11 @@
} }
} }
}, },
"errorUnsupportedNextcloudVersion": "Sorry this Nextcloud instance version is not supported. You need at least version {version} of Nextcloud.", "errorUnsupportedVersion": "Sorry, this Nextcloud {name} version is not supported.",
"@errorUnsupportedNextcloudVersion" : { "@errorUnsupportedVersion" : {
"placeholders": { "placeholders": {
"version": { "name": {
"type": "int" "type": "String"
} }
} }
}, },
@ -122,6 +122,7 @@
"accountOptionsInitialApp": "App to show initially", "accountOptionsInitialApp": "App to show initially",
"accountOptionsAutomatic": "Automatic", "accountOptionsAutomatic": "Automatic",
"licenses": "Licenses", "licenses": "Licenses",
"coreName": "Server",
"filesName": "Files", "filesName": "Files",
"filesUploadFiles": "Upload files", "filesUploadFiles": "Upload files",
"filesUploadImages": "Upload images", "filesUploadImages": "Upload images",

12
packages/neon/lib/l10n/localizations.dart

@ -179,11 +179,11 @@ abstract class AppLocalizations {
/// **'Permission for {name} is missing'** /// **'Permission for {name} is missing'**
String errorMissingPermission(String name); String errorMissingPermission(String name);
/// No description provided for @errorUnsupportedNextcloudVersion. /// No description provided for @errorUnsupportedVersion.
/// ///
/// In en, this message translates to: /// In en, this message translates to:
/// **'Sorry this Nextcloud instance version is not supported. You need at least version {version} of Nextcloud.'** /// **'Sorry, this Nextcloud {name} version is not supported.'**
String errorUnsupportedNextcloudVersion(int version); String errorUnsupportedVersion(String name);
/// No description provided for @errorEmptyField. /// No description provided for @errorEmptyField.
/// ///
@ -533,6 +533,12 @@ abstract class AppLocalizations {
/// **'Licenses'** /// **'Licenses'**
String get licenses; String get licenses;
/// No description provided for @coreName.
///
/// In en, this message translates to:
/// **'Server'**
String get coreName;
/// No description provided for @filesName. /// No description provided for @filesName.
/// ///
/// In en, this message translates to: /// In en, this message translates to:

7
packages/neon/lib/l10n/localizations_en.dart

@ -57,8 +57,8 @@ class AppLocalizationsEn extends AppLocalizations {
} }
@override @override
String errorUnsupportedNextcloudVersion(int version) { String errorUnsupportedVersion(String name) {
return 'Sorry this Nextcloud instance version is not supported. You need at least version $version of Nextcloud.'; return 'Sorry, this Nextcloud $name version is not supported.';
} }
@override @override
@ -243,6 +243,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override @override
String get licenses => 'Licenses'; String get licenses => 'Licenses';
@override
String get coreName => 'Server';
@override @override
String get filesName => 'Files'; String get filesName => 'Files';

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

@ -54,7 +54,7 @@ class AppsBloc extends $AppsBloc {
); );
} else if (result is ResultCached && result.data != null) { } else if (result is ResultCached && result.data != null) {
_appImplementationsSubject.add( _appImplementationsSubject.add(
Result.success(_filteredAppImplementations((result as ResultCached<List<NextcloudApp>>).data)), ResultCached(_filteredAppImplementations((result as ResultCached<List<NextcloudApp>>).data)),
); );
} }

77
packages/neon/lib/src/pages/home/home.dart

@ -54,26 +54,43 @@ class _HomePageState extends State<HomePage> with tray.TrayListener, WindowListe
// ignore cached version and prevent duplicate dialogs // ignore cached version and prevent duplicate dialogs
if (result is ResultSuccess) { if (result is ResultSuccess) {
const requiredMajorVersion = 24; _appsBloc.appImplementations.listen((final appsResult) async {
if (result.data!.version!.major! < requiredMajorVersion) { // ignore cached version and prevent duplicate dialogs
await showDialog( if (appsResult is ResultSuccess) {
context: context, for (final id in [
builder: (final context) => AlertDialog( 'core',
title: Text(AppLocalizations.of(context).errorUnsupportedNextcloudVersion(requiredMajorVersion)), ...appsResult.data!.map((final a) => a.id),
actions: [ ]) {
ElevatedButton( try {
style: ElevatedButton.styleFrom( bool? supported;
backgroundColor: Colors.red, switch (id) {
), case 'core':
onPressed: () { supported = await widget.account.client.core.isSupported(result.data!);
Navigator.of(context).pop(); break;
}, case 'news':
child: Text(AppLocalizations.of(context).close), supported = await widget.account.client.news.isSupported();
), break;
], case 'notes':
), supported = await widget.account.client.notes.isSupported(result.data!);
); break;
} }
if (!(supported ?? true)) {
if (!mounted) {
return;
}
await _showUnsupportedVersion(
id == 'core'
? AppLocalizations.of(context).coreName
: appsResult.data!.singleWhere((final a) => a.id == id).name(context),
);
}
} catch (e, s) {
debugPrint(e.toString());
debugPrint(s.toString());
}
}
}
});
} }
} }
}); });
@ -259,6 +276,26 @@ class _HomePageState extends State<HomePage> with tray.TrayListener, WindowListe
} }
} }
Future _showUnsupportedVersion(final String appName) async {
await showDialog(
context: context,
builder: (final context) => AlertDialog(
title: Text(AppLocalizations.of(context).errorUnsupportedVersion(appName)),
actions: [
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.red,
),
onPressed: () {
Navigator.of(context).pop();
},
child: Text(AppLocalizations.of(context).close),
),
],
),
);
}
@override @override
void dispose() { void dispose() {
_capabilitiesBloc.dispose(); _capabilitiesBloc.dispose();

7
packages/neon/pubspec.lock

@ -1226,6 +1226,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.2" version: "2.1.2"
version:
dependency: transitive
description:
name: version
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.2"
vm_service: vm_service:
dependency: transitive dependency: transitive
description: description:

2
packages/nextcloud/lib/nextcloud.dart

@ -7,6 +7,7 @@ import 'dart:typed_data';
import 'package:crypto/crypto.dart'; import 'package:crypto/crypto.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import 'package:nextcloud/nextcloud.dart'; import 'package:nextcloud/nextcloud.dart';
import 'package:version/version.dart';
import 'package:xml/xml.dart' as xml; import 'package:xml/xml.dart' as xml;
export 'package:crypton/crypton.dart' show RSAKeypair, RSAPublicKey, RSAPrivateKey; export 'package:crypton/crypton.dart' show RSAKeypair, RSAPublicKey, RSAPrivateKey;
@ -16,6 +17,7 @@ export 'src/nextcloud.openapi.dart';
part 'src/app_type.dart'; part 'src/app_type.dart';
part 'src/client.dart'; part 'src/client.dart';
part 'src/helpers.dart'; part 'src/helpers.dart';
part 'src/version_supported.dart';
part 'src/webdav/client.dart'; part 'src/webdav/client.dart';
part 'src/webdav/file.dart'; part 'src/webdav/file.dart';
part 'src/webdav/props.dart'; part 'src/webdav/props.dart';

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

@ -41,9 +41,8 @@ class NextcloudClient extends Client {
} }
return _webdav = WebDavClient( return _webdav = WebDavClient(
super.baseURL, this,
basePath: '/remote.php/dav/files/${(super.authentication! as HttpBasicAuthentication).username}', '/remote.php/dav/files/${(super.authentication! as HttpBasicAuthentication).username}',
baseHeaders: super.baseHeaders,
); );
} }
} }

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

@ -88,7 +88,7 @@ class Client {
NotificationsClient get notifications => NotificationsClient(this); NotificationsClient get notifications => NotificationsClient(this);
ProvisioningApiClient get provisioningApi => ProvisioningApiClient(this); ProvisioningApiClient get provisioningApi => ProvisioningApiClient(this);
UserStatusClient get userStatus => UserStatusClient(this); UserStatusClient get userStatus => UserStatusClient(this);
Future<Response> _doRequest( Future<Response> doRequest(
String method, String method,
String path, String path,
Map<String, String> headers, Map<String, String> headers,
@ -1342,16 +1342,16 @@ class CoreLoginFlowResult {
} }
class CoreClient { class CoreClient {
CoreClient(this._client); CoreClient(this.rootClient);
final Client _client; final Client rootClient;
Future<CoreServerStatus> getStatus() async { Future<CoreServerStatus> getStatus() async {
var path = '/status.php'; var path = '/status.php';
final queryParameters = <String, dynamic>{}; final queryParameters = <String, dynamic>{};
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1368,7 +1368,7 @@ class CoreClient {
final queryParameters = <String, dynamic>{}; final queryParameters = <String, dynamic>{};
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1385,7 +1385,7 @@ class CoreClient {
final queryParameters = <String, dynamic>{}; final queryParameters = <String, dynamic>{};
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1402,7 +1402,7 @@ class CoreClient {
final queryParameters = <String, dynamic>{}; final queryParameters = <String, dynamic>{};
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
final response = await _client._doRequest( final response = await rootClient.doRequest(
'post', 'post',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1420,7 +1420,7 @@ class CoreClient {
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
queryParameters['token'] = token.toString(); queryParameters['token'] = token.toString();
final response = await _client._doRequest( final response = await rootClient.doRequest(
'post', 'post',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1450,7 +1450,7 @@ class CoreClient {
queryParameters['a'] = a.toString(); queryParameters['a'] = a.toString();
queryParameters['forceIcon'] = forceIcon.toString(); queryParameters['forceIcon'] = forceIcon.toString();
queryParameters['mode'] = mode.toString(); queryParameters['mode'] = mode.toString();
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1472,7 +1472,7 @@ class CoreClient {
Uint8List? body; Uint8List? body;
path = path.replaceAll('{userId}', Uri.encodeQueryComponent(userId.toString())); path = path.replaceAll('{userId}', Uri.encodeQueryComponent(userId.toString()));
path = path.replaceAll('{size}', Uri.encodeQueryComponent(size.toString())); path = path.replaceAll('{size}', Uri.encodeQueryComponent(size.toString()));
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1485,6 +1485,17 @@ class CoreClient {
} }
} }
@JsonSerializable()
class GetSupportedApiVersions {
GetSupportedApiVersions({this.apiLevels});
factory GetSupportedApiVersions.fromJson(Map<String, dynamic> json) => _$GetSupportedApiVersionsFromJson(json);
final List<String>? apiLevels;
Map<String, dynamic> toJson() => _$GetSupportedApiVersionsToJson(this);
}
@JsonSerializable() @JsonSerializable()
class NewsArticle { class NewsArticle {
NewsArticle({ NewsArticle({
@ -1669,16 +1680,33 @@ class NewsListArticles {
} }
class NewsClient { class NewsClient {
NewsClient(this._client); NewsClient(this.rootClient);
final Client rootClient;
final Client _client; Future<GetSupportedApiVersions> getSupportedApiVersions() async {
var path = '/apps/news/api';
final queryParameters = <String, dynamic>{};
final headers = <String, String>{};
Uint8List? body;
final response = await rootClient.doRequest(
'get',
Uri(path: path, queryParameters: queryParameters).toString(),
headers,
body,
);
if (response.statusCode == 200) {
return GetSupportedApiVersions.fromJson(json.decode(utf8.decode(response.body)) as Map<String, dynamic>);
}
throw ApiException.fromResponse(response);
}
Future<NewsListFolders> listFolders() async { Future<NewsListFolders> listFolders() async {
var path = '/apps/news/api/v1-2/folders'; var path = '/apps/news/api/v1-2/folders';
final queryParameters = <String, dynamic>{}; final queryParameters = <String, dynamic>{};
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1696,7 +1724,7 @@ class NewsClient {
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
queryParameters['name'] = name.toString(); queryParameters['name'] = name.toString();
final response = await _client._doRequest( final response = await rootClient.doRequest(
'post', 'post',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1718,7 +1746,7 @@ class NewsClient {
Uint8List? body; Uint8List? body;
path = path.replaceAll('{folderId}', Uri.encodeQueryComponent(folderId.toString())); path = path.replaceAll('{folderId}', Uri.encodeQueryComponent(folderId.toString()));
queryParameters['name'] = name.toString(); queryParameters['name'] = name.toString();
final response = await _client._doRequest( final response = await rootClient.doRequest(
'put', 'put',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1736,7 +1764,7 @@ class NewsClient {
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
path = path.replaceAll('{folderId}', Uri.encodeQueryComponent(folderId.toString())); path = path.replaceAll('{folderId}', Uri.encodeQueryComponent(folderId.toString()));
final response = await _client._doRequest( final response = await rootClient.doRequest(
'delete', 'delete',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1758,7 +1786,7 @@ class NewsClient {
Uint8List? body; Uint8List? body;
path = path.replaceAll('{folderId}', Uri.encodeQueryComponent(folderId.toString())); path = path.replaceAll('{folderId}', Uri.encodeQueryComponent(folderId.toString()));
queryParameters['newestItemId'] = newestItemId.toString(); queryParameters['newestItemId'] = newestItemId.toString();
final response = await _client._doRequest( final response = await rootClient.doRequest(
'put', 'put',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1775,7 +1803,7 @@ class NewsClient {
final queryParameters = <String, dynamic>{}; final queryParameters = <String, dynamic>{};
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1799,7 +1827,7 @@ class NewsClient {
if (folderId != null) { if (folderId != null) {
queryParameters['folderId'] = folderId.toString(); queryParameters['folderId'] = folderId.toString();
} }
final response = await _client._doRequest( final response = await rootClient.doRequest(
'post', 'post',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1817,7 +1845,7 @@ class NewsClient {
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
path = path.replaceAll('{feedId}', Uri.encodeQueryComponent(feedId.toString())); path = path.replaceAll('{feedId}', Uri.encodeQueryComponent(feedId.toString()));
final response = await _client._doRequest( final response = await rootClient.doRequest(
'delete', 'delete',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1841,7 +1869,7 @@ class NewsClient {
if (folderId != null) { if (folderId != null) {
queryParameters['folderId'] = folderId.toString(); queryParameters['folderId'] = folderId.toString();
} }
final response = await _client._doRequest( final response = await rootClient.doRequest(
'put', 'put',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1863,7 +1891,7 @@ class NewsClient {
Uint8List? body; Uint8List? body;
path = path.replaceAll('{feedId}', Uri.encodeQueryComponent(feedId.toString())); path = path.replaceAll('{feedId}', Uri.encodeQueryComponent(feedId.toString()));
queryParameters['feedTitle'] = feedTitle.toString(); queryParameters['feedTitle'] = feedTitle.toString();
final response = await _client._doRequest( final response = await rootClient.doRequest(
'put', 'put',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1885,7 +1913,7 @@ class NewsClient {
Uint8List? body; Uint8List? body;
path = path.replaceAll('{feedId}', Uri.encodeQueryComponent(feedId.toString())); path = path.replaceAll('{feedId}', Uri.encodeQueryComponent(feedId.toString()));
queryParameters['newestItemId'] = newestItemId.toString(); queryParameters['newestItemId'] = newestItemId.toString();
final response = await _client._doRequest( final response = await rootClient.doRequest(
'put', 'put',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1915,7 +1943,7 @@ class NewsClient {
queryParameters['batchSize'] = batchSize.toString(); queryParameters['batchSize'] = batchSize.toString();
queryParameters['offset'] = offset.toString(); queryParameters['offset'] = offset.toString();
queryParameters['oldestFirst'] = oldestFirst.toString(); queryParameters['oldestFirst'] = oldestFirst.toString();
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1939,7 +1967,7 @@ class NewsClient {
queryParameters['type'] = type.toString(); queryParameters['type'] = type.toString();
queryParameters['id'] = id.toString(); queryParameters['id'] = id.toString();
queryParameters['lastModified'] = lastModified.toString(); queryParameters['lastModified'] = lastModified.toString();
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1957,7 +1985,7 @@ class NewsClient {
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
path = path.replaceAll('{itemId}', Uri.encodeQueryComponent(itemId.toString())); path = path.replaceAll('{itemId}', Uri.encodeQueryComponent(itemId.toString()));
final response = await _client._doRequest( final response = await rootClient.doRequest(
'put', 'put',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1975,7 +2003,7 @@ class NewsClient {
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
path = path.replaceAll('{itemId}', Uri.encodeQueryComponent(itemId.toString())); path = path.replaceAll('{itemId}', Uri.encodeQueryComponent(itemId.toString()));
final response = await _client._doRequest( final response = await rootClient.doRequest(
'put', 'put',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -1997,7 +2025,7 @@ class NewsClient {
Uint8List? body; Uint8List? body;
path = path.replaceAll('{feedId}', Uri.encodeQueryComponent(feedId.toString())); path = path.replaceAll('{feedId}', Uri.encodeQueryComponent(feedId.toString()));
path = path.replaceAll('{guidHash}', Uri.encodeQueryComponent(guidHash.toString())); path = path.replaceAll('{guidHash}', Uri.encodeQueryComponent(guidHash.toString()));
final response = await _client._doRequest( final response = await rootClient.doRequest(
'put', 'put',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -2019,7 +2047,7 @@ class NewsClient {
Uint8List? body; Uint8List? body;
path = path.replaceAll('{feedId}', Uri.encodeQueryComponent(feedId.toString())); path = path.replaceAll('{feedId}', Uri.encodeQueryComponent(feedId.toString()));
path = path.replaceAll('{guidHash}', Uri.encodeQueryComponent(guidHash.toString())); path = path.replaceAll('{guidHash}', Uri.encodeQueryComponent(guidHash.toString()));
final response = await _client._doRequest( final response = await rootClient.doRequest(
'put', 'put',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -2112,9 +2140,9 @@ class NotesSettings {
} }
class NotesClient { class NotesClient {
NotesClient(this._client); NotesClient(this.rootClient);
final Client _client; final Client rootClient;
Future<List<NotesNote>> getNotes({ Future<List<NotesNote>> getNotes({
String? category, String? category,
@ -2140,7 +2168,7 @@ class NotesClient {
if (ifNoneMatch != null) { if (ifNoneMatch != null) {
headers['If-None-Match'] = ifNoneMatch.toString(); headers['If-None-Match'] = ifNoneMatch.toString();
} }
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -2170,7 +2198,7 @@ class NotesClient {
queryParameters['content'] = content.toString(); queryParameters['content'] = content.toString();
queryParameters['modified'] = modified.toString(); queryParameters['modified'] = modified.toString();
queryParameters['favorite'] = favorite.toString(); queryParameters['favorite'] = favorite.toString();
final response = await _client._doRequest( final response = await rootClient.doRequest(
'post', 'post',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -2196,7 +2224,7 @@ class NotesClient {
if (ifNoneMatch != null) { if (ifNoneMatch != null) {
headers['If-None-Match'] = ifNoneMatch.toString(); headers['If-None-Match'] = ifNoneMatch.toString();
} }
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -2238,7 +2266,7 @@ class NotesClient {
if (ifMatch != null) { if (ifMatch != null) {
headers['If-Match'] = ifMatch.toString(); headers['If-Match'] = ifMatch.toString();
} }
final response = await _client._doRequest( final response = await rootClient.doRequest(
'put', 'put',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -2256,7 +2284,7 @@ class NotesClient {
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
path = path.replaceAll('{id}', Uri.encodeQueryComponent(id.toString())); path = path.replaceAll('{id}', Uri.encodeQueryComponent(id.toString()));
final response = await _client._doRequest( final response = await rootClient.doRequest(
'delete', 'delete',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -2273,7 +2301,7 @@ class NotesClient {
final queryParameters = <String, dynamic>{}; final queryParameters = <String, dynamic>{};
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -2292,7 +2320,7 @@ class NotesClient {
Uint8List? body; Uint8List? body;
headers['Content-Type'] = 'application/json'; headers['Content-Type'] = 'application/json';
body = Uint8List.fromList(utf8.encode(json.encode((notesSettings as NotesSettings).toJson()))); body = Uint8List.fromList(utf8.encode(json.encode((notesSettings as NotesSettings).toJson())));
final response = await _client._doRequest( final response = await rootClient.doRequest(
'put', 'put',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -2524,16 +2552,16 @@ class NotificationsPushServerRegistration {
} }
class NotificationsClient { class NotificationsClient {
NotificationsClient(this._client); NotificationsClient(this.rootClient);
final Client _client; final Client rootClient;
Future<NotificationsListNotifications> listNotifications() async { Future<NotificationsListNotifications> listNotifications() async {
var path = '/ocs/v1.php/apps/notifications/api/v2/notifications'; var path = '/ocs/v1.php/apps/notifications/api/v2/notifications';
final queryParameters = <String, dynamic>{}; final queryParameters = <String, dynamic>{};
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -2550,7 +2578,7 @@ class NotificationsClient {
final queryParameters = <String, dynamic>{}; final queryParameters = <String, dynamic>{};
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
final response = await _client._doRequest( final response = await rootClient.doRequest(
'delete', 'delete',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -2568,7 +2596,7 @@ class NotificationsClient {
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
path = path.replaceAll('{id}', Uri.encodeQueryComponent(id.toString())); path = path.replaceAll('{id}', Uri.encodeQueryComponent(id.toString()));
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -2586,7 +2614,7 @@ class NotificationsClient {
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
path = path.replaceAll('{id}', Uri.encodeQueryComponent(id.toString())); path = path.replaceAll('{id}', Uri.encodeQueryComponent(id.toString()));
final response = await _client._doRequest( final response = await rootClient.doRequest(
'delete', 'delete',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -2610,7 +2638,7 @@ class NotificationsClient {
queryParameters['pushTokenHash'] = pushTokenHash.toString(); queryParameters['pushTokenHash'] = pushTokenHash.toString();
queryParameters['devicePublicKey'] = devicePublicKey.toString(); queryParameters['devicePublicKey'] = devicePublicKey.toString();
queryParameters['proxyServer'] = proxyServer.toString(); queryParameters['proxyServer'] = proxyServer.toString();
final response = await _client._doRequest( final response = await rootClient.doRequest(
'post', 'post',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -2628,7 +2656,7 @@ class NotificationsClient {
final queryParameters = <String, dynamic>{}; final queryParameters = <String, dynamic>{};
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
final response = await _client._doRequest( final response = await rootClient.doRequest(
'delete', 'delete',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -2652,7 +2680,7 @@ class NotificationsClient {
path = path.replaceAll('{userId}', Uri.encodeQueryComponent(userId.toString())); path = path.replaceAll('{userId}', Uri.encodeQueryComponent(userId.toString()));
queryParameters['shortMessage'] = shortMessage.toString(); queryParameters['shortMessage'] = shortMessage.toString();
queryParameters['longMessage'] = longMessage.toString(); queryParameters['longMessage'] = longMessage.toString();
final response = await _client._doRequest( final response = await rootClient.doRequest(
'post', 'post',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -2866,16 +2894,16 @@ class ProvisioningApiUser {
} }
class ProvisioningApiClient { class ProvisioningApiClient {
ProvisioningApiClient(this._client); ProvisioningApiClient(this.rootClient);
final Client _client; final Client rootClient;
Future<ProvisioningApiUser> getCurrentUser() async { Future<ProvisioningApiUser> getCurrentUser() async {
var path = '/ocs/v1.php/cloud/user'; var path = '/ocs/v1.php/cloud/user';
final queryParameters = <String, dynamic>{}; final queryParameters = <String, dynamic>{};
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -2893,7 +2921,7 @@ class ProvisioningApiClient {
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
path = path.replaceAll('{userId}', Uri.encodeQueryComponent(userId.toString())); path = path.replaceAll('{userId}', Uri.encodeQueryComponent(userId.toString()));
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -3321,16 +3349,16 @@ class UserStatusPredefinedStatuses {
} }
class UserStatusClient { class UserStatusClient {
UserStatusClient(this._client); UserStatusClient(this.rootClient);
final Client _client; final Client rootClient;
Future<UserStatusFindAllStatuses> findAllStatuses() async { Future<UserStatusFindAllStatuses> findAllStatuses() async {
var path = '/ocs/v1.php/apps/user_status/api/v1/statuses'; var path = '/ocs/v1.php/apps/user_status/api/v1/statuses';
final queryParameters = <String, dynamic>{}; final queryParameters = <String, dynamic>{};
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -3348,7 +3376,7 @@ class UserStatusClient {
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
path = path.replaceAll('{userId}', Uri.encodeQueryComponent(userId.toString())); path = path.replaceAll('{userId}', Uri.encodeQueryComponent(userId.toString()));
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -3365,7 +3393,7 @@ class UserStatusClient {
final queryParameters = <String, dynamic>{}; final queryParameters = <String, dynamic>{};
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -3383,7 +3411,7 @@ class UserStatusClient {
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
queryParameters['statusType'] = statusType.value.toString(); queryParameters['statusType'] = statusType.value.toString();
final response = await _client._doRequest( final response = await rootClient.doRequest(
'put', 'put',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -3407,7 +3435,7 @@ class UserStatusClient {
if (clearAt != null) { if (clearAt != null) {
queryParameters['clearAt'] = clearAt.toString(); queryParameters['clearAt'] = clearAt.toString();
} }
final response = await _client._doRequest( final response = await rootClient.doRequest(
'put', 'put',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -3437,7 +3465,7 @@ class UserStatusClient {
if (clearAt != null) { if (clearAt != null) {
queryParameters['clearAt'] = clearAt.toString(); queryParameters['clearAt'] = clearAt.toString();
} }
final response = await _client._doRequest( final response = await rootClient.doRequest(
'put', 'put',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -3454,7 +3482,7 @@ class UserStatusClient {
final queryParameters = <String, dynamic>{}; final queryParameters = <String, dynamic>{};
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
final response = await _client._doRequest( final response = await rootClient.doRequest(
'delete', 'delete',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -3471,7 +3499,7 @@ class UserStatusClient {
final queryParameters = <String, dynamic>{}; final queryParameters = <String, dynamic>{};
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
final response = await _client._doRequest( final response = await rootClient.doRequest(
'get', 'get',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -3489,7 +3517,7 @@ class UserStatusClient {
final headers = <String, String>{}; final headers = <String, String>{};
Uint8List? body; Uint8List? body;
queryParameters['status'] = status.value.toString(); queryParameters['status'] = status.value.toString();
final response = await _client._doRequest( final response = await rootClient.doRequest(
'put', 'put',
Uri(path: path, queryParameters: queryParameters).toString(), Uri(path: path, queryParameters: queryParameters).toString(),
headers, headers,
@ -3894,6 +3922,10 @@ final _deserializers = <Type, dynamic Function(dynamic)>{
List<CoreLoginFlowResult>: (final data) => (data as List) List<CoreLoginFlowResult>: (final data) => (data as List)
.map<CoreLoginFlowResult>((final e) => CoreLoginFlowResult.fromJson(e as Map<String, dynamic>)) .map<CoreLoginFlowResult>((final e) => CoreLoginFlowResult.fromJson(e as Map<String, dynamic>))
.toList(), .toList(),
GetSupportedApiVersions: (final data) => GetSupportedApiVersions.fromJson(data as Map<String, dynamic>),
List<GetSupportedApiVersions>: (final data) => (data as List)
.map<GetSupportedApiVersions>((final e) => GetSupportedApiVersions.fromJson(e as Map<String, dynamic>))
.toList(),
NewsListFolders: (final data) => NewsListFolders.fromJson(data as Map<String, dynamic>), NewsListFolders: (final data) => NewsListFolders.fromJson(data as Map<String, dynamic>),
List<NewsListFolders>: (final data) => List<NewsListFolders>: (final data) =>
(data as List).map<NewsListFolders>((final e) => NewsListFolders.fromJson(e as Map<String, dynamic>)).toList(), (data as List).map<NewsListFolders>((final e) => NewsListFolders.fromJson(e as Map<String, dynamic>)).toList(),
@ -4376,6 +4408,9 @@ final _serializers = <Type, dynamic Function(dynamic)>{
CoreLoginFlowResult: (final data) => (data as CoreLoginFlowResult).toJson(), CoreLoginFlowResult: (final data) => (data as CoreLoginFlowResult).toJson(),
List<CoreLoginFlowResult>: (final data) => List<CoreLoginFlowResult>: (final data) =>
(data as List<CoreLoginFlowResult>).map((final e) => (e as CoreLoginFlowResult).toJson()).toList(), (data as List<CoreLoginFlowResult>).map((final e) => (e as CoreLoginFlowResult).toJson()).toList(),
GetSupportedApiVersions: (final data) => (data as GetSupportedApiVersions).toJson(),
List<GetSupportedApiVersions>: (final data) =>
(data as List<GetSupportedApiVersions>).map((final e) => (e as GetSupportedApiVersions).toJson()).toList(),
NewsListFolders: (final data) => (data as NewsListFolders).toJson(), NewsListFolders: (final data) => (data as NewsListFolders).toJson(),
List<NewsListFolders>: (final data) => List<NewsListFolders>: (final data) =>
(data as List<NewsListFolders>).map((final e) => (e as NewsListFolders).toJson()).toList(), (data as List<NewsListFolders>).map((final e) => (e as NewsListFolders).toJson()).toList(),

8
packages/nextcloud/lib/src/nextcloud.openapi.g.dart

@ -1036,6 +1036,14 @@ Map<String, dynamic> _$CoreLoginFlowResultToJson(CoreLoginFlowResult instance) =
'appPassword': instance.appPassword, 'appPassword': instance.appPassword,
}; };
GetSupportedApiVersions _$GetSupportedApiVersionsFromJson(Map<String, dynamic> json) => GetSupportedApiVersions(
apiLevels: (json['apiLevels'] as List<dynamic>?)?.map((e) => e as String).toList(),
);
Map<String, dynamic> _$GetSupportedApiVersionsToJson(GetSupportedApiVersions instance) => <String, dynamic>{
'apiLevels': instance.apiLevels,
};
NewsArticle _$NewsArticleFromJson(Map<String, dynamic> json) => NewsArticle( NewsArticle _$NewsArticleFromJson(Map<String, dynamic> json) => NewsArticle(
id: json['id'] as int?, id: json['id'] as int?,
guid: json['guid'] as String?, guid: json['guid'] as String?,

28
packages/nextcloud/lib/src/nextcloud.openapi.json

@ -1819,6 +1819,34 @@
} }
} }
}, },
"/apps/news/api": {
"get": {
"operationId": "get-supported-api-versions",
"tags": [
"news"
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"apiLevels": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
}
}
}
},
"/apps/news/api/v1-2/folders": { "/apps/news/api/v1-2/folders": {
"get": { "get": {
"operationId": "list-folders", "operationId": "list-folders",

32
packages/nextcloud/lib/src/version_supported.dart

@ -0,0 +1,32 @@
part of '../nextcloud.dart';
// ignore: public_member_api_docs
extension CoreVersionSupported on CoreClient {
/// Checks if the app on the server is supported by the client
Future<bool> isSupported([final CoreServerCapabilitiesOcsData? capabilities]) async =>
(capabilities ?? (await rootClient.core.getCapabilities()).ocs!.data!).version!.major! == 24;
}
// ignore: public_member_api_docs
extension NewsVersionSupported on NewsClient {
/// Checks if the app on the server is supported by the client
Future<bool> isSupported() async {
final versions = await getSupportedApiVersions();
return versions.apiLevels!.contains('v1-2');
}
}
// ignore: public_member_api_docs
extension NotesVersionSupported on NotesClient {
/// Checks if the app on the server is supported by the client
Future<bool> isSupported([final CoreServerCapabilitiesOcsData? capabilities]) async =>
(capabilities ?? (await rootClient.core.getCapabilities()).ocs!.data!)
.capabilities!
.notes!
.apiVersion!
.map(Version.parse)
.where((final version) => version.major == 1)
.isNotEmpty;
}
// Notifications, ProvisioningApi, UserStatus and Webdav are shipped with the Nextcloud server, so their supported versions depend on the major version of the Nextcloud instance.

18
packages/nextcloud/lib/src/webdav/client.dart

@ -4,20 +4,16 @@ part of '../../nextcloud.dart';
class WebDavClient { class WebDavClient {
// ignore: public_member_api_docs // ignore: public_member_api_docs
WebDavClient( WebDavClient(
this.baseUrl, { this.rootClient,
this.basePath = '', this.basePath,
this.baseHeaders, );
});
/// Base URL of the server // ignore: public_member_api_docs
final String baseUrl; final Client rootClient;
/// Base path used on the server /// Base path used on the server
final String basePath; final String basePath;
/// Headers added to each request. Useful for authentication
final Map<String, String>? baseHeaders;
/// XML namespaces supported by this client. /// XML namespaces supported by this client.
/// ///
/// For Nextcloud namespaces see [WebDav/Requesting properties](https://docs.nextcloud.com/server/latest/developer_manual/client_apis/WebDAV/basic.html#requesting-properties). /// For Nextcloud namespaces see [WebDav/Requesting properties](https://docs.nextcloud.com/server/latest/developer_manual/client_apis/WebDAV/basic.html#requesting-properties).
@ -45,7 +41,7 @@ class WebDavClient {
..persistentConnection = true; ..persistentConnection = true;
for (final header in { for (final header in {
HttpHeaders.contentTypeHeader: 'application/xml', HttpHeaders.contentTypeHeader: 'application/xml',
if (baseHeaders != null) ...baseHeaders!, ...rootClient.baseHeaders,
if (headers != null) ...headers, if (headers != null) ...headers,
}.entries) { }.entries) {
request.headers.add(header.key, header.value); request.headers.add(header.key, header.value);
@ -75,7 +71,7 @@ class WebDavClient {
namespaces.putIfAbsent(namespaceUri, () => prefix); namespaces.putIfAbsent(namespaceUri, () => prefix);
String _constructPath([final String? path]) => [ String _constructPath([final String? path]) => [
baseUrl, rootClient.baseURL,
basePath, basePath,
if (path != null) ...[ if (path != null) ...[
path, path,

1
packages/nextcloud/pubspec.yaml

@ -11,6 +11,7 @@ dependencies:
intl: ^0.17.0 intl: ^0.17.0
json_annotation: ^4.6.0 json_annotation: ^4.6.0
meta: ^1.7.0 meta: ^1.7.0
version: ^3.0.2
xml: ^6.1.0 xml: ^6.1.0
dev_dependencies: dev_dependencies:

6
packages/nextcloud/test/core_test.dart

@ -1,3 +1,4 @@
import 'package:nextcloud/nextcloud.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'helper.dart'; import 'helper.dart';
@ -17,6 +18,11 @@ Future main() async {
}); });
tearDown(() => client.destroy()); tearDown(() => client.destroy());
test('Is supported', () async {
final response = await client.core.isSupported();
expect(response, isTrue);
});
test('Get status', () async { test('Get status', () async {
final status = await client.core.getStatus(); final status = await client.core.getStatus();
expect(status.installed, true); expect(status.installed, true);

5
packages/nextcloud/test/news_test.dart

@ -25,6 +25,11 @@ Future main() async {
url: nasaFeedURL, url: nasaFeedURL,
); );
test('Is supported', () async {
final response = await client.news.isSupported();
expect(response, isTrue);
});
test('Add feed', () async { test('Add feed', () async {
var response = await client.news.listFeeds(); var response = await client.news.listFeeds();
expect(response.starredCount, 0); expect(response.starredCount, 0);

5
packages/nextcloud/test/notes_test.dart

@ -13,6 +13,11 @@ Future main() async {
}); });
tearDown(() => client.destroy()); tearDown(() => client.destroy());
test('Is supported', () async {
final response = await client.notes.isSupported();
expect(response, isTrue);
});
test('Create note favorite', () async { test('Create note favorite', () async {
final response = await client.notes.createNote( final response = await client.notes.createNote(
title: 'a', title: 'a',

28
specs/news.json

@ -236,6 +236,34 @@
} }
}, },
"paths": { "paths": {
"/apps/news/api": {
"get": {
"operationId": "get-supported-api-versions",
"tags": [
"news"
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"apiLevels": {
"type": "array",
"items": {
"type": "string"
}
}
}
}
}
}
}
}
}
},
"/apps/news/api/v1-2/folders": { "/apps/news/api/v1-2/folders": {
"get": { "get": {
"operationId": "list-folders", "operationId": "list-folders",

Loading…
Cancel
Save