Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(nextcloud)!: Migrate to DAV v2 endpoint for WebDAV #2557

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -81,19 +81,19 @@ class _FilesBrowserBloc extends InteractiveBloc implements FilesBrowserBloc {
await RequestManager.instance.wrap(
account: account,
subject: files,
getRequest: () => account.client.webdav.propfind_Request(
uri,
prop: const webdav.WebDavPropWithoutValues.fromBools(
davGetcontenttype: true,
davGetetag: true,
davGetlastmodified: true,
ncHasPreview: true,
ncMetadataBlurhash: true,
ocSize: true,
ocFavorite: true,
),
depth: webdav.WebDavDepth.one,
),
getRequest: () => account.client.webdav(account.username).propfind_Request(
uri,
prop: const webdav.WebDavPropWithoutValues.fromBools(
davGetcontenttype: true,
davGetetag: true,
davGetlastmodified: true,
ncHasPreview: true,
ncMetadataBlurhash: true,
ocSize: true,
ocFavorite: true,
),
depth: webdav.WebDavDepth.one,
),
converter: const webdav.WebDavResponseConverter(),
unwrap: (response) => BuiltList<webdav.WebDavFile>.build((b) {
for (final file in response.toWebDavFiles()) {
Expand Down
40 changes: 20 additions & 20 deletions packages/neon_framework/packages/files_app/lib/src/blocs/files.dart
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ class _FilesBloc extends InteractiveBloc implements FilesBloc {
file: File(localPath),
);
tasks.add(tasks.value.rebuild((b) => b.add(task)));
await uploadQueue.add(() => task.execute(account.client));
await uploadQueue.add(() => task.execute(account.client.webdav(account.username)));
tasks.add(tasks.value.rebuild((b) => b.remove(task)));
},
disableTimeout: true,
Expand All @@ -123,7 +123,7 @@ class _FilesBloc extends InteractiveBloc implements FilesBloc {
bytes: bytes,
);
tasks.add(tasks.value.rebuild((b) => b.add(task)));
await uploadQueue.add(() => task.execute(account.client));
await uploadQueue.add(() => task.execute(account.client.webdav(account.username)));
tasks.add(tasks.value.rebuild((b) => b.remove(task)));
},
disableTimeout: true,
Expand Down Expand Up @@ -166,52 +166,52 @@ class _FilesBloc extends InteractiveBloc implements FilesBloc {

@override
Future<void> delete(webdav.PathUri uri) async {
await wrapAction(() async => account.client.webdav.delete(uri));
await wrapAction(() async => account.client.webdav(account.username).delete(uri));
}

@override
Future<void> rename(webdav.PathUri uri, String name) async {
await wrapAction(
() async => account.client.webdav.move(
uri,
uri.rename(name),
),
() async => account.client.webdav(account.username).move(
uri,
uri.rename(name),
),
);
}

@override
Future<void> move(webdav.PathUri uri, webdav.PathUri destination) async {
await wrapAction(() async => account.client.webdav.move(uri, destination));
await wrapAction(() async => account.client.webdav(account.username).move(uri, destination));
}

@override
Future<void> copy(webdav.PathUri uri, webdav.PathUri destination) async {
await wrapAction(() async => account.client.webdav.copy(uri, destination));
await wrapAction(() async => account.client.webdav(account.username).copy(uri, destination));
}

@override
Future<void> addFavorite(webdav.PathUri uri) async {
await wrapAction(
() async => account.client.webdav.proppatch(
uri,
set: const webdav.WebDavProp(ocFavorite: true),
),
() async => account.client.webdav(account.username).proppatch(
uri,
set: const webdav.WebDavProp(ocFavorite: true),
),
);
}

@override
Future<void> removeFavorite(webdav.PathUri uri) async {
await wrapAction(
() async => account.client.webdav.proppatch(
uri,
set: const webdav.WebDavProp(ocFavorite: false),
),
() async => account.client.webdav(account.username).proppatch(
uri,
set: const webdav.WebDavProp(ocFavorite: false),
),
);
}

@override
Future<void> createFolder(webdav.PathUri uri) async {
await wrapAction(() async => account.client.webdav.mkcol(uri));
await wrapAction(() async => account.client.webdav(account.username).mkcol(uri));
}

Future<File> cacheFile(webdav.PathUri uri, String etag) async {
Expand All @@ -236,7 +236,7 @@ class _FilesBloc extends InteractiveBloc implements FilesBloc {
);

tasks.add(tasks.value.rebuild((b) => b.add(task)));
await downloadQueue.add(() => task.execute(account.client));
await downloadQueue.add(() => task.execute(account.client.webdav(account.username)));
tasks.add(tasks.value.rebuild((b) => b.remove(task)));
}

Expand All @@ -248,7 +248,7 @@ class _FilesBloc extends InteractiveBloc implements FilesBloc {
final future = task.stream.forEach(buffer.add);

tasks.add(tasks.value.rebuild((b) => b.add(task)));
await downloadQueue.add(() => task.execute(account.client));
await downloadQueue.add(() => task.execute(account.client.webdav(account.username)));
tasks.add(tasks.value.rebuild((b) => b.remove(task)));

await future;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import 'dart:async';
import 'dart:typed_data';

import 'package:meta/meta.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:nextcloud/webdav.dart' as webdav;
import 'package:timezone/timezone.dart' as tz;
import 'package:universal_io/io.dart';
Expand Down Expand Up @@ -66,8 +65,8 @@ class FilesDownloadTaskIO extends FilesTaskIO implements FilesDownloadTask {
required super.file,
});

Future<void> execute(NextcloudClient client) async {
await client.webdav.getFile(
Future<void> execute(webdav.WebDavClient client) async {
await client.getFile(
uri,
file,
onProgress: progressController.add,
Expand All @@ -90,8 +89,8 @@ class FilesUploadTaskIO extends FilesTaskIO implements FilesUploadTask {
@override
late tz.TZDateTime lastModified = tz.TZDateTime.from(_stat.modified, tz.UTC);

Future<void> execute(NextcloudClient client) async {
await client.webdav.putFile(
Future<void> execute(webdav.WebDavClient client) async {
await client.putFile(
file,
_stat,
uri,
Expand All @@ -107,8 +106,8 @@ class FilesDownloadTaskMemory extends FilesTaskMemory implements FilesDownloadTa
required super.uri,
});

Future<void> execute(NextcloudClient client) async {
final stream = await client.webdav.getStream(
Future<void> execute(webdav.WebDavClient client) async {
final stream = await client.getStream(
uri,
onProgress: progressController.add,
);
Expand All @@ -133,8 +132,8 @@ class FilesUploadTaskMemory extends FilesTaskMemory implements FilesUploadTask {
@override
final tz.TZDateTime? lastModified;

Future<void> execute(NextcloudClient client) async {
await client.webdav.putStream(
Future<void> execute(webdav.WebDavClient client) async {
await client.putStream(
_stream.stream,
uri,
lastModified: lastModified,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import 'package:interceptor_http_client/interceptor_http_client.dart';
import 'package:logging/logging.dart';
import 'package:meta/meta.dart';
import 'package:nextcloud/core.dart' as core;
import 'package:nextcloud/webdav.dart' as webdav;

/// A HttpInterceptor that works around a Nextcloud CSRF bug when cookies are sent.
///
Expand Down Expand Up @@ -43,7 +42,7 @@ final class CSRFInterceptor implements HttpInterceptor {

@override
bool shouldInterceptRequest(http.BaseRequest request) {
if (request.url.host != _baseURL.host || !request.url.path.startsWith('${_baseURL.path}${webdav.webdavBase}')) {
if (request.url.host != _baseURL.host || !request.url.path.startsWith('${_baseURL.path}/remote.php/')) {
return false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ class WebDavFile {
isAbsolute: false,
isDirectory: href.isDirectory,
// The server might be hosted at a subpath, so we don't have a fixed number of path segments to remove
pathSegments: href.pathSegments.sublist(href.pathSegments.indexOf('remote.php') + 2),
pathSegments: href.pathSegments.sublist(href.pathSegments.indexOf('remote.php') + 4),
);
}();

Expand Down
7 changes: 2 additions & 5 deletions packages/nextcloud/lib/src/api/webdav/utils/webdav_uri.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
import 'package:meta/meta.dart';
import 'package:nextcloud/src/api/webdav/webdav.dart';

/// Base path used on the server
final webdavBase = PathUri.parse('/remote.php/webdav');

/// Constructs the uri for a webdav request for a given server [baseURL] and file [path].
@internal
Uri constructUri(Uri baseURL, [PathUri? path]) {
final segments = baseURL.pathSegments.toList()..addAll(webdavBase.pathSegments);
Uri constructUri(String username, Uri baseURL, [PathUri? path]) {
final segments = baseURL.pathSegments.toList()..addAll(['remote.php', 'dav', 'files', username]);
if (path != null) {
segments.addAll(path.pathSegments);
}
Expand Down
15 changes: 12 additions & 3 deletions packages/nextcloud/lib/src/api/webdav/webdav_client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ class WebDavClient extends DynamiteClient {
/// triggered, when cookies are also sent.
WebDavClient(
super.baseURL, {
required this.username,
http.Client? httpClient,
super.authentications,
bool useCSRFClient = true,
Expand All @@ -44,14 +45,22 @@ class WebDavClient extends DynamiteClient {
);

/// Creates a new [WebDavClient] from another [client].
WebDavClient.fromClient(DynamiteClient client)
: this(
WebDavClient.fromClient(
DynamiteClient client, {
required String username,
}) : this(
client.baseURL,
username: username,
httpClient: client.httpClient,
authentications: client.authentications,
);

Uri _constructUri([PathUri? path]) => constructUri(baseURL, path);
/// The username of the user used for all requests.
final String username;

Uri _constructUri([PathUri? path]) {
return constructUri(username, baseURL, path);
}

/// Returns a request to query the WebDAV capabilities of the server.
///
Expand Down
17 changes: 16 additions & 1 deletion packages/nextcloud/lib/webdav.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,23 @@ export 'package:nextcloud/src/api/webdav/webdav.dart'

/// Client for WebDAV.
extension WebDAVExtension on NextcloudClient {
static final _username = Expando<String>();
static final _webdav = Expando<WebDavClient>();

/// Client for WebDAV.
WebDavClient get webdav => _webdav[this] ??= WebDavClient.fromClient(this);
///
/// To acquire the [username] of the user you can use the provisioning_api.
WebDavClient webdav(String username) {
if (_username[this] != null && username != _username[this]) {
throw ArgumentError(
'You can not provide a different username than on the first invocation. Got "$username" but expected "${_username[this]}".',
);
}

_username[this] ??= username;
return _webdav[this] ??= WebDavClient.fromClient(
this,
username: username,
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ final class NextcloudTester {
NextcloudTester({
required String appName,
required Version version,
String username = defaultTestUsername,
}) : _preset = (name: appName, version: version),
_username = username;
this.username = defaultTestUsername,
}) : _preset = (name: appName, version: version);

final Preset _preset;

final String _username;
/// The username of the configured user.
final String username;

/// The app version tested.
Version get version => _preset.version;
Expand Down Expand Up @@ -63,7 +63,7 @@ final class NextcloudTester {
/// Initializes the tester creating the target and default client.
Future<void> init() async {
_target = await TestTargetFactory.instance.spawn(_preset);
_client = await _target!.createClient(username: _username);
_client = await _target!.createClient(username: username);
}

/// Closes the tester.
Expand Down
4 changes: 2 additions & 2 deletions packages/nextcloud/test/api/core/core_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -126,13 +126,13 @@ void main() {
group('Preview', () {
setUp(() async {
final file = File('test/files/test.png');
await tester.client.webdav.putFile(file, file.statSync(), webdav.PathUri.parse('preview.png'));
await tester.client.webdav(tester.username).putFile(file, file.statSync(), webdav.PathUri.parse('preview.png'));
resetFixture();
});

tearDown(() async {
closeFixture();
await tester.client.webdav.delete(webdav.PathUri.parse('preview.png'));
await tester.client.webdav(tester.username).delete(webdav.PathUri.parse('preview.png'));
});

test('Get', () async {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,15 @@ void main() {
group('shareapi', () {
setUp(() async {
final file = File('test/files/test.png');
await tester.client.webdav.putFile(file, file.statSync(), webdav.PathUri.parse('create-share.png'));
await tester.client
.webdav(tester.username)
.putFile(file, file.statSync(), webdav.PathUri.parse('create-share.png'));
resetFixture();
});

tearDown(() async {
closeFixture();
await tester.client.webdav.delete(webdav.PathUri.parse('create-share.png'));
await tester.client.webdav(tester.username).delete(webdav.PathUri.parse('create-share.png'));
});

test('createShare', () async {
Expand Down
Loading