Browse Source

Merge pull request #520 from provokateurin/refactor/webdav-methods

Refactor/webdav methods
pull/522/head
Kate 1 year ago committed by GitHub
parent
commit
0c6c44d1fb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 11
      packages/neon/neon_files/lib/blocs/browser.dart
  2. 11
      packages/neon/neon_files/lib/blocs/files.dart
  3. 2
      packages/neon/neon_files/lib/utils/download_task.dart
  4. 2
      packages/neon/neon_files/lib/utils/upload_task.dart
  5. 2
      packages/nextcloud/bin/generate_props.dart
  6. 247
      packages/nextcloud/lib/src/webdav/client.dart
  7. 9
      packages/nextcloud/lib/src/webdav/props.dart
  8. 27
      packages/nextcloud/lib/src/webdav/props.g.dart
  9. 27
      packages/nextcloud/lib/src/webdav/webdav.dart
  10. 97
      packages/nextcloud/lib/src/webdav/webdav.g.dart
  11. 141
      packages/nextcloud/test/webdav_test.dart

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

@ -44,9 +44,9 @@ class FilesBrowserBloc extends InteractiveBloc implements FilesBrowserBlocEvents
client.id, client.id,
'files-${path.value.join('/')}', 'files-${path.value.join('/')}',
files, files,
() async => client.webdav.ls( () async => client.webdav.propfind(
path.value.join('/'), path.value.join('/'),
prop: WebDavPropfindProp.fromBools( prop: WebDavPropWithoutValues.fromBools(
davgetcontenttype: true, davgetcontenttype: true,
davgetetag: true, davgetetag: true,
davgetlastmodified: true, davgetlastmodified: true,
@ -69,11 +69,6 @@ class FilesBrowserBloc extends InteractiveBloc implements FilesBrowserBlocEvents
@override @override
void createFolder(final List<String> path) { void createFolder(final List<String> path) {
wrapAction( wrapAction(() async => client.webdav.mkcol(path.join('/')));
() async => client.webdav.mkdir(
path.join('/'),
safe: false,
),
);
} }
} }

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

@ -65,7 +65,12 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta
@override @override
void addFavorite(final List<String> path) { void addFavorite(final List<String> path) {
wrapAction(() async => client.webdav.updateProps(path.join('/'), WebDavProp(ocfavorite: 1))); wrapAction(
() async => client.webdav.proppatch(
path.join('/'),
set: WebDavProp(ocfavorite: 1),
),
);
} }
@override @override
@ -119,9 +124,9 @@ class FilesBloc extends InteractiveBloc implements FilesBlocEvents, FilesBlocSta
@override @override
void removeFavorite(final List<String> path) { void removeFavorite(final List<String> path) {
wrapAction( wrapAction(
() async => client.webdav.updateProps( () async => client.webdav.proppatch(
path.join('/'), path.join('/'),
WebDavProp(ocfavorite: 0), set: WebDavProp(ocfavorite: 0),
), ),
); );
} }

2
packages/neon/neon_files/lib/utils/download_task.dart

@ -13,7 +13,7 @@ class DownloadTask {
Future execute(final NextcloudClient client, final IOSink sink) async { Future execute(final NextcloudClient client, final IOSink sink) async {
final completer = Completer(); final completer = Completer();
final response = await client.webdav.downloadStream(path.join('/')); final response = await client.webdav.getStream(path.join('/'));
var downloaded = 0; var downloaded = 0;
response.listen((final chunk) async { response.listen((final chunk) async {

2
packages/neon/neon_files/lib/utils/upload_task.dart

@ -16,7 +16,7 @@ class UploadTask {
Future execute(final NextcloudClient client, final Stream<List<int>> stream) async { Future execute(final NextcloudClient client, final Stream<List<int>> stream) async {
var uploaded = 0; var uploaded = 0;
await client.webdav.uploadStream( await client.webdav.putStream(
stream.map((final chunk) { stream.map((final chunk) {
uploaded += chunk.length; uploaded += chunk.length;
_streamController.add((uploaded / size * 100).toInt()); _streamController.add((uploaded / size * 100).toInt());

2
packages/nextcloud/bin/generate_props.dart

@ -29,7 +29,7 @@ void main() {
"part 'props.g.dart';", "part 'props.g.dart';",
'', '',
...generateClass( ...generateClass(
'WebDavPropfindProp', 'WebDavPropWithoutValues',
'prop', 'prop',
'namespaceDav', 'namespaceDav',
findProps, findProps,

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

@ -20,8 +20,7 @@ class WebDavClient {
Future<HttpClientResponse> _send( Future<HttpClientResponse> _send(
final String method, final String method,
final String url, final String url, {
final List<int> expectedCodes, {
final Stream<Uint8List>? data, final Stream<Uint8List>? data,
final Map<String, String>? headers, final Map<String, String>? headers,
}) async { }) async {
@ -29,7 +28,6 @@ class WebDavClient {
method, method,
Uri.parse(url), Uri.parse(url),
) )
..followRedirects = false
..persistentConnection = true; ..persistentConnection = true;
for (final header in { for (final header in {
HttpHeaders.contentTypeHeader: 'application/xml', HttpHeaders.contentTypeHeader: 'application/xml',
@ -46,7 +44,7 @@ class WebDavClient {
final response = await request.close(); final response = await request.close();
if (!expectedCodes.contains(response.statusCode)) { if (response.statusCode > 299) {
throw DynamiteApiException( throw DynamiteApiException(
response.statusCode, response.statusCode,
response.responseHeaders, response.responseHeaders,
@ -76,73 +74,10 @@ class WebDavClient {
.where((final part) => part.isNotEmpty) .where((final part) => part.isNotEmpty)
.join('/'); .join('/');
/// returns the WebDAV capabilities of the server Future<WebDavMultistatus> _parseResponse(final HttpClientResponse response) async =>
Future<WebDavStatus> status() async { WebDavMultistatus.fromXmlElement(xml.XmlDocument.parse(await response.body).rootElement);
final response = await _send(
'OPTIONS',
_constructPath(),
[200],
);
final davCapabilities = response.headers['dav']?.cast<String>().first ?? '';
final davSearchCapabilities = response.headers['dasl']?.cast<String>().first ?? '';
return WebDavStatus(
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 [remotePath] under current dir
Future<HttpClientResponse> mkdir(
final String remotePath, {
final bool safe = true,
}) async {
final expectedCodes = [
201,
if (safe) ...[
301,
405,
],
];
return _send(
'MKCOL',
_constructPath(remotePath),
expectedCodes,
);
}
/// just like mkdir -p
Future<HttpClientResponse?> mkdirs(
final String remotePath, {
final bool safe = true,
}) async {
final dirs = remotePath.trim().split('/')..removeWhere((final value) => value == '');
if (dirs.isEmpty) {
return null;
}
if (remotePath.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]
Future<HttpClientResponse> delete(final String path) => _send(
'DELETE',
_constructPath(path),
[204],
);
Map<String, String>? _generateUploadHeaders({ Map<String, String>? _getUploadHeaders({
required final DateTime? lastModified, required final DateTime? lastModified,
required final DateTime? created, required final DateTime? created,
required final int? contentLength, required final int? contentLength,
@ -161,72 +96,109 @@ class WebDavClient {
return headers.isNotEmpty ? headers : null; return headers.isNotEmpty ? headers : null;
} }
/// upload a new file with [localData] as content to [remotePath] /// Gets the WebDAV capabilities of the server.
Future<HttpClientResponse> upload( Future<WebDavOptions> options() async {
final response = await _send(
'OPTIONS',
_constructPath(),
);
final davCapabilities = response.headers['dav']?.cast<String>().first ?? '';
final davSearchCapabilities = response.headers['dasl']?.cast<String>().first ?? '';
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(),
);
}
/// 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(
'MKCOL',
_constructPath(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),
);
/// 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 Uint8List localData,
final String remotePath, { final String path, {
final DateTime? lastModified, final DateTime? lastModified,
final DateTime? created, final DateTime? created,
}) => }) =>
uploadStream( putStream(
Stream.value(localData), Stream.value(localData),
remotePath, path,
lastModified: lastModified, lastModified: lastModified,
created: created, created: created,
contentLength: localData.lengthInBytes, contentLength: localData.lengthInBytes,
); );
/// upload a new file with [localData] as content to [remotePath] /// Puts a new file at [path] with [localData] as content.
Future<HttpClientResponse> uploadStream( ///
/// [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 Stream<Uint8List> localData,
final String remotePath, { final String path, {
final DateTime? lastModified, final DateTime? lastModified,
final DateTime? created, final DateTime? created,
final int? contentLength, final int? contentLength,
}) async => }) async =>
_send( _send(
'PUT', 'PUT',
_constructPath(remotePath), _constructPath(path),
[200, 201, 204],
data: localData, data: localData,
headers: _generateUploadHeaders( headers: _getUploadHeaders(
lastModified: lastModified, lastModified: lastModified,
created: created, created: created,
contentLength: contentLength, contentLength: contentLength,
), ),
); );
/// download [remotePath] and store the response file contents to String /// Gets the content of the file at [path].
Future<Uint8List> download(final String remotePath) async => (await downloadStream(remotePath)).bodyBytes; Future<Uint8List> get(final String path) async => (await getStream(path)).bodyBytes;
/// download [remotePath] and store the response file contents to ByteStream /// Gets the content of the file at [path].
Future<HttpClientResponse> downloadStream(final String remotePath) async => _send( Future<HttpClientResponse> getStream(final String path) async => _send(
'GET', 'GET',
_constructPath(remotePath), _constructPath(path),
[200],
); );
Future<WebDavMultistatus> _parseResponse(final HttpClientResponse response) async => /// Retrieves the props for the resource at [path].
WebDavMultistatus.fromXmlElement(xml.XmlDocument.parse(await response.body).rootElement);
/// list the directories and files under given [remotePath].
/// ///
/// Optionally populates the given [prop]s on the returned files. /// Optionally populates the given [prop]s on the returned files.
/// [depth] can be '0', '1' or 'infinity'. /// [depth] can be '0', '1' or 'infinity'.
Future<WebDavMultistatus> ls( /// See http://www.webdav.org/specs/rfc2518.html#METHOD_PROPFIND for more information.
final String remotePath, { Future<WebDavMultistatus> propfind(
final WebDavPropfindProp? prop, final String path, {
final WebDavPropWithoutValues? prop,
final String? depth, final String? depth,
}) async { }) async {
assert(depth == null || ['0', '1', 'infinity'].contains(depth), 'Depth has to be 0, 1 or infinity'); assert(depth == null || ['0', '1', 'infinity'].contains(depth), 'Depth has to be 0, 1 or infinity');
final response = await _send( return _parseResponse(
await _send(
'PROPFIND', 'PROPFIND',
_constructPath(remotePath), _constructPath(path),
[207, 301],
data: Stream.value( data: Stream.value(
Uint8List.fromList( Uint8List.fromList(
utf8.encode( utf8.encode(
WebDavPropfind(prop: prop ?? WebDavPropfindProp()).toXmlElement(namespaces: namespaces).toXmlString(), WebDavPropfind(prop: prop ?? WebDavPropWithoutValues())
.toXmlElement(namespaces: namespaces)
.toXmlString(),
), ),
), ),
), ),
@ -235,61 +207,57 @@ class WebDavClient {
'Depth': depth, 'Depth': depth,
}, },
}, },
),
); );
if (response.statusCode == 301) {
// coverage:ignore-start
return ls(
response.headers['location']!.first,
prop: prop,
depth: depth,
);
// coverage:ignore-end
}
return _parseResponse(response);
} }
/// Runs the filter-files report with the given [filterRules] on the /// Runs the filter-files report with the [filterRules] on the resource at [path].
/// [remotePath].
/// ///
/// Optionally populates the given [prop]s on the returned files. /// Optionally populates the [prop]s on the returned files.
Future<WebDavMultistatus> filter( /// See https://github.com/owncloud/docs/issues/359 for more information.
final String remotePath, Future<WebDavMultistatus> report(
final String path,
final WebDavOcFilterRules filterRules, { final WebDavOcFilterRules filterRules, {
final WebDavPropfindProp? prop, final WebDavPropWithoutValues? prop,
}) async { }) async =>
final response = await _send( _parseResponse(
await _send(
'REPORT', 'REPORT',
_constructPath(remotePath), _constructPath(path),
[200, 207],
data: Stream.value( data: Stream.value(
Uint8List.fromList( Uint8List.fromList(
utf8.encode( utf8.encode(
WebDavOcFilterFiles( WebDavOcFilterFiles(
filterRules: filterRules, filterRules: filterRules,
prop: prop ?? WebDavPropfindProp(), // coverage:ignore-line prop: prop ?? WebDavPropWithoutValues(), // coverage:ignore-line
).toXmlElement(namespaces: namespaces).toXmlString(), ).toXmlElement(namespaces: namespaces).toXmlString(),
), ),
), ),
), ),
),
); );
return _parseResponse(response);
}
/// Update (string) properties of the given [remotePath]. /// Updates the props of the resource at [path].
/// ///
/// The props in [set] will be updated.
/// The props in [remove] will be removed.
/// Returns true if the update was successful. /// Returns true if the update was successful.
Future<bool> updateProps( /// See http://www.webdav.org/specs/rfc2518.html#METHOD_PROPPATCH for more information.
final String remotePath, Future<bool> proppatch(
final WebDavProp prop, final String path, {
) async { final WebDavProp? set,
final WebDavPropWithoutValues? remove,
}) async {
final response = await _send( final response = await _send(
'PROPPATCH', 'PROPPATCH',
_constructPath(remotePath), _constructPath(path),
[200, 207],
data: Stream.value( data: Stream.value(
Uint8List.fromList( Uint8List.fromList(
utf8.encode( utf8.encode(
WebDavPropertyupdate(set: WebDavSet(prop: prop)).toXmlElement(namespaces: namespaces).toXmlString(), WebDavPropertyupdate(
set: set != null ? WebDavSet(prop: set) : null,
remove: remove != null ? WebDavRemove(prop: remove) : null,
).toXmlElement(namespaces: namespaces).toXmlString(),
), ),
), ),
), ),
@ -305,7 +273,10 @@ class WebDavClient {
return true; 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( Future<HttpClientResponse> move(
final String sourcePath, final String sourcePath,
final String destinationPath, { final String destinationPath, {
@ -314,14 +285,16 @@ class WebDavClient {
_send( _send(
'MOVE', 'MOVE',
_constructPath(sourcePath), _constructPath(sourcePath),
[200, 201, 204],
headers: { headers: {
'Destination': _constructPath(destinationPath), 'Destination': _constructPath(destinationPath),
'Overwrite': overwrite ? 'T' : 'F', 'Overwrite': overwrite ? 'T' : 'F',
}, },
); );
/// 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( Future<HttpClientResponse> copy(
final String sourcePath, final String sourcePath,
final String destinationPath, { final String destinationPath, {
@ -330,7 +303,6 @@ class WebDavClient {
_send( _send(
'COPY', 'COPY',
_constructPath(sourcePath), _constructPath(sourcePath),
[200, 201, 204],
headers: { headers: {
'Destination': _constructPath(destinationPath), 'Destination': _constructPath(destinationPath),
'Overwrite': overwrite ? 'T' : 'F', 'Overwrite': overwrite ? 'T' : 'F',
@ -338,10 +310,10 @@ class WebDavClient {
); );
} }
/// WebDAV server status. /// WebDAV capabilities
class WebDavStatus { class WebDavOptions {
/// Creates a new WebDavStatus. /// Creates a new WebDavStatus.
WebDavStatus( WebDavOptions(
this.capabilities, this.capabilities,
this.searchCapabilities, this.searchCapabilities,
); );
@ -349,7 +321,6 @@ class WebDavStatus {
/// DAV capabilities as advertised by the server in the 'dav' header. /// DAV capabilities as advertised by the server in the 'dav' header.
Set<String> capabilities; Set<String> capabilities;
/// DAV search and locating capabilities as advertised by the server in the /// DAV search and locating capabilities as advertised by the server in the 'dasl' header.
/// 'dasl' header.
Set<String> searchCapabilities; Set<String> searchCapabilities;
} }

9
packages/nextcloud/lib/src/webdav/props.dart

@ -7,8 +7,8 @@ part 'props.g.dart';
@annotation.XmlSerializable(createMixin: true) @annotation.XmlSerializable(createMixin: true)
@annotation.XmlRootElement(name: 'prop', namespace: namespaceDav) @annotation.XmlRootElement(name: 'prop', namespace: namespaceDav)
class WebDavPropfindProp with _$WebDavPropfindPropXmlSerializableMixin { class WebDavPropWithoutValues with _$WebDavPropWithoutValuesXmlSerializableMixin {
WebDavPropfindProp({ WebDavPropWithoutValues({
this.davgetlastmodified, this.davgetlastmodified,
this.davgetetag, this.davgetetag,
this.davgetcontenttype, this.davgetcontenttype,
@ -38,7 +38,7 @@ class WebDavPropfindProp with _$WebDavPropfindPropXmlSerializableMixin {
this.ocmsharepermissions, this.ocmsharepermissions,
}); });
WebDavPropfindProp.fromBools({ WebDavPropWithoutValues.fromBools({
final bool davgetlastmodified = false, final bool davgetlastmodified = false,
final bool davgetetag = false, final bool davgetetag = false,
final bool davgetcontenttype = false, final bool davgetcontenttype = false,
@ -94,7 +94,8 @@ class WebDavPropfindProp with _$WebDavPropfindPropXmlSerializableMixin {
ocssharepermissions = ocssharepermissions ? [null] : null, ocssharepermissions = ocssharepermissions ? [null] : null,
ocmsharepermissions = ocmsharepermissions ? [null] : null; ocmsharepermissions = ocmsharepermissions ? [null] : null;
factory WebDavPropfindProp.fromXmlElement(final XmlElement element) => _$WebDavPropfindPropFromXmlElement(element); factory WebDavPropWithoutValues.fromXmlElement(final XmlElement element) =>
_$WebDavPropWithoutValuesFromXmlElement(element);
@annotation.XmlElement( @annotation.XmlElement(
name: 'getlastmodified', name: 'getlastmodified',

27
packages/nextcloud/lib/src/webdav/props.g.dart

@ -6,7 +6,7 @@ part of 'props.dart';
// XmlSerializableGenerator // XmlSerializableGenerator
// ************************************************************************** // **************************************************************************
void _$WebDavPropfindPropBuildXmlChildren(WebDavPropfindProp instance, XmlBuilder builder, void _$WebDavPropWithoutValuesBuildXmlChildren(WebDavPropWithoutValues instance, XmlBuilder builder,
{Map<String, String> namespaces = const {}}) { {Map<String, String> namespaces = const {}}) {
final davgetlastmodified = instance.davgetlastmodified; final davgetlastmodified = instance.davgetlastmodified;
final davgetlastmodifiedSerialized = davgetlastmodified; final davgetlastmodifiedSerialized = davgetlastmodified;
@ -308,14 +308,14 @@ void _$WebDavPropfindPropBuildXmlChildren(WebDavPropfindProp instance, XmlBuilde
} }
} }
void _$WebDavPropfindPropBuildXmlElement(WebDavPropfindProp instance, XmlBuilder builder, void _$WebDavPropWithoutValuesBuildXmlElement(WebDavPropWithoutValues instance, XmlBuilder builder,
{Map<String, String> namespaces = const {}}) { {Map<String, String> namespaces = const {}}) {
builder.element('prop', namespace: 'DAV:', namespaces: namespaces, nest: () { builder.element('prop', namespace: 'DAV:', namespaces: namespaces, nest: () {
instance.buildXmlChildren(builder, namespaces: namespaces); instance.buildXmlChildren(builder, namespaces: namespaces);
}); });
} }
WebDavPropfindProp _$WebDavPropfindPropFromXmlElement(XmlElement element) { WebDavPropWithoutValues _$WebDavPropWithoutValuesFromXmlElement(XmlElement element) {
final davgetlastmodified = final davgetlastmodified =
element.getElements('getlastmodified', namespace: 'DAV:')?.map((e) => e.getText()).whereType<String>(); element.getElements('getlastmodified', namespace: 'DAV:')?.map((e) => e.getText()).whereType<String>();
final davgetetag = element.getElements('getetag', namespace: 'DAV:')?.map((e) => e.getText()).whereType<String>(); final davgetetag = element.getElements('getetag', namespace: 'DAV:')?.map((e) => e.getText()).whereType<String>();
@ -401,7 +401,7 @@ WebDavPropfindProp _$WebDavPropfindPropFromXmlElement(XmlElement element) {
.getElements('share-permissions', namespace: 'http://open-cloud-mesh.org/ns') .getElements('share-permissions', namespace: 'http://open-cloud-mesh.org/ns')
?.map((e) => e.getText()) ?.map((e) => e.getText())
.whereType<String>(); .whereType<String>();
return WebDavPropfindProp( return WebDavPropWithoutValues(
davgetlastmodified: davgetlastmodified?.toList(), davgetlastmodified: davgetlastmodified?.toList(),
davgetetag: davgetetag?.toList(), davgetetag: davgetetag?.toList(),
davgetcontenttype: davgetcontenttype?.toList(), davgetcontenttype: davgetcontenttype?.toList(),
@ -431,13 +431,13 @@ WebDavPropfindProp _$WebDavPropfindPropFromXmlElement(XmlElement element) {
ocmsharepermissions: ocmsharepermissions?.toList()); ocmsharepermissions: ocmsharepermissions?.toList());
} }
List<XmlAttribute> _$WebDavPropfindPropToXmlAttributes(WebDavPropfindProp instance, List<XmlAttribute> _$WebDavPropWithoutValuesToXmlAttributes(WebDavPropWithoutValues instance,
{Map<String, String?> namespaces = const {}}) { {Map<String, String?> namespaces = const {}}) {
final attributes = <XmlAttribute>[]; final attributes = <XmlAttribute>[];
return attributes; return attributes;
} }
List<XmlNode> _$WebDavPropfindPropToXmlChildren(WebDavPropfindProp instance, List<XmlNode> _$WebDavPropWithoutValuesToXmlChildren(WebDavPropWithoutValues instance,
{Map<String, String?> namespaces = const {}}) { {Map<String, String?> namespaces = const {}}) {
final children = <XmlNode>[]; final children = <XmlNode>[];
final davgetlastmodified = instance.davgetlastmodified; final davgetlastmodified = instance.davgetlastmodified;
@ -638,28 +638,29 @@ List<XmlNode> _$WebDavPropfindPropToXmlChildren(WebDavPropfindProp instance,
return children; return children;
} }
XmlElement _$WebDavPropfindPropToXmlElement(WebDavPropfindProp instance, {Map<String, String?> namespaces = const {}}) { XmlElement _$WebDavPropWithoutValuesToXmlElement(WebDavPropWithoutValues instance,
{Map<String, String?> namespaces = const {}}) {
return XmlElement( return XmlElement(
XmlName('prop', namespaces['DAV:']), XmlName('prop', namespaces['DAV:']),
[...namespaces.toXmlAttributes(), ...instance.toXmlAttributes(namespaces: namespaces)], [...namespaces.toXmlAttributes(), ...instance.toXmlAttributes(namespaces: namespaces)],
instance.toXmlChildren(namespaces: namespaces)); instance.toXmlChildren(namespaces: namespaces));
} }
mixin _$WebDavPropfindPropXmlSerializableMixin { mixin _$WebDavPropWithoutValuesXmlSerializableMixin {
void buildXmlChildren(XmlBuilder builder, {Map<String, String> namespaces = const {}}) => void buildXmlChildren(XmlBuilder builder, {Map<String, String> namespaces = const {}}) =>
_$WebDavPropfindPropBuildXmlChildren(this as WebDavPropfindProp, builder, namespaces: namespaces); _$WebDavPropWithoutValuesBuildXmlChildren(this as WebDavPropWithoutValues, builder, namespaces: namespaces);
void buildXmlElement(XmlBuilder builder, {Map<String, String> namespaces = const {}}) => void buildXmlElement(XmlBuilder builder, {Map<String, String> namespaces = const {}}) =>
_$WebDavPropfindPropBuildXmlElement(this as WebDavPropfindProp, builder, namespaces: namespaces); _$WebDavPropWithoutValuesBuildXmlElement(this as WebDavPropWithoutValues, builder, namespaces: namespaces);
List<XmlAttribute> toXmlAttributes({Map<String, String?> namespaces = const {}}) => List<XmlAttribute> toXmlAttributes({Map<String, String?> namespaces = const {}}) =>
_$WebDavPropfindPropToXmlAttributes(this as WebDavPropfindProp, namespaces: namespaces); _$WebDavPropWithoutValuesToXmlAttributes(this as WebDavPropWithoutValues, namespaces: namespaces);
List<XmlNode> toXmlChildren({Map<String, String?> namespaces = const {}}) => List<XmlNode> toXmlChildren({Map<String, String?> namespaces = const {}}) =>
_$WebDavPropfindPropToXmlChildren(this as WebDavPropfindProp, namespaces: namespaces); _$WebDavPropWithoutValuesToXmlChildren(this as WebDavPropWithoutValues, namespaces: namespaces);
XmlElement toXmlElement({Map<String, String?> namespaces = const {}}) => XmlElement toXmlElement({Map<String, String?> namespaces = const {}}) =>
_$WebDavPropfindPropToXmlElement(this as WebDavPropfindProp, namespaces: namespaces); _$WebDavPropWithoutValuesToXmlElement(this as WebDavPropWithoutValues, namespaces: namespaces);
} }
void _$WebDavPropBuildXmlChildren(WebDavProp instance, XmlBuilder builder, void _$WebDavPropBuildXmlChildren(WebDavProp instance, XmlBuilder builder,

27
packages/nextcloud/lib/src/webdav/webdav.dart

@ -76,11 +76,15 @@ class WebDavPropstat with _$WebDavPropstatXmlSerializableMixin {
@annotation.XmlRootElement(name: 'propertyupdate', namespace: namespaceDav) @annotation.XmlRootElement(name: 'propertyupdate', namespace: namespaceDav)
class WebDavPropertyupdate with _$WebDavPropertyupdateXmlSerializableMixin { class WebDavPropertyupdate with _$WebDavPropertyupdateXmlSerializableMixin {
WebDavPropertyupdate({ WebDavPropertyupdate({
required this.set, this.set,
this.remove,
}); });
@annotation.XmlElement(name: 'set', namespace: namespaceDav) @annotation.XmlElement(name: 'set', namespace: namespaceDav, includeIfNull: false)
final WebDavSet set; final WebDavSet? set;
@annotation.XmlElement(name: 'remove', namespace: namespaceDav, includeIfNull: false)
final WebDavRemove? remove;
} }
@annotation.XmlSerializable(createMixin: true) @annotation.XmlSerializable(createMixin: true)
@ -96,6 +100,19 @@ class WebDavSet with _$WebDavSetXmlSerializableMixin {
final WebDavProp prop; // coverage:ignore-line final WebDavProp prop; // coverage:ignore-line
} }
@annotation.XmlSerializable(createMixin: true)
@annotation.XmlRootElement(name: 'remove', namespace: namespaceDav)
class WebDavRemove with _$WebDavRemoveXmlSerializableMixin {
WebDavRemove({
required this.prop,
});
factory WebDavRemove.fromXmlElement(final XmlElement element) => _$WebDavRemoveFromXmlElement(element);
@annotation.XmlElement(name: 'prop', namespace: namespaceDav)
final WebDavPropWithoutValues prop; // coverage:ignore-line
}
@annotation.XmlSerializable(createMixin: true) @annotation.XmlSerializable(createMixin: true)
@annotation.XmlRootElement(name: 'propfind', namespace: namespaceDav) @annotation.XmlRootElement(name: 'propfind', namespace: namespaceDav)
class WebDavPropfind with _$WebDavPropfindXmlSerializableMixin { class WebDavPropfind with _$WebDavPropfindXmlSerializableMixin {
@ -104,7 +121,7 @@ class WebDavPropfind with _$WebDavPropfindXmlSerializableMixin {
}); });
@annotation.XmlElement(name: 'prop', namespace: namespaceDav) @annotation.XmlElement(name: 'prop', namespace: namespaceDav)
final WebDavPropfindProp prop; final WebDavPropWithoutValues prop;
} }
@annotation.XmlSerializable(createMixin: true) @annotation.XmlSerializable(createMixin: true)
@ -119,7 +136,7 @@ class WebDavOcFilterFiles with _$WebDavOcFilterFilesXmlSerializableMixin {
final WebDavOcFilterRules filterRules; final WebDavOcFilterRules filterRules;
@annotation.XmlElement(name: 'prop', namespace: namespaceDav) @annotation.XmlElement(name: 'prop', namespace: namespaceDav)
final WebDavPropfindProp prop; final WebDavPropWithoutValues prop;
} }
@annotation.XmlSerializable(createMixin: true) @annotation.XmlSerializable(createMixin: true)

97
packages/nextcloud/lib/src/webdav/webdav.g.dart

@ -221,9 +221,18 @@ void _$WebDavPropertyupdateBuildXmlChildren(WebDavPropertyupdate instance, XmlBu
{Map<String, String> namespaces = const {}}) { {Map<String, String> namespaces = const {}}) {
final set = instance.set; final set = instance.set;
final setSerialized = set; final setSerialized = set;
if (setSerialized != null) {
builder.element('set', namespace: 'DAV:', nest: () { builder.element('set', namespace: 'DAV:', nest: () {
setSerialized.buildXmlChildren(builder, namespaces: namespaces); setSerialized.buildXmlChildren(builder, namespaces: namespaces);
}); });
}
final remove = instance.remove;
final removeSerialized = remove;
if (removeSerialized != null) {
builder.element('remove', namespace: 'DAV:', nest: () {
removeSerialized.buildXmlChildren(builder, namespaces: namespaces);
});
}
} }
void _$WebDavPropertyupdateBuildXmlElement(WebDavPropertyupdate instance, XmlBuilder builder, void _$WebDavPropertyupdateBuildXmlElement(WebDavPropertyupdate instance, XmlBuilder builder,
@ -234,8 +243,11 @@ void _$WebDavPropertyupdateBuildXmlElement(WebDavPropertyupdate instance, XmlBui
} }
WebDavPropertyupdate _$WebDavPropertyupdateFromXmlElement(XmlElement element) { WebDavPropertyupdate _$WebDavPropertyupdateFromXmlElement(XmlElement element) {
final set = element.getElement('set', namespace: 'DAV:')!; final set = element.getElement('set', namespace: 'DAV:');
return WebDavPropertyupdate(set: WebDavSet.fromXmlElement(set)); final remove = element.getElement('remove', namespace: 'DAV:');
return WebDavPropertyupdate(
set: set != null ? WebDavSet.fromXmlElement(set) : null,
remove: remove != null ? WebDavRemove.fromXmlElement(remove) : null);
} }
List<XmlAttribute> _$WebDavPropertyupdateToXmlAttributes(WebDavPropertyupdate instance, List<XmlAttribute> _$WebDavPropertyupdateToXmlAttributes(WebDavPropertyupdate instance,
@ -249,9 +261,22 @@ List<XmlNode> _$WebDavPropertyupdateToXmlChildren(WebDavPropertyupdate instance,
final children = <XmlNode>[]; final children = <XmlNode>[];
final set = instance.set; final set = instance.set;
final setSerialized = set; final setSerialized = set;
final setConstructed = XmlElement(XmlName('set', namespaces['DAV:']), final setConstructed = setSerialized != null
setSerialized.toXmlAttributes(namespaces: namespaces), setSerialized.toXmlChildren(namespaces: namespaces)); ? XmlElement(XmlName('set', namespaces['DAV:']), setSerialized.toXmlAttributes(namespaces: namespaces),
setSerialized.toXmlChildren(namespaces: namespaces))
: null;
if (setConstructed != null) {
children.add(setConstructed); children.add(setConstructed);
}
final remove = instance.remove;
final removeSerialized = remove;
final removeConstructed = removeSerialized != null
? XmlElement(XmlName('remove', namespaces['DAV:']), removeSerialized.toXmlAttributes(namespaces: namespaces),
removeSerialized.toXmlChildren(namespaces: namespaces))
: null;
if (removeConstructed != null) {
children.add(removeConstructed);
}
return children; return children;
} }
@ -338,6 +363,66 @@ mixin _$WebDavSetXmlSerializableMixin {
_$WebDavSetToXmlElement(this as WebDavSet, namespaces: namespaces); _$WebDavSetToXmlElement(this as WebDavSet, namespaces: namespaces);
} }
void _$WebDavRemoveBuildXmlChildren(WebDavRemove instance, XmlBuilder builder,
{Map<String, String> namespaces = const {}}) {
final prop = instance.prop;
final propSerialized = prop;
builder.element('prop', namespace: 'DAV:', nest: () {
propSerialized.buildXmlChildren(builder, namespaces: namespaces);
});
}
void _$WebDavRemoveBuildXmlElement(WebDavRemove instance, XmlBuilder builder,
{Map<String, String> namespaces = const {}}) {
builder.element('remove', namespace: 'DAV:', namespaces: namespaces, nest: () {
instance.buildXmlChildren(builder, namespaces: namespaces);
});
}
WebDavRemove _$WebDavRemoveFromXmlElement(XmlElement element) {
final prop = element.getElement('prop', namespace: 'DAV:')!;
return WebDavRemove(prop: WebDavPropWithoutValues.fromXmlElement(prop));
}
List<XmlAttribute> _$WebDavRemoveToXmlAttributes(WebDavRemove instance, {Map<String, String?> namespaces = const {}}) {
final attributes = <XmlAttribute>[];
return attributes;
}
List<XmlNode> _$WebDavRemoveToXmlChildren(WebDavRemove instance, {Map<String, String?> namespaces = const {}}) {
final children = <XmlNode>[];
final prop = instance.prop;
final propSerialized = prop;
final propConstructed = XmlElement(XmlName('prop', namespaces['DAV:']),
propSerialized.toXmlAttributes(namespaces: namespaces), propSerialized.toXmlChildren(namespaces: namespaces));
children.add(propConstructed);
return children;
}
XmlElement _$WebDavRemoveToXmlElement(WebDavRemove instance, {Map<String, String?> namespaces = const {}}) {
return XmlElement(
XmlName('remove', namespaces['DAV:']),
[...namespaces.toXmlAttributes(), ...instance.toXmlAttributes(namespaces: namespaces)],
instance.toXmlChildren(namespaces: namespaces));
}
mixin _$WebDavRemoveXmlSerializableMixin {
void buildXmlChildren(XmlBuilder builder, {Map<String, String> namespaces = const {}}) =>
_$WebDavRemoveBuildXmlChildren(this as WebDavRemove, builder, namespaces: namespaces);
void buildXmlElement(XmlBuilder builder, {Map<String, String> namespaces = const {}}) =>
_$WebDavRemoveBuildXmlElement(this as WebDavRemove, builder, namespaces: namespaces);
List<XmlAttribute> toXmlAttributes({Map<String, String?> namespaces = const {}}) =>
_$WebDavRemoveToXmlAttributes(this as WebDavRemove, namespaces: namespaces);
List<XmlNode> toXmlChildren({Map<String, String?> namespaces = const {}}) =>
_$WebDavRemoveToXmlChildren(this as WebDavRemove, namespaces: namespaces);
XmlElement toXmlElement({Map<String, String?> namespaces = const {}}) =>
_$WebDavRemoveToXmlElement(this as WebDavRemove, namespaces: namespaces);
}
void _$WebDavPropfindBuildXmlChildren(WebDavPropfind instance, XmlBuilder builder, void _$WebDavPropfindBuildXmlChildren(WebDavPropfind instance, XmlBuilder builder,
{Map<String, String> namespaces = const {}}) { {Map<String, String> namespaces = const {}}) {
final prop = instance.prop; final prop = instance.prop;
@ -356,7 +441,7 @@ void _$WebDavPropfindBuildXmlElement(WebDavPropfind instance, XmlBuilder builder
WebDavPropfind _$WebDavPropfindFromXmlElement(XmlElement element) { WebDavPropfind _$WebDavPropfindFromXmlElement(XmlElement element) {
final prop = element.getElement('prop', namespace: 'DAV:')!; final prop = element.getElement('prop', namespace: 'DAV:')!;
return WebDavPropfind(prop: WebDavPropfindProp.fromXmlElement(prop)); return WebDavPropfind(prop: WebDavPropWithoutValues.fromXmlElement(prop));
} }
List<XmlAttribute> _$WebDavPropfindToXmlAttributes(WebDavPropfind instance, List<XmlAttribute> _$WebDavPropfindToXmlAttributes(WebDavPropfind instance,
@ -424,7 +509,7 @@ WebDavOcFilterFiles _$WebDavOcFilterFilesFromXmlElement(XmlElement element) {
final filterRules = element.getElement('filter-rules', namespace: 'http://owncloud.org/ns')!; final filterRules = element.getElement('filter-rules', namespace: 'http://owncloud.org/ns')!;
final prop = element.getElement('prop', namespace: 'DAV:')!; final prop = element.getElement('prop', namespace: 'DAV:')!;
return WebDavOcFilterFiles( return WebDavOcFilterFiles(
filterRules: WebDavOcFilterRules.fromXmlElement(filterRules), prop: WebDavPropfindProp.fromXmlElement(prop)); filterRules: WebDavOcFilterRules.fromXmlElement(filterRules), prop: WebDavPropWithoutValues.fromXmlElement(prop));
} }
List<XmlAttribute> _$WebDavOcFilterFilesToXmlAttributes(WebDavOcFilterFiles instance, List<XmlAttribute> _$WebDavOcFilterFilesToXmlAttributes(WebDavOcFilterFiles instance,

141
packages/nextcloud/test/webdav_test.dart

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

Loading…
Cancel
Save