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. 98
      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 '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<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,
() 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<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) {
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<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
void delete(final List<String> path) {
wrapAction(() async => account.client.webdav.delete(path.join('/')));
wrapAction(() async => account.client.webdav.delete(Uri(pathSegments: path)));
}
@override
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
@ -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<String> 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<String> 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),
),
);
}

4
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);

3
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: [

98
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<HttpClientResponse> _send(
final String method,
final String url, {
final Uri url, {
final Stream<Uint8List>? dataStream,
final Uint8List? data,
final Map<String, String>? 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);
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);
}
while (part.endsWith('/')) {
part = part.substring(0, part.length - 1); // coverage:ignore-line
return baseURL.replace(path: pathBuffer.toString());
}
return part;
})
.where((final part) => part.isNotEmpty)
.join('/');
Future<WebDavMultistatus> _parseResponse(final HttpClientResponse response) async =>
WebDavMultistatus.fromXmlElement(xml.XmlDocument.parse(await response.body).rootElement);
@ -99,7 +99,7 @@ class WebDavClient {
Future<WebDavOptions> 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<HttpClientResponse> mkcol(final String path) async => _send(
Future<HttpClientResponse> 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<HttpClientResponse> delete(final String path) => _send(
Future<HttpClientResponse> 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<HttpClientResponse> 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<HttpClientResponse> putStream(
final Stream<Uint8List> 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<HttpClientResponse> 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<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].
Future<HttpClientResponse> getStream(final String path) async => _send(
Future<HttpClientResponse> 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<WebDavMultistatus> 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<WebDavMultistatus> 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<bool> 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<HttpClientResponse> 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<HttpClientResponse> 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',
},
);

1
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

152
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<AssertionError>()));
baseURL = Uri.parse('http://cloud.example.com/');
expect(() => WebDavClient.constructUri(baseURL), throwsA(isA<AssertionError>()));
});
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<DynamiteApiException>((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<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
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<DynamiteApiException>((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<DynamiteApiException>((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<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);
});
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<DynamiteApiException>((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<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);
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<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);
});
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<DynamiteApiException>((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<DynamiteApiException>((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));
});
});

Loading…
Cancel
Save