diff --git a/packages/dynamite/dynamite_runtime/lib/src/http_client.dart b/packages/dynamite/dynamite_runtime/lib/src/http_client.dart index 04d49176..d52aeafb 100644 --- a/packages/dynamite/dynamite_runtime/lib/src/http_client.dart +++ b/packages/dynamite/dynamite_runtime/lib/src/http_client.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:cookie_jar/cookie_jar.dart'; +import 'package:dynamite_runtime/src/uri.dart'; import 'package:universal_io/io.dart'; export 'package:cookie_jar/cookie_jar.dart'; @@ -110,15 +111,16 @@ class DynamiteHttpBearerAuthentication extends DynamiteAuthentication { class DynamiteClient { DynamiteClient( - this.baseURL, { + final Uri baseURL, { this.baseHeaders, final String? userAgent, final HttpClient? httpClient, this.cookieJar, this.authentications = const [], - }) : httpClient = (httpClient ?? HttpClient())..userAgent = userAgent; + }) : httpClient = (httpClient ?? HttpClient())..userAgent = userAgent, + baseURL = baseURL.normalizeEmptyPath(); - final String baseURL; + final Uri baseURL; final Map? baseHeaders; diff --git a/packages/dynamite/dynamite_runtime/lib/src/uri.dart b/packages/dynamite/dynamite_runtime/lib/src/uri.dart new file mode 100644 index 00000000..61e5bf5d --- /dev/null +++ b/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; + } +} diff --git a/packages/dynamite/dynamite_runtime/lib/utils.dart b/packages/dynamite/dynamite_runtime/lib/utils.dart new file mode 100644 index 00000000..30f229d5 --- /dev/null +++ b/packages/dynamite/dynamite_runtime/lib/utils.dart @@ -0,0 +1 @@ +export 'src/uri.dart'; diff --git a/packages/dynamite/dynamite_runtime/test/uri_test.dart b/packages/dynamite/dynamite_runtime/test/uri_test.dart new file mode 100644 index 00000000..010ef7c1 --- /dev/null +++ b/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'); + }); +} diff --git a/packages/neon/neon_files/lib/blocs/browser.dart b/packages/neon/neon_files/lib/blocs/browser.dart index d322e095..15411e2a 100644 --- a/packages/neon/neon_files/lib/blocs/browser.dart +++ b/packages/neon/neon_files/lib/blocs/browser.dart @@ -43,7 +43,7 @@ class FilesBrowserBloc extends InteractiveBloc implements FilesBrowserBlocEvents 'files-${path.value.join('/')}', files, () async => account.client.webdav.propfind( - path.value.join('/'), + Uri(pathSegments: path.value), prop: WebDavPropWithoutValues.fromBools( davgetcontenttype: true, davgetetag: true, @@ -67,6 +67,6 @@ class FilesBrowserBloc extends InteractiveBloc implements FilesBrowserBlocEvents @override void createFolder(final List path) { - wrapAction(() async => account.client.webdav.mkcol(path.join('/'))); + wrapAction(() async => account.client.webdav.mkcol(Uri(pathSegments: path))); } } diff --git a/packages/neon/neon_files/lib/blocs/files.dart b/packages/neon/neon_files/lib/blocs/files.dart index 2898ce8f..4ec1c7cd 100644 --- a/packages/neon/neon_files/lib/blocs/files.dart +++ b/packages/neon/neon_files/lib/blocs/files.dart @@ -57,7 +57,7 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta void addFavorite(final List path) { wrapAction( () async => account.client.webdav.proppatch( - path.join('/'), + Uri(pathSegments: path), set: WebDavProp(ocfavorite: 1), ), ); @@ -65,17 +65,17 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta @override void copy(final List path, final List destination) { - wrapAction(() async => account.client.webdav.copy(path.join('/'), destination.join('/'))); + wrapAction(() async => account.client.webdav.copy(Uri(pathSegments: path), Uri(pathSegments: destination))); } @override void delete(final List path) { - wrapAction(() async => account.client.webdav.delete(path.join('/'))); + wrapAction(() async => account.client.webdav.delete(Uri(pathSegments: path))); } @override void move(final List path, final List destination) { - wrapAction(() async => account.client.webdav.move(path.join('/'), destination.join('/'))); + wrapAction(() async => account.client.webdav.move(Uri(pathSegments: path), Uri(pathSegments: destination))); } @override @@ -85,7 +85,7 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta final cacheDir = await getApplicationCacheDirectory(); final file = File(p.join(cacheDir.path, 'files', etag.replaceAll('"', ''), path.last)); 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()) { await file.parent.create(recursive: true); } @@ -109,7 +109,7 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta void removeFavorite(final List path) { wrapAction( () async => account.client.webdav.proppatch( - path.join('/'), + Uri(pathSegments: path), set: WebDavProp(ocfavorite: 0), ), ); @@ -119,8 +119,8 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta void rename(final List path, final String name) { wrapAction( () async => account.client.webdav.move( - path.join('/'), - (path.sublist(0, path.length - 1)..add(name)).join('/'), + Uri(pathSegments: path), + Uri(pathSegments: List.from(path)..last = name), ), ); } diff --git a/packages/neon/neon_files/lib/utils/task.dart b/packages/neon/neon_files/lib/utils/task.dart index 3e4c6b47..2a61e97b 100644 --- a/packages/neon/neon_files/lib/utils/task.dart +++ b/packages/neon/neon_files/lib/utils/task.dart @@ -25,7 +25,7 @@ class FilesDownloadTask extends FilesTask { Future execute(final NextcloudClient client) async { await client.webdav.getFile( - path.join('/'), + Uri(pathSegments: path), file, onProgress: (final progress) { streamController.add(progress); @@ -47,7 +47,7 @@ class FilesUploadTask extends FilesTask { await client.webdav.putFile( file, stat, - path.join('/'), + Uri(pathSegments: path), lastModified: stat.modified, onProgress: (final progress) { streamController.add(progress); diff --git a/packages/nextcloud/lib/src/client.dart b/packages/nextcloud/lib/src/client.dart index be794ace..fa4d6f51 100644 --- a/packages/nextcloud/lib/src/client.dart +++ b/packages/nextcloud/lib/src/client.dart @@ -7,7 +7,7 @@ import 'package:nextcloud/nextcloud.dart'; class NextcloudClient extends DynamiteClient { // ignore: public_member_api_docs NextcloudClient( - super.baseURL, { + final String baseURL, { this.loginName, final String? password, final String? appPassword, @@ -16,6 +16,7 @@ class NextcloudClient extends DynamiteClient { final String? userAgentOverride, super.cookieJar, }) : super( + Uri.parse(baseURL), baseHeaders: language != null ? {'Accept-Language': language} : null, userAgent: userAgentOverride ?? appType.userAgent, authentications: [ diff --git a/packages/nextcloud/lib/src/webdav/client.dart b/packages/nextcloud/lib/src/webdav/client.dart index ee42e1c7..23bd0068 100644 --- a/packages/nextcloud/lib/src/webdav/client.dart +++ b/packages/nextcloud/lib/src/webdav/client.dart @@ -3,6 +3,7 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:dynamite_runtime/http_client.dart'; +import 'package:meta/meta.dart'; import 'package:nextcloud/src/webdav/props.dart'; import 'package:nextcloud/src/webdav/webdav.dart'; import 'package:universal_io/io.dart'; @@ -21,17 +22,14 @@ class WebDavClient { Future _send( final String method, - final String url, { + final Uri url, { final Stream? dataStream, final Uint8List? data, final Map? headers, }) async { assert(dataStream == null || data == null, 'Only one of dataStream or data can be specified.'); - final request = await HttpClient().openUrl( - method, - Uri.parse(url), - ) + final request = await HttpClient().openUrl(method, url) ..persistentConnection = true; for (final header in { HttpHeaders.contentTypeHeader: 'application/xml', @@ -62,24 +60,26 @@ class WebDavClient { return response; } - String _constructPath([final String? path]) => [ - rootClient.baseURL, - webdavBasePath, - if (path != null) ...[ - path, - ], - ] - .map((part) { - while (part.startsWith('/')) { - part = part.substring(1); - } - while (part.endsWith('/')) { - part = part.substring(0, part.length - 1); // coverage:ignore-line - } - return part; - }) - .where((final part) => part.isNotEmpty) - .join('/'); + Uri _constructUri([final Uri? path]) => constructUri(rootClient.baseURL, path); + + @visibleForTesting + // ignore: public_member_api_docs + static Uri constructUri(final Uri baseURL, [final Uri? path]) { + assert( + path == null || path.path == '/' || !path.path.startsWith('/'), + "The path should not start a '/' unless indicating the root folder.", + ); + assert(!baseURL.path.endsWith('/'), "The baseURL should not end with a '/'."); + + final pathBuffer = StringBuffer(baseURL.path)..write(webdavBasePath); + if (path != null && path.path != '/') { + pathBuffer + ..write('/') + ..write(path.path); + } + + return baseURL.replace(path: pathBuffer.toString()); + } Future _parseResponse(final HttpClientResponse response) async => WebDavMultistatus.fromXmlElement(xml.XmlDocument.parse(await response.body).rootElement); @@ -99,7 +99,7 @@ class WebDavClient { Future options() async { final response = await _send( 'OPTIONS', - _constructPath(), + _constructUri(), ); final davCapabilities = response.headers['dav']?.first ?? ''; final davSearchCapabilities = response.headers['dasl']?.first ?? ''; @@ -112,17 +112,17 @@ class WebDavClient { /// Creates a collection at [path]. /// /// See http://www.webdav.org/specs/rfc2518.html#METHOD_MKCOL for more information. - Future mkcol(final String path) async => _send( + Future mkcol(final Uri path) async => _send( 'MKCOL', - _constructPath(path), + _constructUri(path), ); /// Deletes the resource at [path]. /// /// See http://www.webdav.org/specs/rfc2518.html#METHOD_DELETE for more information. - Future delete(final String path) => _send( + Future delete(final Uri path) => _send( 'DELETE', - _constructPath(path), + _constructUri(path), ); /// 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. Future put( final Uint8List localData, - final String path, { + final Uri path, { final DateTime? lastModified, final DateTime? created, }) => _send( 'PUT', - _constructPath(path), + _constructUri(path), data: localData, headers: _getUploadHeaders( lastModified: lastModified, @@ -156,7 +156,7 @@ class WebDavClient { /// See http://www.webdav.org/specs/rfc2518.html#METHOD_PUT for more information. Future putStream( final Stream localData, - final String path, { + final Uri path, { final DateTime? lastModified, final DateTime? created, final int? contentLength, @@ -165,7 +165,7 @@ class WebDavClient { var uploaded = 0; return _send( 'PUT', - _constructPath(path), + _constructUri(path), dataStream: contentLength != null ? localData.map((final chunk) { uploaded += chunk.length; @@ -190,7 +190,7 @@ class WebDavClient { Future putFile( final File file, final FileStat fileStat, - final String path, { + final Uri path, { final DateTime? lastModified, final DateTime? created, final Function(double progres)? onProgress, @@ -205,17 +205,17 @@ class WebDavClient { ); /// Gets the content of the file at [path]. - Future get(final String path) async => (await getStream(path)).bodyBytes; + Future get(final Uri path) async => (await getStream(path)).bodyBytes; /// Gets the content of the file at [path]. - Future getStream(final String path) async => _send( + Future getStream(final Uri path) async => _send( 'GET', - _constructPath(path), + _constructUri(path), ); /// Gets the content of the file at [path]. Future getFile( - final String path, + final Uri path, final File file, { final Function(double progress)? onProgress, }) async { @@ -245,14 +245,14 @@ class WebDavClient { /// [depth] can be used to limit scope of the returned resources. /// See http://www.webdav.org/specs/rfc2518.html#METHOD_PROPFIND for more information. Future propfind( - final String path, { + final Uri path, { final WebDavPropWithoutValues? prop, final WebDavDepth? depth, }) async => _parseResponse( await _send( 'PROPFIND', - _constructPath(path), + _constructUri(path), data: utf8.encode( WebDavPropfind(prop: prop ?? WebDavPropWithoutValues()).toXmlElement(namespaces: namespaces).toXmlString(), ) as Uint8List, @@ -265,14 +265,14 @@ class WebDavClient { /// Optionally populates the [prop]s on the returned resources. /// See https://github.com/owncloud/docs/issues/359 for more information. Future report( - final String path, + final Uri path, final WebDavOcFilterRules filterRules, { final WebDavPropWithoutValues? prop, }) async => _parseResponse( await _send( 'REPORT', - _constructPath(path), + _constructUri(path), data: utf8.encode( WebDavOcFilterFiles( filterRules: filterRules, @@ -289,13 +289,13 @@ class WebDavClient { /// Returns true if the update was successful. /// See http://www.webdav.org/specs/rfc2518.html#METHOD_PROPPATCH for more information. Future proppatch( - final String path, { + final Uri path, { final WebDavProp? set, final WebDavPropWithoutValues? remove, }) async { final response = await _send( 'PROPPATCH', - _constructPath(path), + _constructUri(path), data: utf8.encode( WebDavPropertyupdate( set: set != null ? WebDavSet(prop: set) : null, @@ -319,15 +319,15 @@ class WebDavClient { /// If [overwrite] is set any existing resource will be replaced. /// See http://www.webdav.org/specs/rfc2518.html#METHOD_MOVE for more information. Future move( - final String sourcePath, - final String destinationPath, { + final Uri sourcePath, + final Uri destinationPath, { final bool overwrite = false, }) => _send( 'MOVE', - _constructPath(sourcePath), + _constructUri(sourcePath), headers: { - 'Destination': _constructPath(destinationPath), + 'Destination': _constructUri(destinationPath).toString(), 'Overwrite': overwrite ? 'T' : 'F', }, ); @@ -337,15 +337,15 @@ class WebDavClient { /// If [overwrite] is set any existing resource will be replaced. /// See http://www.webdav.org/specs/rfc2518.html#METHOD_COPY for more information. Future copy( - final String sourcePath, - final String destinationPath, { + final Uri sourcePath, + final Uri destinationPath, { final bool overwrite = false, }) => _send( 'COPY', - _constructPath(sourcePath), + _constructUri(sourcePath), headers: { - 'Destination': _constructPath(destinationPath), + 'Destination': _constructUri(destinationPath).toString(), 'Overwrite': overwrite ? 'T' : 'F', }, ); diff --git a/packages/nextcloud/pubspec.yaml b/packages/nextcloud/pubspec.yaml index fb3d93e9..9b017df0 100644 --- a/packages/nextcloud/pubspec.yaml +++ b/packages/nextcloud/pubspec.yaml @@ -17,6 +17,7 @@ dependencies: path: packages/dynamite/dynamite_runtime intl: ^0.18.1 json_annotation: ^4.8.1 + meta: ^1.9.1 universal_io: ^2.2.2 version: ^3.0.2 xml: ^6.3.0 diff --git a/packages/nextcloud/test/webdav_test.dart b/packages/nextcloud/test/webdav_test.dart index b9080ccb..83bc0a65 100644 --- a/packages/nextcloud/test/webdav_test.dart +++ b/packages/nextcloud/test/webdav_test.dart @@ -12,6 +12,20 @@ import 'package:test/test.dart'; import 'helper.dart'; 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())); + baseURL = Uri.parse('http://cloud.example.com/'); + expect(() => WebDavClient.constructUri(baseURL), throwsA(isA())); + }); + group('webdav', () { late DockerImage image; setUpAll(() async => image = await getDockerImage()); @@ -26,7 +40,7 @@ void main() { test('List directory', () async { final responses = (await client.webdav.propfind( - '/', + Uri(path: '/'), prop: WebDavPropWithoutValues.fromBools( nchaspreview: true, davgetcontenttype: true, @@ -46,7 +60,7 @@ void main() { test('List directory recursively', () async { final responses = (await client.webdav.propfind( - '/', + Uri(path: '/'), depth: WebDavDepth.infinity, )) .responses; @@ -55,7 +69,7 @@ void main() { test('Get file props', () async { final response = (await client.webdav.propfind( - 'Nextcloud.png', + Uri(path: 'Nextcloud.png'), prop: WebDavPropWithoutValues.fromBools( davgetlastmodified: true, davgetetag: true, @@ -138,11 +152,11 @@ void main() { test('Get directory props', () async { final data = utf8.encode('test') as Uint8List; - await client.webdav.mkcol('test'); - await client.webdav.put(data, 'test/test.txt'); + await client.webdav.mkcol(Uri(path: 'test')); + await client.webdav.put(data, Uri(path: 'test/test.txt')); final response = (await client.webdav.propfind( - 'test', + Uri(path: 'test'), prop: WebDavPropWithoutValues.fromBools( davgetcontenttype: true, davgetlastmodified: true, @@ -169,17 +183,17 @@ void main() { }); 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; await client.webdav.proppatch( - 'test.txt', + Uri(path: 'test.txt'), set: WebDavProp( ocfavorite: 1, ), ); final responses = (await client.webdav.report( - '/', + Uri(path: '/'), WebDavOcFilterRules( ocfavorite: 1, ), @@ -203,13 +217,13 @@ void main() { await client.webdav.put( utf8.encode('test') as Uint8List, - 'test.txt', + Uri(path: 'test.txt'), lastModified: lastModifiedDate, created: createdDate, ); final updated = await client.webdav.proppatch( - 'test.txt', + Uri(path: 'test.txt'), set: WebDavProp( ocfavorite: 1, ), @@ -217,7 +231,7 @@ void main() { expect(updated, isTrue); final props = (await client.webdav.propfind( - 'test.txt', + Uri(path: 'test.txt'), prop: WebDavPropWithoutValues.fromBools( ocfavorite: true, davgetlastmodified: true, @@ -237,10 +251,10 @@ void main() { }); 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( - 'test.txt', + Uri(path: 'test.txt'), set: WebDavProp( ocfavorite: 1, ), @@ -248,7 +262,7 @@ void main() { expect(updated, isTrue); var props = (await client.webdav.propfind( - 'test.txt', + Uri(path: 'test.txt'), prop: WebDavPropWithoutValues.fromBools( ocfavorite: true, nccreationtime: true, @@ -263,7 +277,7 @@ void main() { expect(props.ocfavorite, 1); updated = await client.webdav.proppatch( - 'test.txt', + Uri(path: 'test.txt'), remove: WebDavPropWithoutValues.fromBools( ocfavorite: true, ), @@ -271,7 +285,7 @@ void main() { expect(updated, isFalse); props = (await client.webdav.propfind( - 'test.txt', + Uri(path: 'test.txt'), prop: WebDavPropWithoutValues.fromBools( ocfavorite: true, ), @@ -293,11 +307,11 @@ void main() { await client.webdav.putFile( source, source.statSync(), - 'test.png', + Uri(path: 'test.png'), onProgress: progressValues.add, ); await client.webdav.getFile( - 'test.png', + Uri(path: 'test.png'), destination, onProgress: progressValues.add, ); @@ -325,32 +339,32 @@ void main() { test(name, () async { 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); - final downloadedContent = await client.webdav.get(path); + final downloadedContent = await client.webdav.get(Uri(path: path)); expect(downloadedContent, equals(content)); }); } test('put_no_parent', () async { 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 throwsA(predicate((final e) => e.statusCode == 409)), ); }); 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); }); test('delete_null', () async { expect( - () => client.webdav.delete('test.txt'), + () => client.webdav.delete(Uri(path: 'test.txt')), throwsA(predicate((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 test('mkcol', () async { - final response = await client.webdav.mkcol('test'); + final response = await client.webdav.mkcol(Uri(path: 'test')); expect(response.statusCode, 201); }); test('mkcol_again', () async { - await client.webdav.mkcol('test'); + await client.webdav.mkcol(Uri(path: 'test')); expect( - () => client.webdav.mkcol('test'), + () => client.webdav.mkcol(Uri(path: 'test')), throwsA(predicate((final e) => e.statusCode == 405)), ); }); 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); }); test('mkcol_no_parent', () async { expect( - () => client.webdav.mkcol('409me/noparent'), + () => client.webdav.mkcol(Uri(path: '409me/noparent')), throwsA(predicate((final e) => e.statusCode == 409)), ); }); @@ -390,110 +404,110 @@ void main() { group('copymove', () { 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); }); test('copy_overwrite', () async { - await client.webdav.mkcol('src'); - await client.webdav.mkcol('dst'); + await client.webdav.mkcol(Uri(path: 'src')); + await client.webdav.mkcol(Uri(path: 'dst')); expect( - () => client.webdav.copy('src', 'dst'), + () => client.webdav.copy(Uri(path: 'src'), Uri(path: 'dst')), throwsA(predicate((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); }); test('copy_nodestcoll', () async { - await client.webdav.mkcol('src'); + await client.webdav.mkcol(Uri(path: 'src')); expect( - () => client.webdav.copy('src', 'nonesuch/dst'), + () => client.webdav.copy(Uri(path: 'src'), Uri(path: 'nonesuch/dst')), throwsA(predicate((final e) => e.statusCode == 409)), ); }); test('copy_coll', () async { - await client.webdav.mkcol('src'); - await client.webdav.mkcol('src/sub'); + await client.webdav.mkcol(Uri(path: 'src')); + await client.webdav.mkcol(Uri(path: 'src/sub')); 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('src', 'dst2'); + await client.webdav.copy(Uri(path: 'src'), Uri(path: 'dst1')); + await client.webdav.copy(Uri(path: 'src'), Uri(path: 'dst2')); expect( - () => client.webdav.copy('src', 'dst1'), + () => client.webdav.copy(Uri(path: 'src'), Uri(path: 'dst1')), throwsA(predicate((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); 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); } - response = await client.webdav.delete('dst1/sub'); + response = await client.webdav.delete(Uri(path: 'dst1/sub')); expect(response.statusCode, 204); - response = await client.webdav.delete('dst2'); + response = await client.webdav.delete(Uri(path: 'dst2')); expect(response.statusCode, 204); }); // copy_shallow: Does not work on litmus, let's wait for https://github.com/nextcloud/server/issues/39627 test('move', () async { - await client.webdav.put(Uint8List(0), 'src1.txt'); - await client.webdav.put(Uint8List(0), 'src2.txt'); - await client.webdav.mkcol('coll'); + await client.webdav.put(Uint8List(0), Uri(path: 'src1.txt')); + await client.webdav.put(Uint8List(0), Uri(path: 'src2.txt')); + 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( - () => client.webdav.move('src2.txt', 'dst.txt'), + () => client.webdav.move(Uri(path: 'src2.txt'), Uri(path: 'dst.txt')), throwsA(predicate((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); }); test('move_coll', () async { - await client.webdav.mkcol('src'); - await client.webdav.mkcol('src/sub'); + await client.webdav.mkcol(Uri(path: 'src')); + await client.webdav.mkcol(Uri(path: 'src/sub')); 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.copy('src', 'dst2'); - await client.webdav.move('src', 'dst1'); + await client.webdav.put(Uint8List(0), Uri(path: 'noncoll')); + await client.webdav.copy(Uri(path: 'src'), Uri(path: 'dst2')); + await client.webdav.move(Uri(path: 'src'), Uri(path: 'dst1')); expect( - () => client.webdav.move('dst1', 'dst2'), + () => client.webdav.move(Uri(path: 'dst1'), Uri(path: 'dst2')), throwsA(predicate((final e) => e.statusCode == 412)), ); - await client.webdav.move('dst2', 'dst1', overwrite: true); - await client.webdav.copy('dst1', 'dst2'); + await client.webdav.move(Uri(path: 'dst2'), Uri(path: 'dst1'), overwrite: true); + await client.webdav.copy(Uri(path: 'dst1'), Uri(path: 'dst2')); 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); } - final response = await client.webdav.delete('dst1/sub'); + final response = await client.webdav.delete(Uri(path: 'dst1/sub')); expect(response.statusCode, 204); expect( - () => client.webdav.move('dst2', 'noncoll'), + () => client.webdav.move(Uri(path: 'dst2'), Uri(path: 'noncoll')), throwsA(predicate((final e) => e.statusCode == 412)), ); }); @@ -505,10 +519,10 @@ void main() { // large_put: Already covered by large_get 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); - final downloadedContent = await client.webdav.get('test.txt'); + final downloadedContent = await client.webdav.get(Uri(path: 'test.txt')); expect(downloadedContent, hasLength(largefileSize)); }); });