Browse Source

refactor(dynamite,neon_files,nextcloud): make webdav client use Uri objects for paths

Signed-off-by: Nikolas Rimikis <rimikis.nikolas@gmail.com>
Signed-off-by: Nikolas Rimikis <leptopoda@users.noreply.github.com>
pull/603/head
Nikolas Rimikis 1 year ago committed by Nikolas Rimikis
parent
commit
dac919fb66
No known key found for this signature in database
GPG Key ID: 85ED1DE9786A4FF2
  1. 8
      packages/dynamite/dynamite_runtime/lib/src/http_client.dart
  2. 15
      packages/dynamite/dynamite_runtime/lib/src/uri.dart
  3. 1
      packages/dynamite/dynamite_runtime/lib/utils.dart
  4. 15
      packages/dynamite/dynamite_runtime/test/uri_test.dart
  5. 4
      packages/neon/neon_files/lib/blocs/browser.dart
  6. 16
      packages/neon/neon_files/lib/blocs/files.dart
  7. 4
      packages/neon/neon_files/lib/utils/task.dart
  8. 3
      packages/nextcloud/lib/src/client.dart
  9. 102
      packages/nextcloud/lib/src/webdav/client.dart
  10. 1
      packages/nextcloud/pubspec.yaml
  11. 152
      packages/nextcloud/test/webdav_test.dart

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

@ -3,6 +3,7 @@ import 'dart:convert';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:cookie_jar/cookie_jar.dart'; import 'package:cookie_jar/cookie_jar.dart';
import 'package:dynamite_runtime/src/uri.dart';
import 'package:universal_io/io.dart'; import 'package:universal_io/io.dart';
export 'package:cookie_jar/cookie_jar.dart'; export 'package:cookie_jar/cookie_jar.dart';
@ -110,15 +111,16 @@ class DynamiteHttpBearerAuthentication extends DynamiteAuthentication {
class DynamiteClient { class DynamiteClient {
DynamiteClient( DynamiteClient(
this.baseURL, { final Uri baseURL, {
this.baseHeaders, this.baseHeaders,
final String? userAgent, final String? userAgent,
final HttpClient? httpClient, final HttpClient? httpClient,
this.cookieJar, this.cookieJar,
this.authentications = const [], this.authentications = const [],
}) : httpClient = (httpClient ?? HttpClient())..userAgent = userAgent; }) : httpClient = (httpClient ?? HttpClient())..userAgent = userAgent,
baseURL = baseURL.normalizeEmptyPath();
final String baseURL; final Uri baseURL;
final Map<String, String>? baseHeaders; final Map<String, String>? baseHeaders;

15
packages/dynamite/dynamite_runtime/lib/src/uri.dart

@ -0,0 +1,15 @@
/// Helpers extension for [Uri]s.
///
/// This might evolve into a separate class implementing the [Uri] interface in the future.
///
extension UriExtension on Uri {
/// Similar to [normalizePath] but it will also remove exmpty [pathSegments].
Uri normalizeEmptyPath() {
final normalized = normalizePath();
if (normalized.path.endsWith('/')) {
return normalized.replace(pathSegments: normalized.pathSegments.where((final s) => s.isNotEmpty));
}
return normalized;
}
}

1
packages/dynamite/dynamite_runtime/lib/utils.dart

@ -0,0 +1 @@
export 'src/uri.dart';

15
packages/dynamite/dynamite_runtime/test/uri_test.dart

@ -0,0 +1,15 @@
import 'package:dynamite_runtime/src/uri.dart';
import 'package:test/test.dart';
void main() {
test('UriExtension', () {
var uri = Uri(scheme: 'https', host: 'example.com', path: '/');
expect(uri.normalizeEmptyPath().toString(), 'https://example.com');
uri = Uri(scheme: 'https', host: 'example.com', path: '/slug');
expect(uri.normalizeEmptyPath().toString(), 'https://example.com/slug');
uri = Uri(scheme: 'https', host: 'example.com', path: '/slug/');
expect(uri.normalizeEmptyPath().toString(), 'https://example.com/slug');
});
}

4
packages/neon/neon_files/lib/blocs/browser.dart

@ -43,7 +43,7 @@ class FilesBrowserBloc extends InteractiveBloc implements FilesBrowserBlocEvents
'files-${path.value.join('/')}', 'files-${path.value.join('/')}',
files, files,
() async => account.client.webdav.propfind( () async => account.client.webdav.propfind(
path.value.join('/'), Uri(pathSegments: path.value),
prop: WebDavPropWithoutValues.fromBools( prop: WebDavPropWithoutValues.fromBools(
davgetcontenttype: true, davgetcontenttype: true,
davgetetag: true, davgetetag: true,
@ -67,6 +67,6 @@ class FilesBrowserBloc extends InteractiveBloc implements FilesBrowserBlocEvents
@override @override
void createFolder(final List<String> path) { void createFolder(final List<String> path) {
wrapAction(() async => account.client.webdav.mkcol(path.join('/'))); wrapAction(() async => account.client.webdav.mkcol(Uri(pathSegments: path)));
} }
} }

16
packages/neon/neon_files/lib/blocs/files.dart

@ -57,7 +57,7 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta
void addFavorite(final List<String> path) { void addFavorite(final List<String> path) {
wrapAction( wrapAction(
() async => account.client.webdav.proppatch( () async => account.client.webdav.proppatch(
path.join('/'), Uri(pathSegments: path),
set: WebDavProp(ocfavorite: 1), set: WebDavProp(ocfavorite: 1),
), ),
); );
@ -65,17 +65,17 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta
@override @override
void copy(final List<String> path, final List<String> destination) { void copy(final List<String> path, final List<String> destination) {
wrapAction(() async => account.client.webdav.copy(path.join('/'), destination.join('/'))); wrapAction(() async => account.client.webdav.copy(Uri(pathSegments: path), Uri(pathSegments: destination)));
} }
@override @override
void delete(final List<String> path) { void delete(final List<String> path) {
wrapAction(() async => account.client.webdav.delete(path.join('/'))); wrapAction(() async => account.client.webdav.delete(Uri(pathSegments: path)));
} }
@override @override
void move(final List<String> path, final List<String> destination) { void move(final List<String> path, final List<String> destination) {
wrapAction(() async => account.client.webdav.move(path.join('/'), destination.join('/'))); wrapAction(() async => account.client.webdav.move(Uri(pathSegments: path), Uri(pathSegments: destination)));
} }
@override @override
@ -85,7 +85,7 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta
final cacheDir = await getApplicationCacheDirectory(); final cacheDir = await getApplicationCacheDirectory();
final file = File(p.join(cacheDir.path, 'files', etag.replaceAll('"', ''), path.last)); final file = File(p.join(cacheDir.path, 'files', etag.replaceAll('"', ''), path.last));
if (!file.existsSync()) { if (!file.existsSync()) {
debugPrint('Downloading ${path.join('/')} since it does not exist'); debugPrint('Downloading ${Uri(pathSegments: path)} since it does not exist');
if (!file.parent.existsSync()) { if (!file.parent.existsSync()) {
await file.parent.create(recursive: true); await file.parent.create(recursive: true);
} }
@ -109,7 +109,7 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta
void removeFavorite(final List<String> path) { void removeFavorite(final List<String> path) {
wrapAction( wrapAction(
() async => account.client.webdav.proppatch( () async => account.client.webdav.proppatch(
path.join('/'), Uri(pathSegments: path),
set: WebDavProp(ocfavorite: 0), set: WebDavProp(ocfavorite: 0),
), ),
); );
@ -119,8 +119,8 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta
void rename(final List<String> path, final String name) { void rename(final List<String> path, final String name) {
wrapAction( wrapAction(
() async => account.client.webdav.move( () async => account.client.webdav.move(
path.join('/'), Uri(pathSegments: path),
(path.sublist(0, path.length - 1)..add(name)).join('/'), Uri(pathSegments: List.from(path)..last = name),
), ),
); );
} }

4
packages/neon/neon_files/lib/utils/task.dart

@ -25,7 +25,7 @@ class FilesDownloadTask extends FilesTask {
Future execute(final NextcloudClient client) async { Future execute(final NextcloudClient client) async {
await client.webdav.getFile( await client.webdav.getFile(
path.join('/'), Uri(pathSegments: path),
file, file,
onProgress: (final progress) { onProgress: (final progress) {
streamController.add(progress); streamController.add(progress);
@ -47,7 +47,7 @@ class FilesUploadTask extends FilesTask {
await client.webdav.putFile( await client.webdav.putFile(
file, file,
stat, stat,
path.join('/'), Uri(pathSegments: path),
lastModified: stat.modified, lastModified: stat.modified,
onProgress: (final progress) { onProgress: (final progress) {
streamController.add(progress); streamController.add(progress);

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

@ -7,7 +7,7 @@ import 'package:nextcloud/nextcloud.dart';
class NextcloudClient extends DynamiteClient { class NextcloudClient extends DynamiteClient {
// ignore: public_member_api_docs // ignore: public_member_api_docs
NextcloudClient( NextcloudClient(
super.baseURL, { final String baseURL, {
this.loginName, this.loginName,
final String? password, final String? password,
final String? appPassword, final String? appPassword,
@ -16,6 +16,7 @@ class NextcloudClient extends DynamiteClient {
final String? userAgentOverride, final String? userAgentOverride,
super.cookieJar, super.cookieJar,
}) : super( }) : super(
Uri.parse(baseURL),
baseHeaders: language != null ? {'Accept-Language': language} : null, baseHeaders: language != null ? {'Accept-Language': language} : null,
userAgent: userAgentOverride ?? appType.userAgent, userAgent: userAgentOverride ?? appType.userAgent,
authentications: [ authentications: [

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

@ -3,6 +3,7 @@ import 'dart:convert';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:dynamite_runtime/http_client.dart'; import 'package:dynamite_runtime/http_client.dart';
import 'package:meta/meta.dart';
import 'package:nextcloud/src/webdav/props.dart'; import 'package:nextcloud/src/webdav/props.dart';
import 'package:nextcloud/src/webdav/webdav.dart'; import 'package:nextcloud/src/webdav/webdav.dart';
import 'package:universal_io/io.dart'; import 'package:universal_io/io.dart';
@ -21,17 +22,14 @@ class WebDavClient {
Future<HttpClientResponse> _send( Future<HttpClientResponse> _send(
final String method, final String method,
final String url, { final Uri url, {
final Stream<Uint8List>? dataStream, final Stream<Uint8List>? dataStream,
final Uint8List? data, final Uint8List? data,
final Map<String, String>? headers, final Map<String, String>? headers,
}) async { }) async {
assert(dataStream == null || data == null, 'Only one of dataStream or data can be specified.'); assert(dataStream == null || data == null, 'Only one of dataStream or data can be specified.');
final request = await HttpClient().openUrl( final request = await HttpClient().openUrl(method, url)
method,
Uri.parse(url),
)
..persistentConnection = true; ..persistentConnection = true;
for (final header in { for (final header in {
HttpHeaders.contentTypeHeader: 'application/xml', HttpHeaders.contentTypeHeader: 'application/xml',
@ -62,24 +60,26 @@ class WebDavClient {
return response; return response;
} }
String _constructPath([final String? path]) => [ Uri _constructUri([final Uri? path]) => constructUri(rootClient.baseURL, path);
rootClient.baseURL,
webdavBasePath, @visibleForTesting
if (path != null) ...[ // ignore: public_member_api_docs
path, static Uri constructUri(final Uri baseURL, [final Uri? path]) {
], assert(
] path == null || path.path == '/' || !path.path.startsWith('/'),
.map((part) { "The path should not start a '/' unless indicating the root folder.",
while (part.startsWith('/')) { );
part = part.substring(1); assert(!baseURL.path.endsWith('/'), "The baseURL should not end with a '/'.");
}
while (part.endsWith('/')) { final pathBuffer = StringBuffer(baseURL.path)..write(webdavBasePath);
part = part.substring(0, part.length - 1); // coverage:ignore-line if (path != null && path.path != '/') {
} pathBuffer
return part; ..write('/')
}) ..write(path.path);
.where((final part) => part.isNotEmpty) }
.join('/');
return baseURL.replace(path: pathBuffer.toString());
}
Future<WebDavMultistatus> _parseResponse(final HttpClientResponse response) async => Future<WebDavMultistatus> _parseResponse(final HttpClientResponse response) async =>
WebDavMultistatus.fromXmlElement(xml.XmlDocument.parse(await response.body).rootElement); WebDavMultistatus.fromXmlElement(xml.XmlDocument.parse(await response.body).rootElement);
@ -99,7 +99,7 @@ class WebDavClient {
Future<WebDavOptions> options() async { Future<WebDavOptions> options() async {
final response = await _send( final response = await _send(
'OPTIONS', 'OPTIONS',
_constructPath(), _constructUri(),
); );
final davCapabilities = response.headers['dav']?.first ?? ''; final davCapabilities = response.headers['dav']?.first ?? '';
final davSearchCapabilities = response.headers['dasl']?.first ?? ''; final davSearchCapabilities = response.headers['dasl']?.first ?? '';
@ -112,17 +112,17 @@ class WebDavClient {
/// Creates a collection at [path]. /// Creates a collection at [path].
/// ///
/// See http://www.webdav.org/specs/rfc2518.html#METHOD_MKCOL for more information. /// See http://www.webdav.org/specs/rfc2518.html#METHOD_MKCOL for more information.
Future<HttpClientResponse> mkcol(final String path) async => _send( Future<HttpClientResponse> mkcol(final Uri path) async => _send(
'MKCOL', 'MKCOL',
_constructPath(path), _constructUri(path),
); );
/// Deletes the resource at [path]. /// Deletes the resource at [path].
/// ///
/// See http://www.webdav.org/specs/rfc2518.html#METHOD_DELETE for more information. /// See http://www.webdav.org/specs/rfc2518.html#METHOD_DELETE for more information.
Future<HttpClientResponse> delete(final String path) => _send( Future<HttpClientResponse> delete(final Uri path) => _send(
'DELETE', 'DELETE',
_constructPath(path), _constructUri(path),
); );
/// Puts a new file at [path] with [localData] as content. /// Puts a new file at [path] with [localData] as content.
@ -132,13 +132,13 @@ class WebDavClient {
/// See http://www.webdav.org/specs/rfc2518.html#METHOD_PUT for more information. /// See http://www.webdav.org/specs/rfc2518.html#METHOD_PUT for more information.
Future<HttpClientResponse> put( Future<HttpClientResponse> put(
final Uint8List localData, final Uint8List localData,
final String path, { final Uri path, {
final DateTime? lastModified, final DateTime? lastModified,
final DateTime? created, final DateTime? created,
}) => }) =>
_send( _send(
'PUT', 'PUT',
_constructPath(path), _constructUri(path),
data: localData, data: localData,
headers: _getUploadHeaders( headers: _getUploadHeaders(
lastModified: lastModified, lastModified: lastModified,
@ -156,7 +156,7 @@ class WebDavClient {
/// See http://www.webdav.org/specs/rfc2518.html#METHOD_PUT for more information. /// See http://www.webdav.org/specs/rfc2518.html#METHOD_PUT for more information.
Future<HttpClientResponse> putStream( Future<HttpClientResponse> putStream(
final Stream<Uint8List> localData, final Stream<Uint8List> localData,
final String path, { final Uri path, {
final DateTime? lastModified, final DateTime? lastModified,
final DateTime? created, final DateTime? created,
final int? contentLength, final int? contentLength,
@ -165,7 +165,7 @@ class WebDavClient {
var uploaded = 0; var uploaded = 0;
return _send( return _send(
'PUT', 'PUT',
_constructPath(path), _constructUri(path),
dataStream: contentLength != null dataStream: contentLength != null
? localData.map((final chunk) { ? localData.map((final chunk) {
uploaded += chunk.length; uploaded += chunk.length;
@ -190,7 +190,7 @@ class WebDavClient {
Future<HttpClientResponse> putFile( Future<HttpClientResponse> putFile(
final File file, final File file,
final FileStat fileStat, final FileStat fileStat,
final String path, { final Uri path, {
final DateTime? lastModified, final DateTime? lastModified,
final DateTime? created, final DateTime? created,
final Function(double progres)? onProgress, final Function(double progres)? onProgress,
@ -205,17 +205,17 @@ class WebDavClient {
); );
/// Gets the content of the file at [path]. /// Gets the content of the file at [path].
Future<Uint8List> get(final String path) async => (await getStream(path)).bodyBytes; Future<Uint8List> get(final Uri path) async => (await getStream(path)).bodyBytes;
/// Gets the content of the file at [path]. /// Gets the content of the file at [path].
Future<HttpClientResponse> getStream(final String path) async => _send( Future<HttpClientResponse> getStream(final Uri path) async => _send(
'GET', 'GET',
_constructPath(path), _constructUri(path),
); );
/// Gets the content of the file at [path]. /// Gets the content of the file at [path].
Future getFile( Future getFile(
final String path, final Uri path,
final File file, { final File file, {
final Function(double progress)? onProgress, final Function(double progress)? onProgress,
}) async { }) async {
@ -245,14 +245,14 @@ class WebDavClient {
/// [depth] can be used to limit scope of the returned resources. /// [depth] can be used to limit scope of the returned resources.
/// See http://www.webdav.org/specs/rfc2518.html#METHOD_PROPFIND for more information. /// See http://www.webdav.org/specs/rfc2518.html#METHOD_PROPFIND for more information.
Future<WebDavMultistatus> propfind( Future<WebDavMultistatus> propfind(
final String path, { final Uri path, {
final WebDavPropWithoutValues? prop, final WebDavPropWithoutValues? prop,
final WebDavDepth? depth, final WebDavDepth? depth,
}) async => }) async =>
_parseResponse( _parseResponse(
await _send( await _send(
'PROPFIND', 'PROPFIND',
_constructPath(path), _constructUri(path),
data: utf8.encode( data: utf8.encode(
WebDavPropfind(prop: prop ?? WebDavPropWithoutValues()).toXmlElement(namespaces: namespaces).toXmlString(), WebDavPropfind(prop: prop ?? WebDavPropWithoutValues()).toXmlElement(namespaces: namespaces).toXmlString(),
) as Uint8List, ) as Uint8List,
@ -265,14 +265,14 @@ class WebDavClient {
/// Optionally populates the [prop]s on the returned resources. /// Optionally populates the [prop]s on the returned resources.
/// See https://github.com/owncloud/docs/issues/359 for more information. /// See https://github.com/owncloud/docs/issues/359 for more information.
Future<WebDavMultistatus> report( Future<WebDavMultistatus> report(
final String path, final Uri path,
final WebDavOcFilterRules filterRules, { final WebDavOcFilterRules filterRules, {
final WebDavPropWithoutValues? prop, final WebDavPropWithoutValues? prop,
}) async => }) async =>
_parseResponse( _parseResponse(
await _send( await _send(
'REPORT', 'REPORT',
_constructPath(path), _constructUri(path),
data: utf8.encode( data: utf8.encode(
WebDavOcFilterFiles( WebDavOcFilterFiles(
filterRules: filterRules, filterRules: filterRules,
@ -289,13 +289,13 @@ class WebDavClient {
/// Returns true if the update was successful. /// Returns true if the update was successful.
/// See http://www.webdav.org/specs/rfc2518.html#METHOD_PROPPATCH for more information. /// See http://www.webdav.org/specs/rfc2518.html#METHOD_PROPPATCH for more information.
Future<bool> proppatch( Future<bool> proppatch(
final String path, { final Uri path, {
final WebDavProp? set, final WebDavProp? set,
final WebDavPropWithoutValues? remove, final WebDavPropWithoutValues? remove,
}) async { }) async {
final response = await _send( final response = await _send(
'PROPPATCH', 'PROPPATCH',
_constructPath(path), _constructUri(path),
data: utf8.encode( data: utf8.encode(
WebDavPropertyupdate( WebDavPropertyupdate(
set: set != null ? WebDavSet(prop: set) : null, set: set != null ? WebDavSet(prop: set) : null,
@ -319,15 +319,15 @@ class WebDavClient {
/// If [overwrite] is set any existing resource will be replaced. /// If [overwrite] is set any existing resource will be replaced.
/// See http://www.webdav.org/specs/rfc2518.html#METHOD_MOVE for more information. /// See http://www.webdav.org/specs/rfc2518.html#METHOD_MOVE for more information.
Future<HttpClientResponse> move( Future<HttpClientResponse> move(
final String sourcePath, final Uri sourcePath,
final String destinationPath, { final Uri destinationPath, {
final bool overwrite = false, final bool overwrite = false,
}) => }) =>
_send( _send(
'MOVE', 'MOVE',
_constructPath(sourcePath), _constructUri(sourcePath),
headers: { headers: {
'Destination': _constructPath(destinationPath), 'Destination': _constructUri(destinationPath).toString(),
'Overwrite': overwrite ? 'T' : 'F', 'Overwrite': overwrite ? 'T' : 'F',
}, },
); );
@ -337,15 +337,15 @@ class WebDavClient {
/// If [overwrite] is set any existing resource will be replaced. /// If [overwrite] is set any existing resource will be replaced.
/// See http://www.webdav.org/specs/rfc2518.html#METHOD_COPY for more information. /// See http://www.webdav.org/specs/rfc2518.html#METHOD_COPY for more information.
Future<HttpClientResponse> copy( Future<HttpClientResponse> copy(
final String sourcePath, final Uri sourcePath,
final String destinationPath, { final Uri destinationPath, {
final bool overwrite = false, final bool overwrite = false,
}) => }) =>
_send( _send(
'COPY', 'COPY',
_constructPath(sourcePath), _constructUri(sourcePath),
headers: { headers: {
'Destination': _constructPath(destinationPath), 'Destination': _constructUri(destinationPath).toString(),
'Overwrite': overwrite ? 'T' : 'F', 'Overwrite': overwrite ? 'T' : 'F',
}, },
); );

1
packages/nextcloud/pubspec.yaml

@ -17,6 +17,7 @@ dependencies:
path: packages/dynamite/dynamite_runtime path: packages/dynamite/dynamite_runtime
intl: ^0.18.1 intl: ^0.18.1
json_annotation: ^4.8.1 json_annotation: ^4.8.1
meta: ^1.9.1
universal_io: ^2.2.2 universal_io: ^2.2.2
version: ^3.0.2 version: ^3.0.2
xml: ^6.3.0 xml: ^6.3.0

152
packages/nextcloud/test/webdav_test.dart

@ -12,6 +12,20 @@ import 'package:test/test.dart';
import 'helper.dart'; import 'helper.dart';
void main() { void main() {
test('constructUri', () {
var baseURL = Uri.parse('http://cloud.example.com');
expect(WebDavClient.constructUri(baseURL).toString(), '$baseURL$webdavBasePath');
expect(WebDavClient.constructUri(baseURL, Uri(path: '/')).toString(), '$baseURL$webdavBasePath');
expect(WebDavClient.constructUri(baseURL, Uri(path: 'test')).toString(), '$baseURL$webdavBasePath/test');
baseURL = Uri.parse('http://cloud.example.com/subdir');
expect(WebDavClient.constructUri(baseURL, Uri(path: 'test')).toString(), '$baseURL$webdavBasePath/test');
expect(() => WebDavClient.constructUri(baseURL, Uri(path: '/test')), throwsA(isA<AssertionError>()));
baseURL = Uri.parse('http://cloud.example.com/');
expect(() => WebDavClient.constructUri(baseURL), throwsA(isA<AssertionError>()));
});
group('webdav', () { group('webdav', () {
late DockerImage image; late DockerImage image;
setUpAll(() async => image = await getDockerImage()); setUpAll(() async => image = await getDockerImage());
@ -26,7 +40,7 @@ void main() {
test('List directory', () async { test('List directory', () async {
final responses = (await client.webdav.propfind( final responses = (await client.webdav.propfind(
'/', Uri(path: '/'),
prop: WebDavPropWithoutValues.fromBools( prop: WebDavPropWithoutValues.fromBools(
nchaspreview: true, nchaspreview: true,
davgetcontenttype: true, davgetcontenttype: true,
@ -46,7 +60,7 @@ void main() {
test('List directory recursively', () async { test('List directory recursively', () async {
final responses = (await client.webdav.propfind( final responses = (await client.webdav.propfind(
'/', Uri(path: '/'),
depth: WebDavDepth.infinity, depth: WebDavDepth.infinity,
)) ))
.responses; .responses;
@ -55,7 +69,7 @@ void main() {
test('Get file props', () async { test('Get file props', () async {
final response = (await client.webdav.propfind( final response = (await client.webdav.propfind(
'Nextcloud.png', Uri(path: 'Nextcloud.png'),
prop: WebDavPropWithoutValues.fromBools( prop: WebDavPropWithoutValues.fromBools(
davgetlastmodified: true, davgetlastmodified: true,
davgetetag: true, davgetetag: true,
@ -138,11 +152,11 @@ void main() {
test('Get directory props', () async { test('Get directory props', () async {
final data = utf8.encode('test') as Uint8List; final data = utf8.encode('test') as Uint8List;
await client.webdav.mkcol('test'); await client.webdav.mkcol(Uri(path: 'test'));
await client.webdav.put(data, 'test/test.txt'); await client.webdav.put(data, Uri(path: 'test/test.txt'));
final response = (await client.webdav.propfind( final response = (await client.webdav.propfind(
'test', Uri(path: 'test'),
prop: WebDavPropWithoutValues.fromBools( prop: WebDavPropWithoutValues.fromBools(
davgetcontenttype: true, davgetcontenttype: true,
davgetlastmodified: true, davgetlastmodified: true,
@ -169,17 +183,17 @@ void main() {
}); });
test('Filter files', () async { test('Filter files', () async {
final response = await client.webdav.put(utf8.encode('test') as Uint8List, 'test.txt'); final response = await client.webdav.put(utf8.encode('test') as Uint8List, Uri(path: 'test.txt'));
final id = response.headers['oc-fileid']!.first; final id = response.headers['oc-fileid']!.first;
await client.webdav.proppatch( await client.webdav.proppatch(
'test.txt', Uri(path: 'test.txt'),
set: WebDavProp( set: WebDavProp(
ocfavorite: 1, ocfavorite: 1,
), ),
); );
final responses = (await client.webdav.report( final responses = (await client.webdav.report(
'/', Uri(path: '/'),
WebDavOcFilterRules( WebDavOcFilterRules(
ocfavorite: 1, ocfavorite: 1,
), ),
@ -203,13 +217,13 @@ void main() {
await client.webdav.put( await client.webdav.put(
utf8.encode('test') as Uint8List, utf8.encode('test') as Uint8List,
'test.txt', Uri(path: 'test.txt'),
lastModified: lastModifiedDate, lastModified: lastModifiedDate,
created: createdDate, created: createdDate,
); );
final updated = await client.webdav.proppatch( final updated = await client.webdav.proppatch(
'test.txt', Uri(path: 'test.txt'),
set: WebDavProp( set: WebDavProp(
ocfavorite: 1, ocfavorite: 1,
), ),
@ -217,7 +231,7 @@ void main() {
expect(updated, isTrue); expect(updated, isTrue);
final props = (await client.webdav.propfind( final props = (await client.webdav.propfind(
'test.txt', Uri(path: 'test.txt'),
prop: WebDavPropWithoutValues.fromBools( prop: WebDavPropWithoutValues.fromBools(
ocfavorite: true, ocfavorite: true,
davgetlastmodified: true, davgetlastmodified: true,
@ -237,10 +251,10 @@ void main() {
}); });
test('Remove properties', () async { test('Remove properties', () async {
await client.webdav.put(utf8.encode('test') as Uint8List, 'test.txt'); await client.webdav.put(utf8.encode('test') as Uint8List, Uri(path: 'test.txt'));
var updated = await client.webdav.proppatch( var updated = await client.webdav.proppatch(
'test.txt', Uri(path: 'test.txt'),
set: WebDavProp( set: WebDavProp(
ocfavorite: 1, ocfavorite: 1,
), ),
@ -248,7 +262,7 @@ void main() {
expect(updated, isTrue); expect(updated, isTrue);
var props = (await client.webdav.propfind( var props = (await client.webdav.propfind(
'test.txt', Uri(path: 'test.txt'),
prop: WebDavPropWithoutValues.fromBools( prop: WebDavPropWithoutValues.fromBools(
ocfavorite: true, ocfavorite: true,
nccreationtime: true, nccreationtime: true,
@ -263,7 +277,7 @@ void main() {
expect(props.ocfavorite, 1); expect(props.ocfavorite, 1);
updated = await client.webdav.proppatch( updated = await client.webdav.proppatch(
'test.txt', Uri(path: 'test.txt'),
remove: WebDavPropWithoutValues.fromBools( remove: WebDavPropWithoutValues.fromBools(
ocfavorite: true, ocfavorite: true,
), ),
@ -271,7 +285,7 @@ void main() {
expect(updated, isFalse); expect(updated, isFalse);
props = (await client.webdav.propfind( props = (await client.webdav.propfind(
'test.txt', Uri(path: 'test.txt'),
prop: WebDavPropWithoutValues.fromBools( prop: WebDavPropWithoutValues.fromBools(
ocfavorite: true, ocfavorite: true,
), ),
@ -293,11 +307,11 @@ void main() {
await client.webdav.putFile( await client.webdav.putFile(
source, source,
source.statSync(), source.statSync(),
'test.png', Uri(path: 'test.png'),
onProgress: progressValues.add, onProgress: progressValues.add,
); );
await client.webdav.getFile( await client.webdav.getFile(
'test.png', Uri(path: 'test.png'),
destination, destination,
onProgress: progressValues.add, onProgress: progressValues.add,
); );
@ -325,32 +339,32 @@ void main() {
test(name, () async { test(name, () async {
final content = utf8.encode('This is a test file') as Uint8List; final content = utf8.encode('This is a test file') as Uint8List;
final response = await client.webdav.put(content, path); final response = await client.webdav.put(content, Uri(path: path));
expect(response.statusCode, 201); expect(response.statusCode, 201);
final downloadedContent = await client.webdav.get(path); final downloadedContent = await client.webdav.get(Uri(path: path));
expect(downloadedContent, equals(content)); expect(downloadedContent, equals(content));
}); });
} }
test('put_no_parent', () async { test('put_no_parent', () async {
expect( expect(
() => client.webdav.put(Uint8List(0), '409me/noparent.txt'), () => client.webdav.put(Uint8List(0), Uri(path: '409me/noparent.txt')),
// https://github.com/nextcloud/server/issues/39625 // https://github.com/nextcloud/server/issues/39625
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 409)), throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 409)),
); );
}); });
test('delete', () async { test('delete', () async {
await client.webdav.put(Uint8List(0), 'test.txt'); await client.webdav.put(Uint8List(0), Uri(path: 'test.txt'));
final response = await client.webdav.delete('test.txt'); final response = await client.webdav.delete(Uri(path: 'test.txt'));
expect(response.statusCode, 204); expect(response.statusCode, 204);
}); });
test('delete_null', () async { test('delete_null', () async {
expect( expect(
() => client.webdav.delete('test.txt'), () => client.webdav.delete(Uri(path: 'test.txt')),
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 404)), throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 404)),
); );
}); });
@ -358,29 +372,29 @@ void main() {
// delete_fragment: This test is not applicable because the fragment is already removed on the client side // delete_fragment: This test is not applicable because the fragment is already removed on the client side
test('mkcol', () async { test('mkcol', () async {
final response = await client.webdav.mkcol('test'); final response = await client.webdav.mkcol(Uri(path: 'test'));
expect(response.statusCode, 201); expect(response.statusCode, 201);
}); });
test('mkcol_again', () async { test('mkcol_again', () async {
await client.webdav.mkcol('test'); await client.webdav.mkcol(Uri(path: 'test'));
expect( expect(
() => client.webdav.mkcol('test'), () => client.webdav.mkcol(Uri(path: 'test')),
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 405)), throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 405)),
); );
}); });
test('delete_coll', () async { test('delete_coll', () async {
var response = await client.webdav.mkcol('test'); var response = await client.webdav.mkcol(Uri(path: 'test'));
response = await client.webdav.delete('test'); response = await client.webdav.delete(Uri(path: 'test'));
expect(response.statusCode, 204); expect(response.statusCode, 204);
}); });
test('mkcol_no_parent', () async { test('mkcol_no_parent', () async {
expect( expect(
() => client.webdav.mkcol('409me/noparent'), () => client.webdav.mkcol(Uri(path: '409me/noparent')),
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 409)), throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 409)),
); );
}); });
@ -390,110 +404,110 @@ void main() {
group('copymove', () { group('copymove', () {
test('copy_simple', () async { test('copy_simple', () async {
await client.webdav.mkcol('src'); await client.webdav.mkcol(Uri(path: 'src'));
final response = await client.webdav.copy('src', 'dst'); final response = await client.webdav.copy(Uri(path: 'src'), Uri(path: 'dst'));
expect(response.statusCode, 201); expect(response.statusCode, 201);
}); });
test('copy_overwrite', () async { test('copy_overwrite', () async {
await client.webdav.mkcol('src'); await client.webdav.mkcol(Uri(path: 'src'));
await client.webdav.mkcol('dst'); await client.webdav.mkcol(Uri(path: 'dst'));
expect( expect(
() => client.webdav.copy('src', 'dst'), () => client.webdav.copy(Uri(path: 'src'), Uri(path: 'dst')),
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 412)), throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 412)),
); );
final response = await client.webdav.copy('src', 'dst', overwrite: true); final response = await client.webdav.copy(Uri(path: 'src'), Uri(path: 'dst'), overwrite: true);
expect(response.statusCode, 204); expect(response.statusCode, 204);
}); });
test('copy_nodestcoll', () async { test('copy_nodestcoll', () async {
await client.webdav.mkcol('src'); await client.webdav.mkcol(Uri(path: 'src'));
expect( expect(
() => client.webdav.copy('src', 'nonesuch/dst'), () => client.webdav.copy(Uri(path: 'src'), Uri(path: 'nonesuch/dst')),
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 409)), throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 409)),
); );
}); });
test('copy_coll', () async { test('copy_coll', () async {
await client.webdav.mkcol('src'); await client.webdav.mkcol(Uri(path: 'src'));
await client.webdav.mkcol('src/sub'); await client.webdav.mkcol(Uri(path: 'src/sub'));
for (var i = 0; i < 10; i++) { for (var i = 0; i < 10; i++) {
await client.webdav.put(Uint8List(0), 'src/$i.txt'); await client.webdav.put(Uint8List(0), Uri(path: 'src/$i.txt'));
} }
await client.webdav.copy('src', 'dst1'); await client.webdav.copy(Uri(path: 'src'), Uri(path: 'dst1'));
await client.webdav.copy('src', 'dst2'); await client.webdav.copy(Uri(path: 'src'), Uri(path: 'dst2'));
expect( expect(
() => client.webdav.copy('src', 'dst1'), () => client.webdav.copy(Uri(path: 'src'), Uri(path: 'dst1')),
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 412)), throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 412)),
); );
var response = await client.webdav.copy('src', 'dst2', overwrite: true); var response = await client.webdav.copy(Uri(path: 'src'), Uri(path: 'dst2'), overwrite: true);
expect(response.statusCode, 204); expect(response.statusCode, 204);
for (var i = 0; i < 10; i++) { for (var i = 0; i < 10; i++) {
response = await client.webdav.delete('dst1/$i.txt'); response = await client.webdav.delete(Uri(path: 'dst1/$i.txt'));
expect(response.statusCode, 204); expect(response.statusCode, 204);
} }
response = await client.webdav.delete('dst1/sub'); response = await client.webdav.delete(Uri(path: 'dst1/sub'));
expect(response.statusCode, 204); expect(response.statusCode, 204);
response = await client.webdav.delete('dst2'); response = await client.webdav.delete(Uri(path: 'dst2'));
expect(response.statusCode, 204); expect(response.statusCode, 204);
}); });
// copy_shallow: Does not work on litmus, let's wait for https://github.com/nextcloud/server/issues/39627 // copy_shallow: Does not work on litmus, let's wait for https://github.com/nextcloud/server/issues/39627
test('move', () async { test('move', () async {
await client.webdav.put(Uint8List(0), 'src1.txt'); await client.webdav.put(Uint8List(0), Uri(path: 'src1.txt'));
await client.webdav.put(Uint8List(0), 'src2.txt'); await client.webdav.put(Uint8List(0), Uri(path: 'src2.txt'));
await client.webdav.mkcol('coll'); await client.webdav.mkcol(Uri(path: 'coll'));
var response = await client.webdav.move('src1.txt', 'dst.txt'); var response = await client.webdav.move(Uri(path: 'src1.txt'), Uri(path: 'dst.txt'));
expect(response.statusCode, 201); expect(response.statusCode, 201);
expect( expect(
() => client.webdav.move('src2.txt', 'dst.txt'), () => client.webdav.move(Uri(path: 'src2.txt'), Uri(path: 'dst.txt')),
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 412)), throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 412)),
); );
response = await client.webdav.move('src2.txt', 'dst.txt', overwrite: true); response = await client.webdav.move(Uri(path: 'src2.txt'), Uri(path: 'dst.txt'), overwrite: true);
expect(response.statusCode, 204); expect(response.statusCode, 204);
}); });
test('move_coll', () async { test('move_coll', () async {
await client.webdav.mkcol('src'); await client.webdav.mkcol(Uri(path: 'src'));
await client.webdav.mkcol('src/sub'); await client.webdav.mkcol(Uri(path: 'src/sub'));
for (var i = 0; i < 10; i++) { for (var i = 0; i < 10; i++) {
await client.webdav.put(Uint8List(0), 'src/$i.txt'); await client.webdav.put(Uint8List(0), Uri(path: 'src/$i.txt'));
} }
await client.webdav.put(Uint8List(0), 'noncoll'); await client.webdav.put(Uint8List(0), Uri(path: 'noncoll'));
await client.webdav.copy('src', 'dst2'); await client.webdav.copy(Uri(path: 'src'), Uri(path: 'dst2'));
await client.webdav.move('src', 'dst1'); await client.webdav.move(Uri(path: 'src'), Uri(path: 'dst1'));
expect( expect(
() => client.webdav.move('dst1', 'dst2'), () => client.webdav.move(Uri(path: 'dst1'), Uri(path: 'dst2')),
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 412)), throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 412)),
); );
await client.webdav.move('dst2', 'dst1', overwrite: true); await client.webdav.move(Uri(path: 'dst2'), Uri(path: 'dst1'), overwrite: true);
await client.webdav.copy('dst1', 'dst2'); await client.webdav.copy(Uri(path: 'dst1'), Uri(path: 'dst2'));
for (var i = 0; i < 10; i++) { for (var i = 0; i < 10; i++) {
final response = await client.webdav.delete('dst1/$i.txt'); final response = await client.webdav.delete(Uri(path: 'dst1/$i.txt'));
expect(response.statusCode, 204); expect(response.statusCode, 204);
} }
final response = await client.webdav.delete('dst1/sub'); final response = await client.webdav.delete(Uri(path: 'dst1/sub'));
expect(response.statusCode, 204); expect(response.statusCode, 204);
expect( expect(
() => client.webdav.move('dst2', 'noncoll'), () => client.webdav.move(Uri(path: 'dst2'), Uri(path: 'noncoll')),
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 412)), throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 412)),
); );
}); });
@ -505,10 +519,10 @@ void main() {
// large_put: Already covered by large_get // large_put: Already covered by large_get
test('large_get', () async { test('large_get', () async {
final response = await client.webdav.put(Uint8List(largefileSize), 'test.txt'); final response = await client.webdav.put(Uint8List(largefileSize), Uri(path: 'test.txt'));
expect(response.statusCode, 201); expect(response.statusCode, 201);
final downloadedContent = await client.webdav.get('test.txt'); final downloadedContent = await client.webdav.get(Uri(path: 'test.txt'));
expect(downloadedContent, hasLength(largefileSize)); expect(downloadedContent, hasLength(largefileSize));
}); });
}); });

Loading…
Cancel
Save