Skip to content

Commit

Permalink
feat(nextcloud)!: deserialize webdav date fields
Browse files Browse the repository at this point in the history
Signed-off-by: Nikolas Rimikis <leptopoda@users.noreply.github.com>
  • Loading branch information
Leptopoda committed Apr 3, 2024
1 parent f79792d commit e128a2a
Show file tree
Hide file tree
Showing 7 changed files with 344 additions and 125 deletions.
36 changes: 27 additions & 9 deletions packages/nextcloud/generate_props.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ void main() {
final props = <String, Map<String, String>>{
'dav': {
// lockdiscovery and supportedlock are omitted because the server only has a dummy implementation anyway.
'creationdate': 'DateTime',
'creationdate': 'iso8601',
'displayname': 'String',
'getcontentlanguage': 'String',
'getcontentlength': 'int',
'getcontenttype': 'String',
'getetag': 'String',
'getlastmodified': 'String',
'getlastmodified': 'httpDate',
'quota-available-bytes': 'int',
'quota-used-bytes': 'int',
'resourcetype': 'WebDavResourcetype',
Expand All @@ -23,7 +23,7 @@ void main() {
'acl-list': 'WebDavNcAclList',
'contained-file-count': 'int',
'contained-folder-count': 'int',
'creation_time': 'int',
'creation_time': 'unixEpoch',
'data-fingerprint': 'String',
'group-folder-id': 'int',
'has-preview': 'bool',
Expand All @@ -36,18 +36,18 @@ void main() {
'lock-owner-displayname': 'String',
'lock-owner-editor': 'String',
'lock-owner-type': 'int',
'lock-time': 'int',
'lock-time': 'unixEpoch',
'lock-timeout': 'int',
'lock-token': 'String',
'metadata_etag': 'String',
'mount-type': 'String',
'note': 'String',
'reminder-due-date': 'DateTime',
'reminder-due-date': 'iso8601',
'rich-workspace': 'String',
'rich-workspace-file': 'int',
'share-attributes': 'String',
'sharees': 'WebDavNcShareeList',
'upload_time': 'int',
'upload_time': 'unixEpoch',
'version-author': 'String',
'version-label': 'String',
},
Expand Down Expand Up @@ -80,17 +80,33 @@ void main() {
final variables = <String>[];
for (final namespacePrefix in props.keys) {
for (final name in props[namespacePrefix]!.keys) {
final type = props[namespacePrefix]![name]!;
var type = props[namespacePrefix]![name]!;

final namespaceVariable = convertNamespace(namespacePrefix);
final variable = toDartName('$namespacePrefix-$name');
valueProps.add('''

final value = StringBuffer('''
@annotation.XmlElement(
name: '$name',
namespace: $namespaceVariable,
includeIfNull: false,
)
final $type? $variable;''');
''');
switch (type) {
case 'httpDate':
value.writeln(' @HttpDateXMLConverter()');
type = 'tz.TZDateTime';
case 'iso8601':
value.writeln(' @ISO8601XMLConverter()');
type = 'tz.TZDateTime';
case 'unixEpoch':
value.writeln(' @UnixEpochXMLConverter()');
type = 'tz.TZDateTime';
}

value.write(' final $type? $variable;');
valueProps.add(value.toString());

findProps.add('''
@annotation.XmlElement(
name: '$name',
Expand All @@ -107,7 +123,9 @@ void main() {
'// ignore_for_file: public_member_api_docs',
'// coverage:ignore-file',
"import 'package:meta/meta.dart';",
"import 'package:nextcloud/src/utils/date_time.dart';",
"import 'package:nextcloud/src/webdav/webdav.dart';",
"import 'package:timezone/timezone.dart' as tz;",
"import 'package:xml/xml.dart';",
"import 'package:xml_annotation/xml_annotation.dart' as annotation;",
"part 'props.g.dart';",
Expand Down
170 changes: 170 additions & 0 deletions packages/nextcloud/lib/src/utils/date_time.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import 'package:meta/meta.dart';
import 'package:nextcloud/src/utils/http_date_parser.dart';
import 'package:nextcloud/utils.dart';
import 'package:timezone/timezone.dart' as tz;
import 'package:xml/xml.dart' as xml;
import 'package:xml_annotation/xml_annotation.dart' as xml_annotation;

/// Common methods to work with [DateTime] data.
extension DateTimeUtils on DateTime {
Expand Down Expand Up @@ -28,3 +33,168 @@ extension DateTimeUtils on DateTime {
/// In other words: `secondsSinceEpoch.abs() <= 8640000000000`.
int get secondsSinceEpoch => millisecondsSinceEpoch ~/ Duration.millisecondsPerSecond;
}

@internal
final class HttpDateXMLConverter implements xml_annotation.XmlConverter<tz.TZDateTime?> {
const HttpDateXMLConverter();

@override
void buildXmlChildren(
tz.TZDateTime? instance,
xml.XmlBuilder builder, {
Map<String, String> namespaces = const {},
}) {
if (instance == null) {
return;
}

final date = formatHttpDate(instance);
builder.text(date);
}

@override
tz.TZDateTime? fromXmlElement(
xml.XmlElement element,
) {
final date = element.getText();
if (date != null) {
return parseHttpDate(date);
}

return null;
}

@override
List<xml.XmlAttribute> toXmlAttributes(
tz.TZDateTime? instance, {
Map<String, String?> namespaces = const {},
}) {
return const <xml.XmlAttribute>[];
}

@override
List<xml.XmlNode> toXmlChildren(
tz.TZDateTime? instance, {
Map<String, String?> namespaces = const {},
}) {
if (instance == null) {
return const <xml.XmlNode>[];
}

final date = formatHttpDate(instance);
return <xml.XmlNode>[
xml.XmlText(date),
];
}
}

@internal
final class ISO8601XMLConverter implements xml_annotation.XmlConverter<tz.TZDateTime?> {
const ISO8601XMLConverter();

@override
void buildXmlChildren(
tz.TZDateTime? instance,
xml.XmlBuilder builder, {
Map<String, String> namespaces = const {},
}) {
if (instance == null) {
return;
}

final date = instance.toIso8601String();
builder.text(date);
}

@override
tz.TZDateTime? fromXmlElement(
xml.XmlElement element,
) {
final date = element.getText();
if (date != null) {
return tz.TZDateTime.parse(tz.UTC, date);
}

return null;
}

@override
List<xml.XmlAttribute> toXmlAttributes(
tz.TZDateTime? instance, {
Map<String, String?> namespaces = const {},
}) {
return const <xml.XmlAttribute>[];
}

@override
List<xml.XmlNode> toXmlChildren(
tz.TZDateTime? instance, {
Map<String, String?> namespaces = const {},
}) {
if (instance == null) {
return const <xml.XmlNode>[];
}

final date = instance.toIso8601String();
return <xml.XmlNode>[
xml.XmlText(date),
];
}
}

@internal
final class UnixEpochXMLConverter implements xml_annotation.XmlConverter<tz.TZDateTime?> {
const UnixEpochXMLConverter();

@override
void buildXmlChildren(
tz.TZDateTime? instance,
xml.XmlBuilder builder, {
Map<String, String> namespaces = const {},
}) {
if (instance == null) {
return;
}

final date = instance.secondsSinceEpoch.toString();
builder.text(date);
}

@override
tz.TZDateTime? fromXmlElement(
xml.XmlElement element,
) {
final date = element.getText();
if (date != null) {
return DateTimeUtils.fromSecondsSinceEpoch(
tz.UTC,
int.parse(date),
);
}

return null;
}

@override
List<xml.XmlAttribute> toXmlAttributes(
tz.TZDateTime? instance, {
Map<String, String?> namespaces = const {},
}) {
return const <xml.XmlAttribute>[];
}

@override
List<xml.XmlNode> toXmlChildren(
tz.TZDateTime? instance, {
Map<String, String?> namespaces = const {},
}) {
if (instance == null) {
return const <xml.XmlNode>[];
}

final date = instance.secondsSinceEpoch.toString();
return <xml.XmlNode>[
xml.XmlText(date),
];
}
}
23 changes: 3 additions & 20 deletions packages/nextcloud/lib/src/webdav/file.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
import 'package:nextcloud/src/utils/date_time.dart';
import 'package:nextcloud/src/utils/http_date_parser.dart';
import 'package:nextcloud/src/webdav/path_uri.dart';
import 'package:nextcloud/src/webdav/props.dart';
import 'package:nextcloud/src/webdav/utils.dart';
Expand Down Expand Up @@ -65,28 +63,13 @@ class WebDavFile {
/// Last modified date of the file.
///
/// It will throw a [FormatException] if the date is invalid.
late final tz.TZDateTime? lastModified = () {
if (props.davGetlastmodified != null) {
return parseHttpDate(props.davGetlastmodified!);
}
return null;
}();
late final tz.TZDateTime? lastModified = props.davGetlastmodified;

/// Upload date of the file
late final tz.TZDateTime? uploadedDate = props.ncUploadTime != null
? DateTimeUtils.fromSecondsSinceEpoch(
tz.UTC,
props.ncUploadTime!,
)
: null;
late final tz.TZDateTime? uploadedDate = props.ncUploadTime;

/// Creation date of the file as provided by uploader
late final tz.TZDateTime? createdDate = props.ncCreationTime != null
? DateTimeUtils.fromSecondsSinceEpoch(
tz.UTC,
props.ncCreationTime!,
)
: null;
late final tz.TZDateTime? createdDate = props.ncCreationTime;

/// Whether this file is marked as favorite
late final bool? favorite = props.ocFavorite == null ? null : props.ocFavorite == 1;
Expand Down

0 comments on commit e128a2a

Please sign in to comment.