Browse Source

refactor(nextcloud): Rename WebDAV methods to their HTTP methods and add documentation

pull/520/head
jld3103 1 year ago
parent
commit
6acd512cef
No known key found for this signature in database
GPG Key ID: 9062417B9E8EB7B3
  1. 139
      packages/nextcloud/lib/src/webdav/client.dart
  2. 75
      packages/nextcloud/test/webdav_test.dart

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

@ -76,8 +76,8 @@ class WebDavClient {
.where((final part) => part.isNotEmpty)
.join('/');
/// returns the WebDAV capabilities of the server
Future<WebDavStatus> status() async {
/// Gets the WebDAV capabilities of the server.
Future<WebDavOptions> options() async {
final response = await _send(
'OPTIONS',
_constructPath(),
@ -85,14 +85,16 @@ class WebDavClient {
);
final davCapabilities = response.headers['dav']?.cast<String>().first ?? '';
final davSearchCapabilities = response.headers['dasl']?.cast<String>().first ?? '';
return WebDavStatus(
return WebDavOptions(
davCapabilities.split(',').map((final e) => e.trim()).where((final e) => e.isNotEmpty).toSet(),
davSearchCapabilities.split(',').map((final e) => e.trim()).where((final e) => e.isNotEmpty).toSet(),
);
}
/// make a dir with [path] under current dir
Future<HttpClientResponse> mkdir(
/// Creates a collection at [path].
///
/// See http://www.webdav.org/specs/rfc2518.html#METHOD_MKCOL for more information.
Future<HttpClientResponse> mkcol(
final String path, {
final bool safe = true,
}) async {
@ -110,32 +112,9 @@ class WebDavClient {
);
}
/// just like mkdir -p
Future<HttpClientResponse?> mkdirs(
final String path, {
final bool safe = true,
}) async {
final dirs = path.trim().split('/')..removeWhere((final value) => value == '');
if (dirs.isEmpty) {
return null;
}
if (path.trim().startsWith('/')) {
dirs[0] = '/${dirs[0]}'; // coverage:ignore-line
}
final prevPath = StringBuffer();
late HttpClientResponse response;
for (final dir in dirs) {
response = await mkdir(
'$prevPath/$dir',
safe: safe,
);
prevPath.write('/$dir');
}
return response;
}
/// remove dir with given [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(
'DELETE',
_constructPath(path),
@ -161,14 +140,18 @@ class WebDavClient {
return headers.isNotEmpty ? headers : null;
}
/// upload a new file with [localData] as content to [path]
Future<HttpClientResponse> upload(
/// Puts a new file at [path] with [localData] as content.
///
/// [lastModified] sets the date when the file was last modified on the server.
/// [created] sets the date when the file was created on the server.
/// See http://www.webdav.org/specs/rfc2518.html#METHOD_PUT for more information.
Future<HttpClientResponse> put(
final Uint8List localData,
final String path, {
final DateTime? lastModified,
final DateTime? created,
}) =>
uploadStream(
putStream(
Stream.value(localData),
path,
lastModified: lastModified,
@ -176,8 +159,13 @@ class WebDavClient {
contentLength: localData.lengthInBytes,
);
/// upload a new file with [localData] as content to [path]
Future<HttpClientResponse> uploadStream(
/// Puts a new file at [path] with [localData] as content.
///
/// [lastModified] sets the date when the file was last modified on the server.
/// [created] sets the date when the file was created on the server.
/// [contentLength] sets the length of the [localData] that is uploaded.
/// See http://www.webdav.org/specs/rfc2518.html#METHOD_PUT for more information.
Future<HttpClientResponse> putStream(
final Stream<Uint8List> localData,
final String path, {
final DateTime? lastModified,
@ -196,11 +184,11 @@ class WebDavClient {
),
);
/// download [path] and store the response file contents to String
Future<Uint8List> download(final String path) async => (await downloadStream(path)).bodyBytes;
/// Gets the content of the file at [path].
Future<Uint8List> get(final String path) async => (await getStream(path)).bodyBytes;
/// download [path] and store the response file contents to ByteStream
Future<HttpClientResponse> downloadStream(final String path) async => _send(
/// Gets the content of the file at [path].
Future<HttpClientResponse> getStream(final String path) async => _send(
'GET',
_constructPath(path),
[200],
@ -209,11 +197,12 @@ class WebDavClient {
Future<WebDavMultistatus> _parseResponse(final HttpClientResponse response) async =>
WebDavMultistatus.fromXmlElement(xml.XmlDocument.parse(await response.body).rootElement);
/// list the directories and files under given [path].
/// Retrieves the props for the resource at [path].
///
/// Optionally populates the given [prop]s on the returned files.
/// [depth] can be '0', '1' or 'infinity'.
Future<WebDavMultistatus> ls(
/// See http://www.webdav.org/specs/rfc2518.html#METHOD_PROPFIND for more information.
Future<WebDavMultistatus> propfind(
final String path, {
final WebDavPropfindProp? prop,
final String? depth,
@ -238,7 +227,7 @@ class WebDavClient {
);
if (response.statusCode == 301) {
// coverage:ignore-start
return ls(
return propfind(
response.headers['location']!.first,
prop: prop,
depth: depth,
@ -248,37 +237,38 @@ class WebDavClient {
return _parseResponse(response);
}
/// Runs the filter-files report with the given [filterRules] on the
/// [path].
/// Runs the filter-files report with the [filterRules] on the resource at [path].
///
/// Optionally populates the given [prop]s on the returned files.
Future<WebDavMultistatus> filter(
/// Optionally populates the [prop]s on the returned files.
/// See https://github.com/owncloud/docs/issues/359 for more information.
Future<WebDavMultistatus> report(
final String path,
final WebDavOcFilterRules filterRules, {
final WebDavPropfindProp? prop,
}) async {
final response = await _send(
'REPORT',
_constructPath(path),
[200, 207],
data: Stream.value(
Uint8List.fromList(
utf8.encode(
WebDavOcFilterFiles(
filterRules: filterRules,
prop: prop ?? WebDavPropfindProp(), // coverage:ignore-line
).toXmlElement(namespaces: namespaces).toXmlString(),
}) async =>
_parseResponse(
await _send(
'REPORT',
_constructPath(path),
[200, 207],
data: Stream.value(
Uint8List.fromList(
utf8.encode(
WebDavOcFilterFiles(
filterRules: filterRules,
prop: prop ?? WebDavPropfindProp(), // coverage:ignore-line
).toXmlElement(namespaces: namespaces).toXmlString(),
),
),
),
),
),
);
return _parseResponse(response);
}
);
/// Update (string) properties of the given [path].
/// Updates the props of the resource at [path].
///
/// Returns true if the update was successful.
Future<bool> updateProps(
/// See http://www.webdav.org/specs/rfc2518.html#METHOD_PROPPATCH for more information.
Future<bool> proppatch(
final String path,
final WebDavProp prop,
) async {
@ -305,7 +295,10 @@ class WebDavClient {
return true;
}
/// Move a file from [sourcePath] to [destinationPath]
/// Moves the resource from [sourcePath] to [destinationPath].
///
/// 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, {
@ -321,7 +314,10 @@ class WebDavClient {
},
);
/// Copy a file from [sourcePath] to [destinationPath]
/// Copies the resource from [sourcePath] to [destinationPath].
///
/// 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, {
@ -338,10 +334,10 @@ class WebDavClient {
);
}
/// WebDAV server status.
class WebDavStatus {
/// WebDAV capabilities
class WebDavOptions {
/// Creates a new WebDavStatus.
WebDavStatus(
WebDavOptions(
this.capabilities,
this.searchCapabilities,
);
@ -349,7 +345,6 @@ class WebDavStatus {
/// DAV capabilities as advertised by the server in the 'dav' header.
Set<String> capabilities;
/// DAV search and locating capabilities as advertised by the server in the
/// 'dasl' header.
/// DAV search and locating capabilities as advertised by the server in the 'dasl' header.
Set<String> searchCapabilities;
}

75
packages/nextcloud/test/webdav_test.dart

@ -24,13 +24,13 @@ void main() {
tearDown(() => container.destroy());
test('Get status', () async {
final status = await client.webdav.status();
expect(status.capabilities, containsAll(['1', '3']));
expect(status.searchCapabilities, hasLength(0));
final options = await client.webdav.options();
expect(options.capabilities, containsAll(['1', '3']));
expect(options.searchCapabilities, hasLength(0));
});
test('List directory', () async {
final responses = (await client.webdav.ls(
final responses = (await client.webdav.propfind(
'/',
prop: WebDavPropfindProp.fromBools(
nchaspreview: true,
@ -50,7 +50,7 @@ void main() {
});
test('List directory recursively', () async {
final responses = (await client.webdav.ls(
final responses = (await client.webdav.propfind(
'/',
depth: 'infinity',
))
@ -59,30 +59,21 @@ void main() {
});
test('Create directory', () async {
final response = await client.webdav.mkdir('test');
final response = await client.webdav.mkcol('test');
expect(response.statusCode, equals(201));
});
test('Create directory recursively', () async {
final response = await client.webdav.mkdirs('test/bla');
expect(response!.statusCode, equals(201));
final responses = (await client.webdav.ls('/test')).responses;
expect(responses, hasLength(2));
expect(responses[1].href, endsWith('/test/bla/'));
});
test('Upload files', () async {
final pngBytes = File('test/files/test.png').readAsBytesSync();
final txtBytes = File('test/files/test.txt').readAsBytesSync();
var response = await client.webdav.upload(pngBytes, 'test.png');
var response = await client.webdav.put(pngBytes, 'test.png');
expect(response.statusCode, equals(201));
response = await client.webdav.upload(txtBytes, 'test.txt');
response = await client.webdav.put(txtBytes, 'test.txt');
expect(response.statusCode, equals(201));
final responses = (await client.webdav.ls(
final responses = (await client.webdav.propfind(
'/',
prop: WebDavPropfindProp.fromBools(
ocsize: true,
@ -105,7 +96,7 @@ void main() {
final created = lastModified.subtract(const Duration(hours: 1));
final txtBytes = File('test/files/test.txt').readAsBytesSync();
final response = await client.webdav.upload(
final response = await client.webdav.put(
txtBytes,
'test.txt',
lastModified: lastModified,
@ -113,7 +104,7 @@ void main() {
);
expect(response.statusCode, equals(201));
final props = (await client.webdav.ls(
final props = (await client.webdav.propfind(
'/',
prop: WebDavPropfindProp.fromBools(
davgetlastmodified: true,
@ -136,14 +127,14 @@ void main() {
});
test('Download file', () async {
final response = await client.webdav.download('Nextcloud.png');
final response = await client.webdav.get('Nextcloud.png');
expect(response, isNotEmpty);
});
test('Delete file', () async {
final response = await client.webdav.delete('Nextcloud.png');
expect(response.statusCode, 204);
final responses = (await client.webdav.ls('/')).responses;
final responses = (await client.webdav.propfind('/')).responses;
expect(responses.where((final response) => response.href!.endsWith('/Nextcloud.png')), hasLength(0));
});
@ -153,14 +144,14 @@ void main() {
'test.png',
);
expect(response.statusCode, 201);
final responses = (await client.webdav.ls('/')).responses;
final responses = (await client.webdav.propfind('/')).responses;
expect(responses.where((final response) => response.href!.endsWith('/Nextcloud.png')), hasLength(1));
expect(responses.where((final response) => response.href!.endsWith('/test.png')), hasLength(1));
});
test('Copy file (overwrite fail)', () async {
await client.webdav.upload(Uint8List.fromList(utf8.encode('1')), '1.txt');
await client.webdav.upload(Uint8List.fromList(utf8.encode('2')), '2.txt');
await client.webdav.put(Uint8List.fromList(utf8.encode('1')), '1.txt');
await client.webdav.put(Uint8List.fromList(utf8.encode('2')), '2.txt');
expect(
() => client.webdav.copy('1.txt', '2.txt'),
@ -169,8 +160,8 @@ void main() {
});
test('Copy file (overwrite success)', () async {
await client.webdav.upload(Uint8List.fromList(utf8.encode('1')), '1.txt');
await client.webdav.upload(Uint8List.fromList(utf8.encode('2')), '2.txt');
await client.webdav.put(Uint8List.fromList(utf8.encode('1')), '1.txt');
await client.webdav.put(Uint8List.fromList(utf8.encode('2')), '2.txt');
final response = await client.webdav.copy(
'1.txt',
@ -186,14 +177,14 @@ void main() {
'test.png',
);
expect(response.statusCode, 201);
final responses = (await client.webdav.ls('/')).responses;
final responses = (await client.webdav.propfind('/')).responses;
expect(responses.where((final response) => response.href!.endsWith('/Nextcloud.png')), hasLength(0));
expect(responses.where((final response) => response.href!.endsWith('/test.png')), hasLength(1));
});
test('Move file (overwrite fail)', () async {
await client.webdav.upload(Uint8List.fromList(utf8.encode('1')), '1.txt');
await client.webdav.upload(Uint8List.fromList(utf8.encode('2')), '2.txt');
await client.webdav.put(Uint8List.fromList(utf8.encode('1')), '1.txt');
await client.webdav.put(Uint8List.fromList(utf8.encode('2')), '2.txt');
expect(
() => client.webdav.move('1.txt', '2.txt'),
@ -202,8 +193,8 @@ void main() {
});
test('Move file (overwrite success)', () async {
await client.webdav.upload(Uint8List.fromList(utf8.encode('1')), '1.txt');
await client.webdav.upload(Uint8List.fromList(utf8.encode('2')), '2.txt');
await client.webdav.put(Uint8List.fromList(utf8.encode('1')), '1.txt');
await client.webdav.put(Uint8List.fromList(utf8.encode('2')), '2.txt');
final response = await client.webdav.move(
'1.txt',
@ -214,7 +205,7 @@ void main() {
});
test('Get file props', () async {
final response = (await client.webdav.ls(
final response = (await client.webdav.propfind(
'Nextcloud.png',
prop: WebDavPropfindProp.fromBools(
davgetlastmodified: true,
@ -298,10 +289,10 @@ void main() {
test('Get directory props', () async {
final data = Uint8List.fromList(utf8.encode('test'));
await client.webdav.mkdir('test');
await client.webdav.upload(data, 'test/test.txt');
await client.webdav.mkcol('test');
await client.webdav.put(data, 'test/test.txt');
final response = (await client.webdav.ls(
final response = (await client.webdav.propfind(
'test',
prop: WebDavPropfindProp.fromBools(
davgetcontenttype: true,
@ -329,16 +320,16 @@ void main() {
});
test('Filter files', () async {
final response = await client.webdav.upload(Uint8List.fromList(utf8.encode('test')), 'test.txt');
final response = await client.webdav.put(Uint8List.fromList(utf8.encode('test')), 'test.txt');
final id = response.headers['oc-fileid']!.first;
await client.webdav.updateProps(
await client.webdav.proppatch(
'test.txt',
WebDavProp(
ocfavorite: 1,
),
);
final responses = (await client.webdav.filter(
final responses = (await client.webdav.report(
'/',
WebDavOcFilterRules(
ocfavorite: 1,
@ -361,9 +352,9 @@ void main() {
final createdEpoch = createdDate.millisecondsSinceEpoch ~/ 1000;
final uploadTime = DateTime.now();
await client.webdav.upload(Uint8List.fromList(utf8.encode('test')), 'test.txt');
await client.webdav.put(Uint8List.fromList(utf8.encode('test')), 'test.txt');
final updated = await client.webdav.updateProps(
final updated = await client.webdav.proppatch(
'test.txt',
WebDavProp(
ocfavorite: 1,
@ -372,7 +363,7 @@ void main() {
);
expect(updated, isTrue);
final props = (await client.webdav.ls(
final props = (await client.webdav.propfind(
'test.txt',
prop: WebDavPropfindProp.fromBools(
ocfavorite: true,

Loading…
Cancel
Save