You've already forked immich
mirror of
https://github.com/immich-app/immich.git
synced 2025-07-03 05:46:58 +02:00
feat: sync memories (#19579)
This commit is contained in:
4
mobile/openapi/README.md
generated
4
mobile/openapi/README.md
generated
@ -475,6 +475,10 @@ Class | Method | HTTP request | Description
|
|||||||
- [SyncAssetExifV1](doc//SyncAssetExifV1.md)
|
- [SyncAssetExifV1](doc//SyncAssetExifV1.md)
|
||||||
- [SyncAssetV1](doc//SyncAssetV1.md)
|
- [SyncAssetV1](doc//SyncAssetV1.md)
|
||||||
- [SyncEntityType](doc//SyncEntityType.md)
|
- [SyncEntityType](doc//SyncEntityType.md)
|
||||||
|
- [SyncMemoryAssetDeleteV1](doc//SyncMemoryAssetDeleteV1.md)
|
||||||
|
- [SyncMemoryAssetV1](doc//SyncMemoryAssetV1.md)
|
||||||
|
- [SyncMemoryDeleteV1](doc//SyncMemoryDeleteV1.md)
|
||||||
|
- [SyncMemoryV1](doc//SyncMemoryV1.md)
|
||||||
- [SyncPartnerDeleteV1](doc//SyncPartnerDeleteV1.md)
|
- [SyncPartnerDeleteV1](doc//SyncPartnerDeleteV1.md)
|
||||||
- [SyncPartnerV1](doc//SyncPartnerV1.md)
|
- [SyncPartnerV1](doc//SyncPartnerV1.md)
|
||||||
- [SyncRequestType](doc//SyncRequestType.md)
|
- [SyncRequestType](doc//SyncRequestType.md)
|
||||||
|
4
mobile/openapi/lib/api.dart
generated
4
mobile/openapi/lib/api.dart
generated
@ -258,6 +258,10 @@ part 'model/sync_asset_delete_v1.dart';
|
|||||||
part 'model/sync_asset_exif_v1.dart';
|
part 'model/sync_asset_exif_v1.dart';
|
||||||
part 'model/sync_asset_v1.dart';
|
part 'model/sync_asset_v1.dart';
|
||||||
part 'model/sync_entity_type.dart';
|
part 'model/sync_entity_type.dart';
|
||||||
|
part 'model/sync_memory_asset_delete_v1.dart';
|
||||||
|
part 'model/sync_memory_asset_v1.dart';
|
||||||
|
part 'model/sync_memory_delete_v1.dart';
|
||||||
|
part 'model/sync_memory_v1.dart';
|
||||||
part 'model/sync_partner_delete_v1.dart';
|
part 'model/sync_partner_delete_v1.dart';
|
||||||
part 'model/sync_partner_v1.dart';
|
part 'model/sync_partner_v1.dart';
|
||||||
part 'model/sync_request_type.dart';
|
part 'model/sync_request_type.dart';
|
||||||
|
8
mobile/openapi/lib/api_client.dart
generated
8
mobile/openapi/lib/api_client.dart
generated
@ -572,6 +572,14 @@ class ApiClient {
|
|||||||
return SyncAssetV1.fromJson(value);
|
return SyncAssetV1.fromJson(value);
|
||||||
case 'SyncEntityType':
|
case 'SyncEntityType':
|
||||||
return SyncEntityTypeTypeTransformer().decode(value);
|
return SyncEntityTypeTypeTransformer().decode(value);
|
||||||
|
case 'SyncMemoryAssetDeleteV1':
|
||||||
|
return SyncMemoryAssetDeleteV1.fromJson(value);
|
||||||
|
case 'SyncMemoryAssetV1':
|
||||||
|
return SyncMemoryAssetV1.fromJson(value);
|
||||||
|
case 'SyncMemoryDeleteV1':
|
||||||
|
return SyncMemoryDeleteV1.fromJson(value);
|
||||||
|
case 'SyncMemoryV1':
|
||||||
|
return SyncMemoryV1.fromJson(value);
|
||||||
case 'SyncPartnerDeleteV1':
|
case 'SyncPartnerDeleteV1':
|
||||||
return SyncPartnerDeleteV1.fromJson(value);
|
return SyncPartnerDeleteV1.fromJson(value);
|
||||||
case 'SyncPartnerV1':
|
case 'SyncPartnerV1':
|
||||||
|
24
mobile/openapi/lib/model/sync_entity_type.dart
generated
24
mobile/openapi/lib/model/sync_entity_type.dart
generated
@ -25,11 +25,11 @@ class SyncEntityType {
|
|||||||
|
|
||||||
static const userV1 = SyncEntityType._(r'UserV1');
|
static const userV1 = SyncEntityType._(r'UserV1');
|
||||||
static const userDeleteV1 = SyncEntityType._(r'UserDeleteV1');
|
static const userDeleteV1 = SyncEntityType._(r'UserDeleteV1');
|
||||||
static const partnerV1 = SyncEntityType._(r'PartnerV1');
|
|
||||||
static const partnerDeleteV1 = SyncEntityType._(r'PartnerDeleteV1');
|
|
||||||
static const assetV1 = SyncEntityType._(r'AssetV1');
|
static const assetV1 = SyncEntityType._(r'AssetV1');
|
||||||
static const assetDeleteV1 = SyncEntityType._(r'AssetDeleteV1');
|
static const assetDeleteV1 = SyncEntityType._(r'AssetDeleteV1');
|
||||||
static const assetExifV1 = SyncEntityType._(r'AssetExifV1');
|
static const assetExifV1 = SyncEntityType._(r'AssetExifV1');
|
||||||
|
static const partnerV1 = SyncEntityType._(r'PartnerV1');
|
||||||
|
static const partnerDeleteV1 = SyncEntityType._(r'PartnerDeleteV1');
|
||||||
static const partnerAssetV1 = SyncEntityType._(r'PartnerAssetV1');
|
static const partnerAssetV1 = SyncEntityType._(r'PartnerAssetV1');
|
||||||
static const partnerAssetBackfillV1 = SyncEntityType._(r'PartnerAssetBackfillV1');
|
static const partnerAssetBackfillV1 = SyncEntityType._(r'PartnerAssetBackfillV1');
|
||||||
static const partnerAssetDeleteV1 = SyncEntityType._(r'PartnerAssetDeleteV1');
|
static const partnerAssetDeleteV1 = SyncEntityType._(r'PartnerAssetDeleteV1');
|
||||||
@ -47,17 +47,21 @@ class SyncEntityType {
|
|||||||
static const albumToAssetV1 = SyncEntityType._(r'AlbumToAssetV1');
|
static const albumToAssetV1 = SyncEntityType._(r'AlbumToAssetV1');
|
||||||
static const albumToAssetDeleteV1 = SyncEntityType._(r'AlbumToAssetDeleteV1');
|
static const albumToAssetDeleteV1 = SyncEntityType._(r'AlbumToAssetDeleteV1');
|
||||||
static const albumToAssetBackfillV1 = SyncEntityType._(r'AlbumToAssetBackfillV1');
|
static const albumToAssetBackfillV1 = SyncEntityType._(r'AlbumToAssetBackfillV1');
|
||||||
|
static const memoryV1 = SyncEntityType._(r'MemoryV1');
|
||||||
|
static const memoryDeleteV1 = SyncEntityType._(r'MemoryDeleteV1');
|
||||||
|
static const memoryToAssetV1 = SyncEntityType._(r'MemoryToAssetV1');
|
||||||
|
static const memoryToAssetDeleteV1 = SyncEntityType._(r'MemoryToAssetDeleteV1');
|
||||||
static const syncAckV1 = SyncEntityType._(r'SyncAckV1');
|
static const syncAckV1 = SyncEntityType._(r'SyncAckV1');
|
||||||
|
|
||||||
/// List of all possible values in this [enum][SyncEntityType].
|
/// List of all possible values in this [enum][SyncEntityType].
|
||||||
static const values = <SyncEntityType>[
|
static const values = <SyncEntityType>[
|
||||||
userV1,
|
userV1,
|
||||||
userDeleteV1,
|
userDeleteV1,
|
||||||
partnerV1,
|
|
||||||
partnerDeleteV1,
|
|
||||||
assetV1,
|
assetV1,
|
||||||
assetDeleteV1,
|
assetDeleteV1,
|
||||||
assetExifV1,
|
assetExifV1,
|
||||||
|
partnerV1,
|
||||||
|
partnerDeleteV1,
|
||||||
partnerAssetV1,
|
partnerAssetV1,
|
||||||
partnerAssetBackfillV1,
|
partnerAssetBackfillV1,
|
||||||
partnerAssetDeleteV1,
|
partnerAssetDeleteV1,
|
||||||
@ -75,6 +79,10 @@ class SyncEntityType {
|
|||||||
albumToAssetV1,
|
albumToAssetV1,
|
||||||
albumToAssetDeleteV1,
|
albumToAssetDeleteV1,
|
||||||
albumToAssetBackfillV1,
|
albumToAssetBackfillV1,
|
||||||
|
memoryV1,
|
||||||
|
memoryDeleteV1,
|
||||||
|
memoryToAssetV1,
|
||||||
|
memoryToAssetDeleteV1,
|
||||||
syncAckV1,
|
syncAckV1,
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -116,11 +124,11 @@ class SyncEntityTypeTypeTransformer {
|
|||||||
switch (data) {
|
switch (data) {
|
||||||
case r'UserV1': return SyncEntityType.userV1;
|
case r'UserV1': return SyncEntityType.userV1;
|
||||||
case r'UserDeleteV1': return SyncEntityType.userDeleteV1;
|
case r'UserDeleteV1': return SyncEntityType.userDeleteV1;
|
||||||
case r'PartnerV1': return SyncEntityType.partnerV1;
|
|
||||||
case r'PartnerDeleteV1': return SyncEntityType.partnerDeleteV1;
|
|
||||||
case r'AssetV1': return SyncEntityType.assetV1;
|
case r'AssetV1': return SyncEntityType.assetV1;
|
||||||
case r'AssetDeleteV1': return SyncEntityType.assetDeleteV1;
|
case r'AssetDeleteV1': return SyncEntityType.assetDeleteV1;
|
||||||
case r'AssetExifV1': return SyncEntityType.assetExifV1;
|
case r'AssetExifV1': return SyncEntityType.assetExifV1;
|
||||||
|
case r'PartnerV1': return SyncEntityType.partnerV1;
|
||||||
|
case r'PartnerDeleteV1': return SyncEntityType.partnerDeleteV1;
|
||||||
case r'PartnerAssetV1': return SyncEntityType.partnerAssetV1;
|
case r'PartnerAssetV1': return SyncEntityType.partnerAssetV1;
|
||||||
case r'PartnerAssetBackfillV1': return SyncEntityType.partnerAssetBackfillV1;
|
case r'PartnerAssetBackfillV1': return SyncEntityType.partnerAssetBackfillV1;
|
||||||
case r'PartnerAssetDeleteV1': return SyncEntityType.partnerAssetDeleteV1;
|
case r'PartnerAssetDeleteV1': return SyncEntityType.partnerAssetDeleteV1;
|
||||||
@ -138,6 +146,10 @@ class SyncEntityTypeTypeTransformer {
|
|||||||
case r'AlbumToAssetV1': return SyncEntityType.albumToAssetV1;
|
case r'AlbumToAssetV1': return SyncEntityType.albumToAssetV1;
|
||||||
case r'AlbumToAssetDeleteV1': return SyncEntityType.albumToAssetDeleteV1;
|
case r'AlbumToAssetDeleteV1': return SyncEntityType.albumToAssetDeleteV1;
|
||||||
case r'AlbumToAssetBackfillV1': return SyncEntityType.albumToAssetBackfillV1;
|
case r'AlbumToAssetBackfillV1': return SyncEntityType.albumToAssetBackfillV1;
|
||||||
|
case r'MemoryV1': return SyncEntityType.memoryV1;
|
||||||
|
case r'MemoryDeleteV1': return SyncEntityType.memoryDeleteV1;
|
||||||
|
case r'MemoryToAssetV1': return SyncEntityType.memoryToAssetV1;
|
||||||
|
case r'MemoryToAssetDeleteV1': return SyncEntityType.memoryToAssetDeleteV1;
|
||||||
case r'SyncAckV1': return SyncEntityType.syncAckV1;
|
case r'SyncAckV1': return SyncEntityType.syncAckV1;
|
||||||
default:
|
default:
|
||||||
if (!allowNull) {
|
if (!allowNull) {
|
||||||
|
107
mobile/openapi/lib/model/sync_memory_asset_delete_v1.dart
generated
Normal file
107
mobile/openapi/lib/model/sync_memory_asset_delete_v1.dart
generated
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
//
|
||||||
|
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||||
|
//
|
||||||
|
// @dart=2.18
|
||||||
|
|
||||||
|
// ignore_for_file: unused_element, unused_import
|
||||||
|
// ignore_for_file: always_put_required_named_parameters_first
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
|
part of openapi.api;
|
||||||
|
|
||||||
|
class SyncMemoryAssetDeleteV1 {
|
||||||
|
/// Returns a new [SyncMemoryAssetDeleteV1] instance.
|
||||||
|
SyncMemoryAssetDeleteV1({
|
||||||
|
required this.assetId,
|
||||||
|
required this.memoryId,
|
||||||
|
});
|
||||||
|
|
||||||
|
String assetId;
|
||||||
|
|
||||||
|
String memoryId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) => identical(this, other) || other is SyncMemoryAssetDeleteV1 &&
|
||||||
|
other.assetId == assetId &&
|
||||||
|
other.memoryId == memoryId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
// ignore: unnecessary_parenthesis
|
||||||
|
(assetId.hashCode) +
|
||||||
|
(memoryId.hashCode);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'SyncMemoryAssetDeleteV1[assetId=$assetId, memoryId=$memoryId]';
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final json = <String, dynamic>{};
|
||||||
|
json[r'assetId'] = this.assetId;
|
||||||
|
json[r'memoryId'] = this.memoryId;
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new [SyncMemoryAssetDeleteV1] instance and imports its values from
|
||||||
|
/// [value] if it's a [Map], null otherwise.
|
||||||
|
// ignore: prefer_constructors_over_static_methods
|
||||||
|
static SyncMemoryAssetDeleteV1? fromJson(dynamic value) {
|
||||||
|
upgradeDto(value, "SyncMemoryAssetDeleteV1");
|
||||||
|
if (value is Map) {
|
||||||
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
|
return SyncMemoryAssetDeleteV1(
|
||||||
|
assetId: mapValueOfType<String>(json, r'assetId')!,
|
||||||
|
memoryId: mapValueOfType<String>(json, r'memoryId')!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<SyncMemoryAssetDeleteV1> listFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final result = <SyncMemoryAssetDeleteV1>[];
|
||||||
|
if (json is List && json.isNotEmpty) {
|
||||||
|
for (final row in json) {
|
||||||
|
final value = SyncMemoryAssetDeleteV1.fromJson(row);
|
||||||
|
if (value != null) {
|
||||||
|
result.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toList(growable: growable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, SyncMemoryAssetDeleteV1> mapFromJson(dynamic json) {
|
||||||
|
final map = <String, SyncMemoryAssetDeleteV1>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
final value = SyncMemoryAssetDeleteV1.fromJson(entry.value);
|
||||||
|
if (value != null) {
|
||||||
|
map[entry.key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// maps a json object with a list of SyncMemoryAssetDeleteV1-objects as value to a dart map
|
||||||
|
static Map<String, List<SyncMemoryAssetDeleteV1>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final map = <String, List<SyncMemoryAssetDeleteV1>>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
json = json.cast<String, dynamic>();
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
map[entry.key] = SyncMemoryAssetDeleteV1.listFromJson(entry.value, growable: growable,);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The list of required keys that must be present in a JSON.
|
||||||
|
static const requiredKeys = <String>{
|
||||||
|
'assetId',
|
||||||
|
'memoryId',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
107
mobile/openapi/lib/model/sync_memory_asset_v1.dart
generated
Normal file
107
mobile/openapi/lib/model/sync_memory_asset_v1.dart
generated
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
//
|
||||||
|
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||||
|
//
|
||||||
|
// @dart=2.18
|
||||||
|
|
||||||
|
// ignore_for_file: unused_element, unused_import
|
||||||
|
// ignore_for_file: always_put_required_named_parameters_first
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
|
part of openapi.api;
|
||||||
|
|
||||||
|
class SyncMemoryAssetV1 {
|
||||||
|
/// Returns a new [SyncMemoryAssetV1] instance.
|
||||||
|
SyncMemoryAssetV1({
|
||||||
|
required this.assetId,
|
||||||
|
required this.memoryId,
|
||||||
|
});
|
||||||
|
|
||||||
|
String assetId;
|
||||||
|
|
||||||
|
String memoryId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) => identical(this, other) || other is SyncMemoryAssetV1 &&
|
||||||
|
other.assetId == assetId &&
|
||||||
|
other.memoryId == memoryId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
// ignore: unnecessary_parenthesis
|
||||||
|
(assetId.hashCode) +
|
||||||
|
(memoryId.hashCode);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'SyncMemoryAssetV1[assetId=$assetId, memoryId=$memoryId]';
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final json = <String, dynamic>{};
|
||||||
|
json[r'assetId'] = this.assetId;
|
||||||
|
json[r'memoryId'] = this.memoryId;
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new [SyncMemoryAssetV1] instance and imports its values from
|
||||||
|
/// [value] if it's a [Map], null otherwise.
|
||||||
|
// ignore: prefer_constructors_over_static_methods
|
||||||
|
static SyncMemoryAssetV1? fromJson(dynamic value) {
|
||||||
|
upgradeDto(value, "SyncMemoryAssetV1");
|
||||||
|
if (value is Map) {
|
||||||
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
|
return SyncMemoryAssetV1(
|
||||||
|
assetId: mapValueOfType<String>(json, r'assetId')!,
|
||||||
|
memoryId: mapValueOfType<String>(json, r'memoryId')!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<SyncMemoryAssetV1> listFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final result = <SyncMemoryAssetV1>[];
|
||||||
|
if (json is List && json.isNotEmpty) {
|
||||||
|
for (final row in json) {
|
||||||
|
final value = SyncMemoryAssetV1.fromJson(row);
|
||||||
|
if (value != null) {
|
||||||
|
result.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toList(growable: growable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, SyncMemoryAssetV1> mapFromJson(dynamic json) {
|
||||||
|
final map = <String, SyncMemoryAssetV1>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
final value = SyncMemoryAssetV1.fromJson(entry.value);
|
||||||
|
if (value != null) {
|
||||||
|
map[entry.key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// maps a json object with a list of SyncMemoryAssetV1-objects as value to a dart map
|
||||||
|
static Map<String, List<SyncMemoryAssetV1>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final map = <String, List<SyncMemoryAssetV1>>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
json = json.cast<String, dynamic>();
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
map[entry.key] = SyncMemoryAssetV1.listFromJson(entry.value, growable: growable,);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The list of required keys that must be present in a JSON.
|
||||||
|
static const requiredKeys = <String>{
|
||||||
|
'assetId',
|
||||||
|
'memoryId',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
99
mobile/openapi/lib/model/sync_memory_delete_v1.dart
generated
Normal file
99
mobile/openapi/lib/model/sync_memory_delete_v1.dart
generated
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
//
|
||||||
|
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||||
|
//
|
||||||
|
// @dart=2.18
|
||||||
|
|
||||||
|
// ignore_for_file: unused_element, unused_import
|
||||||
|
// ignore_for_file: always_put_required_named_parameters_first
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
|
part of openapi.api;
|
||||||
|
|
||||||
|
class SyncMemoryDeleteV1 {
|
||||||
|
/// Returns a new [SyncMemoryDeleteV1] instance.
|
||||||
|
SyncMemoryDeleteV1({
|
||||||
|
required this.memoryId,
|
||||||
|
});
|
||||||
|
|
||||||
|
String memoryId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) => identical(this, other) || other is SyncMemoryDeleteV1 &&
|
||||||
|
other.memoryId == memoryId;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
// ignore: unnecessary_parenthesis
|
||||||
|
(memoryId.hashCode);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'SyncMemoryDeleteV1[memoryId=$memoryId]';
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final json = <String, dynamic>{};
|
||||||
|
json[r'memoryId'] = this.memoryId;
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new [SyncMemoryDeleteV1] instance and imports its values from
|
||||||
|
/// [value] if it's a [Map], null otherwise.
|
||||||
|
// ignore: prefer_constructors_over_static_methods
|
||||||
|
static SyncMemoryDeleteV1? fromJson(dynamic value) {
|
||||||
|
upgradeDto(value, "SyncMemoryDeleteV1");
|
||||||
|
if (value is Map) {
|
||||||
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
|
return SyncMemoryDeleteV1(
|
||||||
|
memoryId: mapValueOfType<String>(json, r'memoryId')!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<SyncMemoryDeleteV1> listFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final result = <SyncMemoryDeleteV1>[];
|
||||||
|
if (json is List && json.isNotEmpty) {
|
||||||
|
for (final row in json) {
|
||||||
|
final value = SyncMemoryDeleteV1.fromJson(row);
|
||||||
|
if (value != null) {
|
||||||
|
result.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toList(growable: growable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, SyncMemoryDeleteV1> mapFromJson(dynamic json) {
|
||||||
|
final map = <String, SyncMemoryDeleteV1>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
final value = SyncMemoryDeleteV1.fromJson(entry.value);
|
||||||
|
if (value != null) {
|
||||||
|
map[entry.key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// maps a json object with a list of SyncMemoryDeleteV1-objects as value to a dart map
|
||||||
|
static Map<String, List<SyncMemoryDeleteV1>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final map = <String, List<SyncMemoryDeleteV1>>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
json = json.cast<String, dynamic>();
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
map[entry.key] = SyncMemoryDeleteV1.listFromJson(entry.value, growable: growable,);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The list of required keys that must be present in a JSON.
|
||||||
|
static const requiredKeys = <String>{
|
||||||
|
'memoryId',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
203
mobile/openapi/lib/model/sync_memory_v1.dart
generated
Normal file
203
mobile/openapi/lib/model/sync_memory_v1.dart
generated
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
//
|
||||||
|
// AUTO-GENERATED FILE, DO NOT MODIFY!
|
||||||
|
//
|
||||||
|
// @dart=2.18
|
||||||
|
|
||||||
|
// ignore_for_file: unused_element, unused_import
|
||||||
|
// ignore_for_file: always_put_required_named_parameters_first
|
||||||
|
// ignore_for_file: constant_identifier_names
|
||||||
|
// ignore_for_file: lines_longer_than_80_chars
|
||||||
|
|
||||||
|
part of openapi.api;
|
||||||
|
|
||||||
|
class SyncMemoryV1 {
|
||||||
|
/// Returns a new [SyncMemoryV1] instance.
|
||||||
|
SyncMemoryV1({
|
||||||
|
required this.createdAt,
|
||||||
|
required this.data,
|
||||||
|
required this.deletedAt,
|
||||||
|
required this.hideAt,
|
||||||
|
required this.id,
|
||||||
|
required this.isSaved,
|
||||||
|
required this.memoryAt,
|
||||||
|
required this.ownerId,
|
||||||
|
required this.seenAt,
|
||||||
|
required this.showAt,
|
||||||
|
required this.type,
|
||||||
|
required this.updatedAt,
|
||||||
|
});
|
||||||
|
|
||||||
|
DateTime createdAt;
|
||||||
|
|
||||||
|
Object data;
|
||||||
|
|
||||||
|
DateTime? deletedAt;
|
||||||
|
|
||||||
|
DateTime? hideAt;
|
||||||
|
|
||||||
|
String id;
|
||||||
|
|
||||||
|
bool isSaved;
|
||||||
|
|
||||||
|
DateTime memoryAt;
|
||||||
|
|
||||||
|
String ownerId;
|
||||||
|
|
||||||
|
DateTime? seenAt;
|
||||||
|
|
||||||
|
DateTime? showAt;
|
||||||
|
|
||||||
|
MemoryType type;
|
||||||
|
|
||||||
|
DateTime updatedAt;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool operator ==(Object other) => identical(this, other) || other is SyncMemoryV1 &&
|
||||||
|
other.createdAt == createdAt &&
|
||||||
|
other.data == data &&
|
||||||
|
other.deletedAt == deletedAt &&
|
||||||
|
other.hideAt == hideAt &&
|
||||||
|
other.id == id &&
|
||||||
|
other.isSaved == isSaved &&
|
||||||
|
other.memoryAt == memoryAt &&
|
||||||
|
other.ownerId == ownerId &&
|
||||||
|
other.seenAt == seenAt &&
|
||||||
|
other.showAt == showAt &&
|
||||||
|
other.type == type &&
|
||||||
|
other.updatedAt == updatedAt;
|
||||||
|
|
||||||
|
@override
|
||||||
|
int get hashCode =>
|
||||||
|
// ignore: unnecessary_parenthesis
|
||||||
|
(createdAt.hashCode) +
|
||||||
|
(data.hashCode) +
|
||||||
|
(deletedAt == null ? 0 : deletedAt!.hashCode) +
|
||||||
|
(hideAt == null ? 0 : hideAt!.hashCode) +
|
||||||
|
(id.hashCode) +
|
||||||
|
(isSaved.hashCode) +
|
||||||
|
(memoryAt.hashCode) +
|
||||||
|
(ownerId.hashCode) +
|
||||||
|
(seenAt == null ? 0 : seenAt!.hashCode) +
|
||||||
|
(showAt == null ? 0 : showAt!.hashCode) +
|
||||||
|
(type.hashCode) +
|
||||||
|
(updatedAt.hashCode);
|
||||||
|
|
||||||
|
@override
|
||||||
|
String toString() => 'SyncMemoryV1[createdAt=$createdAt, data=$data, deletedAt=$deletedAt, hideAt=$hideAt, id=$id, isSaved=$isSaved, memoryAt=$memoryAt, ownerId=$ownerId, seenAt=$seenAt, showAt=$showAt, type=$type, updatedAt=$updatedAt]';
|
||||||
|
|
||||||
|
Map<String, dynamic> toJson() {
|
||||||
|
final json = <String, dynamic>{};
|
||||||
|
json[r'createdAt'] = this.createdAt.toUtc().toIso8601String();
|
||||||
|
json[r'data'] = this.data;
|
||||||
|
if (this.deletedAt != null) {
|
||||||
|
json[r'deletedAt'] = this.deletedAt!.toUtc().toIso8601String();
|
||||||
|
} else {
|
||||||
|
// json[r'deletedAt'] = null;
|
||||||
|
}
|
||||||
|
if (this.hideAt != null) {
|
||||||
|
json[r'hideAt'] = this.hideAt!.toUtc().toIso8601String();
|
||||||
|
} else {
|
||||||
|
// json[r'hideAt'] = null;
|
||||||
|
}
|
||||||
|
json[r'id'] = this.id;
|
||||||
|
json[r'isSaved'] = this.isSaved;
|
||||||
|
json[r'memoryAt'] = this.memoryAt.toUtc().toIso8601String();
|
||||||
|
json[r'ownerId'] = this.ownerId;
|
||||||
|
if (this.seenAt != null) {
|
||||||
|
json[r'seenAt'] = this.seenAt!.toUtc().toIso8601String();
|
||||||
|
} else {
|
||||||
|
// json[r'seenAt'] = null;
|
||||||
|
}
|
||||||
|
if (this.showAt != null) {
|
||||||
|
json[r'showAt'] = this.showAt!.toUtc().toIso8601String();
|
||||||
|
} else {
|
||||||
|
// json[r'showAt'] = null;
|
||||||
|
}
|
||||||
|
json[r'type'] = this.type;
|
||||||
|
json[r'updatedAt'] = this.updatedAt.toUtc().toIso8601String();
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a new [SyncMemoryV1] instance and imports its values from
|
||||||
|
/// [value] if it's a [Map], null otherwise.
|
||||||
|
// ignore: prefer_constructors_over_static_methods
|
||||||
|
static SyncMemoryV1? fromJson(dynamic value) {
|
||||||
|
upgradeDto(value, "SyncMemoryV1");
|
||||||
|
if (value is Map) {
|
||||||
|
final json = value.cast<String, dynamic>();
|
||||||
|
|
||||||
|
return SyncMemoryV1(
|
||||||
|
createdAt: mapDateTime(json, r'createdAt', r'')!,
|
||||||
|
data: mapValueOfType<Object>(json, r'data')!,
|
||||||
|
deletedAt: mapDateTime(json, r'deletedAt', r''),
|
||||||
|
hideAt: mapDateTime(json, r'hideAt', r''),
|
||||||
|
id: mapValueOfType<String>(json, r'id')!,
|
||||||
|
isSaved: mapValueOfType<bool>(json, r'isSaved')!,
|
||||||
|
memoryAt: mapDateTime(json, r'memoryAt', r'')!,
|
||||||
|
ownerId: mapValueOfType<String>(json, r'ownerId')!,
|
||||||
|
seenAt: mapDateTime(json, r'seenAt', r''),
|
||||||
|
showAt: mapDateTime(json, r'showAt', r''),
|
||||||
|
type: MemoryType.fromJson(json[r'type'])!,
|
||||||
|
updatedAt: mapDateTime(json, r'updatedAt', r'')!,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static List<SyncMemoryV1> listFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final result = <SyncMemoryV1>[];
|
||||||
|
if (json is List && json.isNotEmpty) {
|
||||||
|
for (final row in json) {
|
||||||
|
final value = SyncMemoryV1.fromJson(row);
|
||||||
|
if (value != null) {
|
||||||
|
result.add(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result.toList(growable: growable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, SyncMemoryV1> mapFromJson(dynamic json) {
|
||||||
|
final map = <String, SyncMemoryV1>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
json = json.cast<String, dynamic>(); // ignore: parameter_assignments
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
final value = SyncMemoryV1.fromJson(entry.value);
|
||||||
|
if (value != null) {
|
||||||
|
map[entry.key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
// maps a json object with a list of SyncMemoryV1-objects as value to a dart map
|
||||||
|
static Map<String, List<SyncMemoryV1>> mapListFromJson(dynamic json, {bool growable = false,}) {
|
||||||
|
final map = <String, List<SyncMemoryV1>>{};
|
||||||
|
if (json is Map && json.isNotEmpty) {
|
||||||
|
// ignore: parameter_assignments
|
||||||
|
json = json.cast<String, dynamic>();
|
||||||
|
for (final entry in json.entries) {
|
||||||
|
map[entry.key] = SyncMemoryV1.listFromJson(entry.value, growable: growable,);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The list of required keys that must be present in a JSON.
|
||||||
|
static const requiredKeys = <String>{
|
||||||
|
'createdAt',
|
||||||
|
'data',
|
||||||
|
'deletedAt',
|
||||||
|
'hideAt',
|
||||||
|
'id',
|
||||||
|
'isSaved',
|
||||||
|
'memoryAt',
|
||||||
|
'ownerId',
|
||||||
|
'seenAt',
|
||||||
|
'showAt',
|
||||||
|
'type',
|
||||||
|
'updatedAt',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
6
mobile/openapi/lib/model/sync_request_type.dart
generated
6
mobile/openapi/lib/model/sync_request_type.dart
generated
@ -34,6 +34,8 @@ class SyncRequestType {
|
|||||||
static const albumToAssetsV1 = SyncRequestType._(r'AlbumToAssetsV1');
|
static const albumToAssetsV1 = SyncRequestType._(r'AlbumToAssetsV1');
|
||||||
static const albumAssetsV1 = SyncRequestType._(r'AlbumAssetsV1');
|
static const albumAssetsV1 = SyncRequestType._(r'AlbumAssetsV1');
|
||||||
static const albumAssetExifsV1 = SyncRequestType._(r'AlbumAssetExifsV1');
|
static const albumAssetExifsV1 = SyncRequestType._(r'AlbumAssetExifsV1');
|
||||||
|
static const memoriesV1 = SyncRequestType._(r'MemoriesV1');
|
||||||
|
static const memoryToAssetsV1 = SyncRequestType._(r'MemoryToAssetsV1');
|
||||||
|
|
||||||
/// List of all possible values in this [enum][SyncRequestType].
|
/// List of all possible values in this [enum][SyncRequestType].
|
||||||
static const values = <SyncRequestType>[
|
static const values = <SyncRequestType>[
|
||||||
@ -48,6 +50,8 @@ class SyncRequestType {
|
|||||||
albumToAssetsV1,
|
albumToAssetsV1,
|
||||||
albumAssetsV1,
|
albumAssetsV1,
|
||||||
albumAssetExifsV1,
|
albumAssetExifsV1,
|
||||||
|
memoriesV1,
|
||||||
|
memoryToAssetsV1,
|
||||||
];
|
];
|
||||||
|
|
||||||
static SyncRequestType? fromJson(dynamic value) => SyncRequestTypeTypeTransformer().decode(value);
|
static SyncRequestType? fromJson(dynamic value) => SyncRequestTypeTypeTransformer().decode(value);
|
||||||
@ -97,6 +101,8 @@ class SyncRequestTypeTypeTransformer {
|
|||||||
case r'AlbumToAssetsV1': return SyncRequestType.albumToAssetsV1;
|
case r'AlbumToAssetsV1': return SyncRequestType.albumToAssetsV1;
|
||||||
case r'AlbumAssetsV1': return SyncRequestType.albumAssetsV1;
|
case r'AlbumAssetsV1': return SyncRequestType.albumAssetsV1;
|
||||||
case r'AlbumAssetExifsV1': return SyncRequestType.albumAssetExifsV1;
|
case r'AlbumAssetExifsV1': return SyncRequestType.albumAssetExifsV1;
|
||||||
|
case r'MemoriesV1': return SyncRequestType.memoriesV1;
|
||||||
|
case r'MemoryToAssetsV1': return SyncRequestType.memoryToAssetsV1;
|
||||||
default:
|
default:
|
||||||
if (!allowNull) {
|
if (!allowNull) {
|
||||||
throw ArgumentError('Unknown enum value to decode: $data');
|
throw ArgumentError('Unknown enum value to decode: $data');
|
||||||
|
@ -13794,11 +13794,11 @@
|
|||||||
"enum": [
|
"enum": [
|
||||||
"UserV1",
|
"UserV1",
|
||||||
"UserDeleteV1",
|
"UserDeleteV1",
|
||||||
"PartnerV1",
|
|
||||||
"PartnerDeleteV1",
|
|
||||||
"AssetV1",
|
"AssetV1",
|
||||||
"AssetDeleteV1",
|
"AssetDeleteV1",
|
||||||
"AssetExifV1",
|
"AssetExifV1",
|
||||||
|
"PartnerV1",
|
||||||
|
"PartnerDeleteV1",
|
||||||
"PartnerAssetV1",
|
"PartnerAssetV1",
|
||||||
"PartnerAssetBackfillV1",
|
"PartnerAssetBackfillV1",
|
||||||
"PartnerAssetDeleteV1",
|
"PartnerAssetDeleteV1",
|
||||||
@ -13816,10 +13816,125 @@
|
|||||||
"AlbumToAssetV1",
|
"AlbumToAssetV1",
|
||||||
"AlbumToAssetDeleteV1",
|
"AlbumToAssetDeleteV1",
|
||||||
"AlbumToAssetBackfillV1",
|
"AlbumToAssetBackfillV1",
|
||||||
|
"MemoryV1",
|
||||||
|
"MemoryDeleteV1",
|
||||||
|
"MemoryToAssetV1",
|
||||||
|
"MemoryToAssetDeleteV1",
|
||||||
"SyncAckV1"
|
"SyncAckV1"
|
||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"SyncMemoryAssetDeleteV1": {
|
||||||
|
"properties": {
|
||||||
|
"assetId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"memoryId": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"assetId",
|
||||||
|
"memoryId"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"SyncMemoryAssetV1": {
|
||||||
|
"properties": {
|
||||||
|
"assetId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"memoryId": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"assetId",
|
||||||
|
"memoryId"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"SyncMemoryDeleteV1": {
|
||||||
|
"properties": {
|
||||||
|
"memoryId": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"memoryId"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"SyncMemoryV1": {
|
||||||
|
"properties": {
|
||||||
|
"createdAt": {
|
||||||
|
"format": "date-time",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
|
"deletedAt": {
|
||||||
|
"format": "date-time",
|
||||||
|
"nullable": true,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"hideAt": {
|
||||||
|
"format": "date-time",
|
||||||
|
"nullable": true,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"id": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"isSaved": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
|
"memoryAt": {
|
||||||
|
"format": "date-time",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"ownerId": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"seenAt": {
|
||||||
|
"format": "date-time",
|
||||||
|
"nullable": true,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"showAt": {
|
||||||
|
"format": "date-time",
|
||||||
|
"nullable": true,
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": {
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/MemoryType"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"updatedAt": {
|
||||||
|
"format": "date-time",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": [
|
||||||
|
"createdAt",
|
||||||
|
"data",
|
||||||
|
"deletedAt",
|
||||||
|
"hideAt",
|
||||||
|
"id",
|
||||||
|
"isSaved",
|
||||||
|
"memoryAt",
|
||||||
|
"ownerId",
|
||||||
|
"seenAt",
|
||||||
|
"showAt",
|
||||||
|
"type",
|
||||||
|
"updatedAt"
|
||||||
|
],
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
"SyncPartnerDeleteV1": {
|
"SyncPartnerDeleteV1": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"sharedById": {
|
"sharedById": {
|
||||||
@ -13866,7 +13981,9 @@
|
|||||||
"AlbumUsersV1",
|
"AlbumUsersV1",
|
||||||
"AlbumToAssetsV1",
|
"AlbumToAssetsV1",
|
||||||
"AlbumAssetsV1",
|
"AlbumAssetsV1",
|
||||||
"AlbumAssetExifsV1"
|
"AlbumAssetExifsV1",
|
||||||
|
"MemoriesV1",
|
||||||
|
"MemoryToAssetsV1"
|
||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
@ -4063,11 +4063,11 @@ export enum Error2 {
|
|||||||
export enum SyncEntityType {
|
export enum SyncEntityType {
|
||||||
UserV1 = "UserV1",
|
UserV1 = "UserV1",
|
||||||
UserDeleteV1 = "UserDeleteV1",
|
UserDeleteV1 = "UserDeleteV1",
|
||||||
PartnerV1 = "PartnerV1",
|
|
||||||
PartnerDeleteV1 = "PartnerDeleteV1",
|
|
||||||
AssetV1 = "AssetV1",
|
AssetV1 = "AssetV1",
|
||||||
AssetDeleteV1 = "AssetDeleteV1",
|
AssetDeleteV1 = "AssetDeleteV1",
|
||||||
AssetExifV1 = "AssetExifV1",
|
AssetExifV1 = "AssetExifV1",
|
||||||
|
PartnerV1 = "PartnerV1",
|
||||||
|
PartnerDeleteV1 = "PartnerDeleteV1",
|
||||||
PartnerAssetV1 = "PartnerAssetV1",
|
PartnerAssetV1 = "PartnerAssetV1",
|
||||||
PartnerAssetBackfillV1 = "PartnerAssetBackfillV1",
|
PartnerAssetBackfillV1 = "PartnerAssetBackfillV1",
|
||||||
PartnerAssetDeleteV1 = "PartnerAssetDeleteV1",
|
PartnerAssetDeleteV1 = "PartnerAssetDeleteV1",
|
||||||
@ -4085,6 +4085,10 @@ export enum SyncEntityType {
|
|||||||
AlbumToAssetV1 = "AlbumToAssetV1",
|
AlbumToAssetV1 = "AlbumToAssetV1",
|
||||||
AlbumToAssetDeleteV1 = "AlbumToAssetDeleteV1",
|
AlbumToAssetDeleteV1 = "AlbumToAssetDeleteV1",
|
||||||
AlbumToAssetBackfillV1 = "AlbumToAssetBackfillV1",
|
AlbumToAssetBackfillV1 = "AlbumToAssetBackfillV1",
|
||||||
|
MemoryV1 = "MemoryV1",
|
||||||
|
MemoryDeleteV1 = "MemoryDeleteV1",
|
||||||
|
MemoryToAssetV1 = "MemoryToAssetV1",
|
||||||
|
MemoryToAssetDeleteV1 = "MemoryToAssetDeleteV1",
|
||||||
SyncAckV1 = "SyncAckV1"
|
SyncAckV1 = "SyncAckV1"
|
||||||
}
|
}
|
||||||
export enum SyncRequestType {
|
export enum SyncRequestType {
|
||||||
@ -4098,7 +4102,9 @@ export enum SyncRequestType {
|
|||||||
AlbumUsersV1 = "AlbumUsersV1",
|
AlbumUsersV1 = "AlbumUsersV1",
|
||||||
AlbumToAssetsV1 = "AlbumToAssetsV1",
|
AlbumToAssetsV1 = "AlbumToAssetsV1",
|
||||||
AlbumAssetsV1 = "AlbumAssetsV1",
|
AlbumAssetsV1 = "AlbumAssetsV1",
|
||||||
AlbumAssetExifsV1 = "AlbumAssetExifsV1"
|
AlbumAssetExifsV1 = "AlbumAssetExifsV1",
|
||||||
|
MemoriesV1 = "MemoriesV1",
|
||||||
|
MemoryToAssetsV1 = "MemoryToAssetsV1"
|
||||||
}
|
}
|
||||||
export enum TranscodeHWAccel {
|
export enum TranscodeHWAccel {
|
||||||
Nvenc = "nvenc",
|
Nvenc = "nvenc",
|
||||||
|
@ -13,7 +13,7 @@ import {
|
|||||||
UserAvatarColor,
|
UserAvatarColor,
|
||||||
UserStatus,
|
UserStatus,
|
||||||
} from 'src/enum';
|
} from 'src/enum';
|
||||||
import { OnThisDayData, UserMetadataItem } from 'src/types';
|
import { UserMetadataItem } from 'src/types';
|
||||||
|
|
||||||
export type AuthUser = {
|
export type AuthUser = {
|
||||||
id: string;
|
id: string;
|
||||||
@ -95,7 +95,7 @@ export type Memory = {
|
|||||||
showAt: Date | null;
|
showAt: Date | null;
|
||||||
hideAt: Date | null;
|
hideAt: Date | null;
|
||||||
type: MemoryType;
|
type: MemoryType;
|
||||||
data: OnThisDayData;
|
data: object;
|
||||||
ownerId: string;
|
ownerId: string;
|
||||||
isSaved: boolean;
|
isSaved: boolean;
|
||||||
assets: MapAsset[];
|
assets: MapAsset[];
|
||||||
|
16
server/src/db.d.ts
vendored
16
server/src/db.d.ts
vendored
@ -19,8 +19,11 @@ import {
|
|||||||
SourceType,
|
SourceType,
|
||||||
SyncEntityType,
|
SyncEntityType,
|
||||||
} from 'src/enum';
|
} from 'src/enum';
|
||||||
|
import { MemoryAssetAuditTable } from 'src/schema/tables/memory-asset-audit.table';
|
||||||
|
import { MemoryAssetTable } from 'src/schema/tables/memory-asset.table';
|
||||||
|
import { MemoryAuditTable } from 'src/schema/tables/memory-audit.table';
|
||||||
import { UserTable } from 'src/schema/tables/user.table';
|
import { UserTable } from 'src/schema/tables/user.table';
|
||||||
import { OnThisDayData, UserMetadataItem } from 'src/types';
|
import { UserMetadataItem } from 'src/types';
|
||||||
|
|
||||||
export type ArrayType<T> = ArrayTypeImpl<T> extends (infer U)[] ? U[] : ArrayTypeImpl<T>;
|
export type ArrayType<T> = ArrayTypeImpl<T> extends (infer U)[] ? U[] : ArrayTypeImpl<T>;
|
||||||
|
|
||||||
@ -278,7 +281,7 @@ export interface Libraries {
|
|||||||
|
|
||||||
export interface Memories {
|
export interface Memories {
|
||||||
createdAt: Generated<Timestamp>;
|
createdAt: Generated<Timestamp>;
|
||||||
data: OnThisDayData;
|
data: object;
|
||||||
deletedAt: Timestamp | null;
|
deletedAt: Timestamp | null;
|
||||||
hideAt: Timestamp | null;
|
hideAt: Timestamp | null;
|
||||||
id: Generated<string>;
|
id: Generated<string>;
|
||||||
@ -307,11 +310,6 @@ export interface Notifications {
|
|||||||
readAt: Timestamp | null;
|
readAt: Timestamp | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface MemoriesAssetsAssets {
|
|
||||||
assetsId: string;
|
|
||||||
memoriesId: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface Migrations {
|
export interface Migrations {
|
||||||
id: Generated<number>;
|
id: Generated<number>;
|
||||||
name: string;
|
name: string;
|
||||||
@ -512,7 +510,9 @@ export interface DB {
|
|||||||
geodata_places: GeodataPlaces;
|
geodata_places: GeodataPlaces;
|
||||||
libraries: Libraries;
|
libraries: Libraries;
|
||||||
memories: Memories;
|
memories: Memories;
|
||||||
memories_assets_assets: MemoriesAssetsAssets;
|
memories_audit: MemoryAuditTable;
|
||||||
|
memories_assets_assets: MemoryAssetTable;
|
||||||
|
memory_assets_audit: MemoryAssetAuditTable;
|
||||||
migrations: Migrations;
|
migrations: Migrations;
|
||||||
notifications: Notifications;
|
notifications: Notifications;
|
||||||
move_history: MoveHistory;
|
move_history: MoveHistory;
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
|
/* eslint-disable @typescript-eslint/no-unsafe-function-type */
|
||||||
import { ApiProperty } from '@nestjs/swagger';
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
import { ArrayMaxSize, IsEnum, IsInt, IsPositive, IsString } from 'class-validator';
|
import { ArrayMaxSize, IsEnum, IsInt, IsPositive, IsString } from 'class-validator';
|
||||||
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
import { AssetResponseDto } from 'src/dtos/asset-response.dto';
|
||||||
import { AlbumUserRole, AssetOrder, AssetType, AssetVisibility, SyncEntityType, SyncRequestType } from 'src/enum';
|
import {
|
||||||
|
AlbumUserRole,
|
||||||
|
AssetOrder,
|
||||||
|
AssetType,
|
||||||
|
AssetVisibility,
|
||||||
|
MemoryType,
|
||||||
|
SyncEntityType,
|
||||||
|
SyncRequestType,
|
||||||
|
} from 'src/enum';
|
||||||
import { Optional, ValidateDate, ValidateUUID } from 'src/validation';
|
import { Optional, ValidateDate, ValidateUUID } from 'src/validation';
|
||||||
|
|
||||||
export class AssetFullSyncDto {
|
export class AssetFullSyncDto {
|
||||||
@ -34,6 +43,15 @@ export class AssetDeltaSyncResponseDto {
|
|||||||
deleted!: string[];
|
deleted!: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const extraSyncModels: Function[] = [];
|
||||||
|
|
||||||
|
export const ExtraModel = (): ClassDecorator => {
|
||||||
|
return (object: Function) => {
|
||||||
|
extraSyncModels.push(object);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
export class SyncUserV1 {
|
export class SyncUserV1 {
|
||||||
id!: string;
|
id!: string;
|
||||||
name!: string;
|
name!: string;
|
||||||
@ -41,21 +59,25 @@ export class SyncUserV1 {
|
|||||||
deletedAt!: Date | null;
|
deletedAt!: Date | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
export class SyncUserDeleteV1 {
|
export class SyncUserDeleteV1 {
|
||||||
userId!: string;
|
userId!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
export class SyncPartnerV1 {
|
export class SyncPartnerV1 {
|
||||||
sharedById!: string;
|
sharedById!: string;
|
||||||
sharedWithId!: string;
|
sharedWithId!: string;
|
||||||
inTimeline!: boolean;
|
inTimeline!: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
export class SyncPartnerDeleteV1 {
|
export class SyncPartnerDeleteV1 {
|
||||||
sharedById!: string;
|
sharedById!: string;
|
||||||
sharedWithId!: string;
|
sharedWithId!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
export class SyncAssetV1 {
|
export class SyncAssetV1 {
|
||||||
id!: string;
|
id!: string;
|
||||||
ownerId!: string;
|
ownerId!: string;
|
||||||
@ -74,10 +96,12 @@ export class SyncAssetV1 {
|
|||||||
visibility!: AssetVisibility;
|
visibility!: AssetVisibility;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
export class SyncAssetDeleteV1 {
|
export class SyncAssetDeleteV1 {
|
||||||
assetId!: string;
|
assetId!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
export class SyncAssetExifV1 {
|
export class SyncAssetExifV1 {
|
||||||
assetId!: string;
|
assetId!: string;
|
||||||
description!: string | null;
|
description!: string | null;
|
||||||
@ -116,15 +140,18 @@ export class SyncAssetExifV1 {
|
|||||||
fps!: number | null;
|
fps!: number | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
export class SyncAlbumDeleteV1 {
|
export class SyncAlbumDeleteV1 {
|
||||||
albumId!: string;
|
albumId!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
export class SyncAlbumUserDeleteV1 {
|
export class SyncAlbumUserDeleteV1 {
|
||||||
albumId!: string;
|
albumId!: string;
|
||||||
userId!: string;
|
userId!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
export class SyncAlbumUserV1 {
|
export class SyncAlbumUserV1 {
|
||||||
albumId!: string;
|
albumId!: string;
|
||||||
userId!: string;
|
userId!: string;
|
||||||
@ -132,6 +159,7 @@ export class SyncAlbumUserV1 {
|
|||||||
role!: AlbumUserRole;
|
role!: AlbumUserRole;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
export class SyncAlbumV1 {
|
export class SyncAlbumV1 {
|
||||||
id!: string;
|
id!: string;
|
||||||
ownerId!: string;
|
ownerId!: string;
|
||||||
@ -145,16 +173,53 @@ export class SyncAlbumV1 {
|
|||||||
order!: AssetOrder;
|
order!: AssetOrder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
export class SyncAlbumToAssetV1 {
|
export class SyncAlbumToAssetV1 {
|
||||||
albumId!: string;
|
albumId!: string;
|
||||||
assetId!: string;
|
assetId!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
export class SyncAlbumToAssetDeleteV1 {
|
export class SyncAlbumToAssetDeleteV1 {
|
||||||
albumId!: string;
|
albumId!: string;
|
||||||
assetId!: string;
|
assetId!: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
|
export class SyncMemoryV1 {
|
||||||
|
id!: string;
|
||||||
|
createdAt!: Date;
|
||||||
|
updatedAt!: Date;
|
||||||
|
deletedAt!: Date | null;
|
||||||
|
ownerId!: string;
|
||||||
|
@ApiProperty({ enumName: 'MemoryType', enum: MemoryType })
|
||||||
|
type!: MemoryType;
|
||||||
|
data!: object;
|
||||||
|
isSaved!: boolean;
|
||||||
|
memoryAt!: Date;
|
||||||
|
seenAt!: Date | null;
|
||||||
|
showAt!: Date | null;
|
||||||
|
hideAt!: Date | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
|
export class SyncMemoryDeleteV1 {
|
||||||
|
memoryId!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
|
export class SyncMemoryAssetV1 {
|
||||||
|
memoryId!: string;
|
||||||
|
assetId!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
|
export class SyncMemoryAssetDeleteV1 {
|
||||||
|
memoryId!: string;
|
||||||
|
assetId!: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ExtraModel()
|
||||||
export class SyncAckV1 {}
|
export class SyncAckV1 {}
|
||||||
|
|
||||||
export type SyncItem = {
|
export type SyncItem = {
|
||||||
@ -182,28 +247,13 @@ export type SyncItem = {
|
|||||||
[SyncEntityType.AlbumToAssetV1]: SyncAlbumToAssetV1;
|
[SyncEntityType.AlbumToAssetV1]: SyncAlbumToAssetV1;
|
||||||
[SyncEntityType.AlbumToAssetBackfillV1]: SyncAlbumToAssetV1;
|
[SyncEntityType.AlbumToAssetBackfillV1]: SyncAlbumToAssetV1;
|
||||||
[SyncEntityType.AlbumToAssetDeleteV1]: SyncAlbumToAssetDeleteV1;
|
[SyncEntityType.AlbumToAssetDeleteV1]: SyncAlbumToAssetDeleteV1;
|
||||||
|
[SyncEntityType.MemoryV1]: SyncMemoryV1;
|
||||||
|
[SyncEntityType.MemoryDeleteV1]: SyncMemoryDeleteV1;
|
||||||
|
[SyncEntityType.MemoryToAssetV1]: SyncMemoryAssetV1;
|
||||||
|
[SyncEntityType.MemoryToAssetDeleteV1]: SyncMemoryAssetDeleteV1;
|
||||||
[SyncEntityType.SyncAckV1]: SyncAckV1;
|
[SyncEntityType.SyncAckV1]: SyncAckV1;
|
||||||
};
|
};
|
||||||
|
|
||||||
const responseDtos = [
|
|
||||||
SyncUserV1,
|
|
||||||
SyncUserDeleteV1,
|
|
||||||
SyncPartnerV1,
|
|
||||||
SyncPartnerDeleteV1,
|
|
||||||
SyncAssetV1,
|
|
||||||
SyncAssetDeleteV1,
|
|
||||||
SyncAssetExifV1,
|
|
||||||
SyncAlbumV1,
|
|
||||||
SyncAlbumDeleteV1,
|
|
||||||
SyncAlbumUserV1,
|
|
||||||
SyncAlbumUserDeleteV1,
|
|
||||||
SyncAlbumToAssetV1,
|
|
||||||
SyncAlbumToAssetDeleteV1,
|
|
||||||
SyncAckV1,
|
|
||||||
];
|
|
||||||
|
|
||||||
export const extraSyncModels = responseDtos;
|
|
||||||
|
|
||||||
export class SyncStreamDto {
|
export class SyncStreamDto {
|
||||||
@IsEnum(SyncRequestType, { each: true })
|
@IsEnum(SyncRequestType, { each: true })
|
||||||
@ApiProperty({ enumName: 'SyncRequestType', enum: SyncRequestType, isArray: true })
|
@ApiProperty({ enumName: 'SyncRequestType', enum: SyncRequestType, isArray: true })
|
||||||
|
@ -584,19 +584,21 @@ export enum SyncRequestType {
|
|||||||
AlbumToAssetsV1 = 'AlbumToAssetsV1',
|
AlbumToAssetsV1 = 'AlbumToAssetsV1',
|
||||||
AlbumAssetsV1 = 'AlbumAssetsV1',
|
AlbumAssetsV1 = 'AlbumAssetsV1',
|
||||||
AlbumAssetExifsV1 = 'AlbumAssetExifsV1',
|
AlbumAssetExifsV1 = 'AlbumAssetExifsV1',
|
||||||
|
MemoriesV1 = 'MemoriesV1',
|
||||||
|
MemoryToAssetsV1 = 'MemoryToAssetsV1',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SyncEntityType {
|
export enum SyncEntityType {
|
||||||
UserV1 = 'UserV1',
|
UserV1 = 'UserV1',
|
||||||
UserDeleteV1 = 'UserDeleteV1',
|
UserDeleteV1 = 'UserDeleteV1',
|
||||||
|
|
||||||
PartnerV1 = 'PartnerV1',
|
|
||||||
PartnerDeleteV1 = 'PartnerDeleteV1',
|
|
||||||
|
|
||||||
AssetV1 = 'AssetV1',
|
AssetV1 = 'AssetV1',
|
||||||
AssetDeleteV1 = 'AssetDeleteV1',
|
AssetDeleteV1 = 'AssetDeleteV1',
|
||||||
AssetExifV1 = 'AssetExifV1',
|
AssetExifV1 = 'AssetExifV1',
|
||||||
|
|
||||||
|
PartnerV1 = 'PartnerV1',
|
||||||
|
PartnerDeleteV1 = 'PartnerDeleteV1',
|
||||||
|
|
||||||
PartnerAssetV1 = 'PartnerAssetV1',
|
PartnerAssetV1 = 'PartnerAssetV1',
|
||||||
PartnerAssetBackfillV1 = 'PartnerAssetBackfillV1',
|
PartnerAssetBackfillV1 = 'PartnerAssetBackfillV1',
|
||||||
PartnerAssetDeleteV1 = 'PartnerAssetDeleteV1',
|
PartnerAssetDeleteV1 = 'PartnerAssetDeleteV1',
|
||||||
@ -605,17 +607,26 @@ export enum SyncEntityType {
|
|||||||
|
|
||||||
AlbumV1 = 'AlbumV1',
|
AlbumV1 = 'AlbumV1',
|
||||||
AlbumDeleteV1 = 'AlbumDeleteV1',
|
AlbumDeleteV1 = 'AlbumDeleteV1',
|
||||||
|
|
||||||
AlbumUserV1 = 'AlbumUserV1',
|
AlbumUserV1 = 'AlbumUserV1',
|
||||||
AlbumUserBackfillV1 = 'AlbumUserBackfillV1',
|
AlbumUserBackfillV1 = 'AlbumUserBackfillV1',
|
||||||
AlbumUserDeleteV1 = 'AlbumUserDeleteV1',
|
AlbumUserDeleteV1 = 'AlbumUserDeleteV1',
|
||||||
|
|
||||||
AlbumAssetV1 = 'AlbumAssetV1',
|
AlbumAssetV1 = 'AlbumAssetV1',
|
||||||
AlbumAssetBackfillV1 = 'AlbumAssetBackfillV1',
|
AlbumAssetBackfillV1 = 'AlbumAssetBackfillV1',
|
||||||
AlbumAssetExifV1 = 'AlbumAssetExifV1',
|
AlbumAssetExifV1 = 'AlbumAssetExifV1',
|
||||||
AlbumAssetExifBackfillV1 = 'AlbumAssetExifBackfillV1',
|
AlbumAssetExifBackfillV1 = 'AlbumAssetExifBackfillV1',
|
||||||
|
|
||||||
AlbumToAssetV1 = 'AlbumToAssetV1',
|
AlbumToAssetV1 = 'AlbumToAssetV1',
|
||||||
AlbumToAssetDeleteV1 = 'AlbumToAssetDeleteV1',
|
AlbumToAssetDeleteV1 = 'AlbumToAssetDeleteV1',
|
||||||
AlbumToAssetBackfillV1 = 'AlbumToAssetBackfillV1',
|
AlbumToAssetBackfillV1 = 'AlbumToAssetBackfillV1',
|
||||||
|
|
||||||
|
MemoryV1 = 'MemoryV1',
|
||||||
|
MemoryDeleteV1 = 'MemoryDeleteV1',
|
||||||
|
|
||||||
|
MemoryToAssetV1 = 'MemoryToAssetV1',
|
||||||
|
MemoryToAssetDeleteV1 = 'MemoryToAssetDeleteV1',
|
||||||
|
|
||||||
SyncAckV1 = 'SyncAckV1',
|
SyncAckV1 = 'SyncAckV1',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -652,3 +652,78 @@ where
|
|||||||
)
|
)
|
||||||
order by
|
order by
|
||||||
"exif"."updateId" asc
|
"exif"."updateId" asc
|
||||||
|
|
||||||
|
-- SyncRepository.getMemoryUpserts
|
||||||
|
select
|
||||||
|
"id",
|
||||||
|
"createdAt",
|
||||||
|
"updatedAt",
|
||||||
|
"deletedAt",
|
||||||
|
"ownerId",
|
||||||
|
"type",
|
||||||
|
"data",
|
||||||
|
"isSaved",
|
||||||
|
"memoryAt",
|
||||||
|
"seenAt",
|
||||||
|
"showAt",
|
||||||
|
"hideAt",
|
||||||
|
"updateId"
|
||||||
|
from
|
||||||
|
"memories"
|
||||||
|
where
|
||||||
|
"ownerId" = $1
|
||||||
|
and "updatedAt" < now() - interval '1 millisecond'
|
||||||
|
order by
|
||||||
|
"updateId" asc
|
||||||
|
|
||||||
|
-- SyncRepository.getMemoryDeletes
|
||||||
|
select
|
||||||
|
"id",
|
||||||
|
"memoryId"
|
||||||
|
from
|
||||||
|
"memories_audit"
|
||||||
|
where
|
||||||
|
"userId" = $1
|
||||||
|
and "deletedAt" < now() - interval '1 millisecond'
|
||||||
|
order by
|
||||||
|
"id" asc
|
||||||
|
|
||||||
|
-- SyncRepository.getMemoryAssetUpserts
|
||||||
|
select
|
||||||
|
"memoriesId" as "memoryId",
|
||||||
|
"assetsId" as "assetId",
|
||||||
|
"updateId"
|
||||||
|
from
|
||||||
|
"memories_assets_assets"
|
||||||
|
where
|
||||||
|
"memoriesId" in (
|
||||||
|
select
|
||||||
|
"id"
|
||||||
|
from
|
||||||
|
"memories"
|
||||||
|
where
|
||||||
|
"ownerId" = $1
|
||||||
|
)
|
||||||
|
and "updatedAt" < now() - interval '1 millisecond'
|
||||||
|
order by
|
||||||
|
"updateId" asc
|
||||||
|
|
||||||
|
-- SyncRepository.getMemoryAssetDeletes
|
||||||
|
select
|
||||||
|
"id",
|
||||||
|
"memoryId",
|
||||||
|
"assetId"
|
||||||
|
from
|
||||||
|
"memory_assets_audit"
|
||||||
|
where
|
||||||
|
"memoryId" in (
|
||||||
|
select
|
||||||
|
"id"
|
||||||
|
from
|
||||||
|
"memories"
|
||||||
|
where
|
||||||
|
"ownerId" = $1
|
||||||
|
)
|
||||||
|
and "deletedAt" < now() - interval '1 millisecond'
|
||||||
|
order by
|
||||||
|
"id" asc
|
||||||
|
@ -13,8 +13,18 @@ type AuditTables =
|
|||||||
| 'assets_audit'
|
| 'assets_audit'
|
||||||
| 'albums_audit'
|
| 'albums_audit'
|
||||||
| 'album_users_audit'
|
| 'album_users_audit'
|
||||||
| 'album_assets_audit';
|
| 'album_assets_audit'
|
||||||
type UpsertTables = 'users' | 'partners' | 'assets' | 'exif' | 'albums' | 'albums_shared_users_users';
|
| 'memories_audit'
|
||||||
|
| 'memory_assets_audit';
|
||||||
|
type UpsertTables =
|
||||||
|
| 'users'
|
||||||
|
| 'partners'
|
||||||
|
| 'assets'
|
||||||
|
| 'exif'
|
||||||
|
| 'albums'
|
||||||
|
| 'albums_shared_users_users'
|
||||||
|
| 'memories'
|
||||||
|
| 'memories_assets_assets';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SyncRepository {
|
export class SyncRepository {
|
||||||
@ -438,6 +448,61 @@ export class SyncRepository {
|
|||||||
.stream();
|
.stream();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||||
|
getMemoryUpserts(userId: string, ack?: SyncAck) {
|
||||||
|
return this.db
|
||||||
|
.selectFrom('memories')
|
||||||
|
.select([
|
||||||
|
'id',
|
||||||
|
'createdAt',
|
||||||
|
'updatedAt',
|
||||||
|
'deletedAt',
|
||||||
|
'ownerId',
|
||||||
|
'type',
|
||||||
|
'data',
|
||||||
|
'isSaved',
|
||||||
|
'memoryAt',
|
||||||
|
'seenAt',
|
||||||
|
'showAt',
|
||||||
|
'hideAt',
|
||||||
|
])
|
||||||
|
.select('updateId')
|
||||||
|
.where('ownerId', '=', userId)
|
||||||
|
.$call((qb) => this.upsertTableFilters(qb, ack))
|
||||||
|
.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||||
|
getMemoryDeletes(userId: string, ack?: SyncAck) {
|
||||||
|
return this.db
|
||||||
|
.selectFrom('memories_audit')
|
||||||
|
.select(['id', 'memoryId'])
|
||||||
|
.where('userId', '=', userId)
|
||||||
|
.$call((qb) => this.auditTableFilters(qb, ack))
|
||||||
|
.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||||
|
getMemoryAssetUpserts(userId: string, ack?: SyncAck) {
|
||||||
|
return this.db
|
||||||
|
.selectFrom('memories_assets_assets')
|
||||||
|
.select(['memoriesId as memoryId', 'assetsId as assetId'])
|
||||||
|
.select('updateId')
|
||||||
|
.where('memoriesId', 'in', (eb) => eb.selectFrom('memories').select('id').where('ownerId', '=', userId))
|
||||||
|
.$call((qb) => this.upsertTableFilters(qb, ack))
|
||||||
|
.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||||
|
getMemoryAssetDeletes(userId: string, ack?: SyncAck) {
|
||||||
|
return this.db
|
||||||
|
.selectFrom('memory_assets_audit')
|
||||||
|
.select(['id', 'memoryId', 'assetId'])
|
||||||
|
.where('memoryId', 'in', (eb) => eb.selectFrom('memories').select('id').where('ownerId', '=', userId))
|
||||||
|
.$call((qb) => this.auditTableFilters(qb, ack))
|
||||||
|
.stream();
|
||||||
|
}
|
||||||
|
|
||||||
private auditTableFilters<T extends keyof Pick<DB, AuditTables>, D>(qb: SelectQueryBuilder<DB, T, D>, ack?: SyncAck) {
|
private auditTableFilters<T extends keyof Pick<DB, AuditTables>, D>(qb: SelectQueryBuilder<DB, T, D>, ack?: SyncAck) {
|
||||||
const builder = qb as SelectQueryBuilder<DB, AuditTables, D>;
|
const builder = qb as SelectQueryBuilder<DB, AuditTables, D>;
|
||||||
return builder
|
return builder
|
||||||
|
@ -176,3 +176,31 @@ export const album_users_delete_audit = registerFunction({
|
|||||||
END`,
|
END`,
|
||||||
synchronize: false,
|
synchronize: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
export const memories_delete_audit = registerFunction({
|
||||||
|
name: 'memories_delete_audit',
|
||||||
|
returnType: 'TRIGGER',
|
||||||
|
language: 'PLPGSQL',
|
||||||
|
body: `
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO memories_audit ("memoryId", "userId")
|
||||||
|
SELECT "id", "ownerId"
|
||||||
|
FROM OLD;
|
||||||
|
RETURN NULL;
|
||||||
|
END`,
|
||||||
|
synchronize: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const memory_assets_delete_audit = registerFunction({
|
||||||
|
name: 'memory_assets_delete_audit',
|
||||||
|
returnType: 'TRIGGER',
|
||||||
|
language: 'PLPGSQL',
|
||||||
|
body: `
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO memory_assets_audit ("memoryId", "assetId")
|
||||||
|
SELECT "memoriesId", "assetsId" FROM OLD
|
||||||
|
WHERE "memoriesId" IN (SELECT "id" FROM memories WHERE "id" IN (SELECT "memoriesId" FROM OLD));
|
||||||
|
RETURN NULL;
|
||||||
|
END`,
|
||||||
|
synchronize: false,
|
||||||
|
});
|
||||||
|
@ -8,6 +8,8 @@ import {
|
|||||||
f_unaccent,
|
f_unaccent,
|
||||||
immich_uuid_v7,
|
immich_uuid_v7,
|
||||||
ll_to_earth_public,
|
ll_to_earth_public,
|
||||||
|
memories_delete_audit,
|
||||||
|
memory_assets_delete_audit,
|
||||||
partners_delete_audit,
|
partners_delete_audit,
|
||||||
updated_at,
|
updated_at,
|
||||||
users_delete_audit,
|
users_delete_audit,
|
||||||
@ -30,8 +32,10 @@ import { ExifTable } from 'src/schema/tables/exif.table';
|
|||||||
import { FaceSearchTable } from 'src/schema/tables/face-search.table';
|
import { FaceSearchTable } from 'src/schema/tables/face-search.table';
|
||||||
import { GeodataPlacesTable } from 'src/schema/tables/geodata-places.table';
|
import { GeodataPlacesTable } from 'src/schema/tables/geodata-places.table';
|
||||||
import { LibraryTable } from 'src/schema/tables/library.table';
|
import { LibraryTable } from 'src/schema/tables/library.table';
|
||||||
|
import { MemoryAssetAuditTable } from 'src/schema/tables/memory-asset-audit.table';
|
||||||
|
import { MemoryAssetTable } from 'src/schema/tables/memory-asset.table';
|
||||||
|
import { MemoryAuditTable } from 'src/schema/tables/memory-audit.table';
|
||||||
import { MemoryTable } from 'src/schema/tables/memory.table';
|
import { MemoryTable } from 'src/schema/tables/memory.table';
|
||||||
import { MemoryAssetTable } from 'src/schema/tables/memory_asset.table';
|
|
||||||
import { MoveTable } from 'src/schema/tables/move.table';
|
import { MoveTable } from 'src/schema/tables/move.table';
|
||||||
import { NaturalEarthCountriesTable } from 'src/schema/tables/natural-earth-countries.table';
|
import { NaturalEarthCountriesTable } from 'src/schema/tables/natural-earth-countries.table';
|
||||||
import { NotificationTable } from 'src/schema/tables/notification.table';
|
import { NotificationTable } from 'src/schema/tables/notification.table';
|
||||||
@ -75,8 +79,10 @@ export class ImmichDatabase {
|
|||||||
FaceSearchTable,
|
FaceSearchTable,
|
||||||
GeodataPlacesTable,
|
GeodataPlacesTable,
|
||||||
LibraryTable,
|
LibraryTable,
|
||||||
MemoryAssetTable,
|
|
||||||
MemoryTable,
|
MemoryTable,
|
||||||
|
MemoryAuditTable,
|
||||||
|
MemoryAssetTable,
|
||||||
|
MemoryAssetAuditTable,
|
||||||
MoveTable,
|
MoveTable,
|
||||||
NaturalEarthCountriesTable,
|
NaturalEarthCountriesTable,
|
||||||
NotificationTable,
|
NotificationTable,
|
||||||
@ -110,6 +116,8 @@ export class ImmichDatabase {
|
|||||||
albums_delete_audit,
|
albums_delete_audit,
|
||||||
album_user_after_insert,
|
album_user_after_insert,
|
||||||
album_users_delete_audit,
|
album_users_delete_audit,
|
||||||
|
memories_delete_audit,
|
||||||
|
memory_assets_delete_audit,
|
||||||
];
|
];
|
||||||
|
|
||||||
enum = [assets_status_enum, asset_face_source_type, asset_visibility_enum];
|
enum = [assets_status_enum, asset_face_source_type, asset_visibility_enum];
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
import { Kysely, sql } from 'kysely';
|
||||||
|
|
||||||
|
export async function up(db: Kysely<any>): Promise<void> {
|
||||||
|
await sql`CREATE TABLE "memory_assets_audit" ("id" uuid NOT NULL DEFAULT immich_uuid_v7(), "memoryId" uuid NOT NULL, "assetId" uuid NOT NULL, "deletedAt" timestamp with time zone NOT NULL DEFAULT clock_timestamp());`.execute(db);
|
||||||
|
await sql`CREATE TABLE "memories_audit" ("id" uuid NOT NULL DEFAULT immich_uuid_v7(), "memoryId" uuid NOT NULL, "userId" uuid NOT NULL, "deletedAt" timestamp with time zone NOT NULL DEFAULT clock_timestamp());`.execute(db);
|
||||||
|
await sql`ALTER TABLE "memories_assets_assets" ADD "createdAt" timestamp with time zone NOT NULL DEFAULT now();`.execute(db);
|
||||||
|
await sql`ALTER TABLE "memories_assets_assets" ADD "updatedAt" timestamp with time zone NOT NULL DEFAULT now();`.execute(db);
|
||||||
|
await sql`ALTER TABLE "memories_assets_assets" ADD "updateId" uuid NOT NULL DEFAULT immich_uuid_v7();`.execute(db);
|
||||||
|
await sql`ALTER TABLE "memory_assets_audit" ADD CONSTRAINT "PK_35ef16910228f980e0766dcc59b" PRIMARY KEY ("id");`.execute(db);
|
||||||
|
await sql`ALTER TABLE "memories_audit" ADD CONSTRAINT "PK_19de798c033a710dcfa5c72f81b" PRIMARY KEY ("id");`.execute(db);
|
||||||
|
await sql`ALTER TABLE "memory_assets_audit" ADD CONSTRAINT "FK_225a204afcb0bd6de015080fb03" FOREIGN KEY ("memoryId") REFERENCES "memories" ("id") ON UPDATE CASCADE ON DELETE CASCADE;`.execute(db);
|
||||||
|
await sql`CREATE INDEX "IDX_memory_assets_audit_memory_id" ON "memory_assets_audit" ("memoryId")`.execute(db);
|
||||||
|
await sql`CREATE INDEX "IDX_memory_assets_audit_asset_id" ON "memory_assets_audit" ("assetId")`.execute(db);
|
||||||
|
await sql`CREATE INDEX "IDX_memory_assets_audit_deleted_at" ON "memory_assets_audit" ("deletedAt")`.execute(db);
|
||||||
|
await sql`CREATE INDEX "IDX_memory_assets_update_id" ON "memories_assets_assets" ("updateId")`.execute(db);
|
||||||
|
await sql`CREATE INDEX "IDX_memories_audit_memory_id" ON "memories_audit" ("memoryId")`.execute(db);
|
||||||
|
await sql`CREATE INDEX "IDX_memories_audit_user_id" ON "memories_audit" ("userId")`.execute(db);
|
||||||
|
await sql`CREATE INDEX "IDX_memories_audit_deleted_at" ON "memories_audit" ("deletedAt")`.execute(db);
|
||||||
|
await sql`CREATE OR REPLACE FUNCTION memories_delete_audit()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO memories_audit ("memoryId", "userId")
|
||||||
|
SELECT "id", "ownerId"
|
||||||
|
FROM OLD;
|
||||||
|
RETURN NULL;
|
||||||
|
END
|
||||||
|
$$;`.execute(db);
|
||||||
|
await sql`CREATE OR REPLACE FUNCTION memory_assets_delete_audit()
|
||||||
|
RETURNS TRIGGER
|
||||||
|
LANGUAGE PLPGSQL
|
||||||
|
AS $$
|
||||||
|
BEGIN
|
||||||
|
INSERT INTO memory_assets_audit ("memoryId", "assetId")
|
||||||
|
SELECT "memoriesId", "assetsId" FROM OLD
|
||||||
|
WHERE "memoriesId" IN (SELECT "id" FROM memories WHERE "id" IN (SELECT "memoriesId" FROM OLD));
|
||||||
|
RETURN NULL;
|
||||||
|
END
|
||||||
|
$$;`.execute(db);
|
||||||
|
await sql`CREATE OR REPLACE TRIGGER "memories_delete_audit"
|
||||||
|
AFTER DELETE ON "memories"
|
||||||
|
REFERENCING OLD TABLE AS "old"
|
||||||
|
FOR EACH STATEMENT
|
||||||
|
WHEN (pg_trigger_depth() = 0)
|
||||||
|
EXECUTE FUNCTION memories_delete_audit();`.execute(db);
|
||||||
|
await sql`CREATE OR REPLACE TRIGGER "memory_assets_delete_audit"
|
||||||
|
AFTER DELETE ON "memories_assets_assets"
|
||||||
|
REFERENCING OLD TABLE AS "old"
|
||||||
|
FOR EACH STATEMENT
|
||||||
|
WHEN (pg_trigger_depth() <= 1)
|
||||||
|
EXECUTE FUNCTION memory_assets_delete_audit();`.execute(db);
|
||||||
|
await sql`CREATE OR REPLACE TRIGGER "memory_assets_updated_at"
|
||||||
|
BEFORE UPDATE ON "memories_assets_assets"
|
||||||
|
FOR EACH ROW
|
||||||
|
EXECUTE FUNCTION updated_at();`.execute(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function down(db: Kysely<any>): Promise<void> {
|
||||||
|
await sql`DROP TRIGGER "memories_delete_audit" ON "memories";`.execute(db);
|
||||||
|
await sql`DROP TRIGGER "memory_assets_delete_audit" ON "memories_assets_assets";`.execute(db);
|
||||||
|
await sql`DROP TRIGGER "memory_assets_updated_at" ON "memories_assets_assets";`.execute(db);
|
||||||
|
await sql`DROP INDEX "IDX_memory_assets_update_id";`.execute(db);
|
||||||
|
await sql`DROP INDEX "IDX_memory_assets_audit_memory_id";`.execute(db);
|
||||||
|
await sql`DROP INDEX "IDX_memory_assets_audit_asset_id";`.execute(db);
|
||||||
|
await sql`DROP INDEX "IDX_memory_assets_audit_deleted_at";`.execute(db);
|
||||||
|
await sql`DROP INDEX "IDX_memories_audit_memory_id";`.execute(db);
|
||||||
|
await sql`DROP INDEX "IDX_memories_audit_user_id";`.execute(db);
|
||||||
|
await sql`DROP INDEX "IDX_memories_audit_deleted_at";`.execute(db);
|
||||||
|
await sql`ALTER TABLE "memory_assets_audit" DROP CONSTRAINT "FK_225a204afcb0bd6de015080fb03";`.execute(db);
|
||||||
|
await sql`ALTER TABLE "memory_assets_audit" DROP CONSTRAINT "PK_35ef16910228f980e0766dcc59b";`.execute(db);
|
||||||
|
await sql`ALTER TABLE "memories_audit" DROP CONSTRAINT "PK_19de798c033a710dcfa5c72f81b";`.execute(db);
|
||||||
|
await sql`ALTER TABLE "memories_assets_assets" DROP COLUMN "createdAt";`.execute(db);
|
||||||
|
await sql`ALTER TABLE "memories_assets_assets" DROP COLUMN "updatedAt";`.execute(db);
|
||||||
|
await sql`ALTER TABLE "memories_assets_assets" DROP COLUMN "updateId";`.execute(db);
|
||||||
|
await sql`DROP TABLE "memory_assets_audit";`.execute(db);
|
||||||
|
await sql`DROP TABLE "memories_audit";`.execute(db);
|
||||||
|
await sql`DROP FUNCTION memories_delete_audit;`.execute(db);
|
||||||
|
await sql`DROP FUNCTION memory_assets_delete_audit;`.execute(db);
|
||||||
|
}
|
@ -13,8 +13,8 @@ import 'src/schema/tables/exif.table';
|
|||||||
import 'src/schema/tables/face-search.table';
|
import 'src/schema/tables/face-search.table';
|
||||||
import 'src/schema/tables/geodata-places.table';
|
import 'src/schema/tables/geodata-places.table';
|
||||||
import 'src/schema/tables/library.table';
|
import 'src/schema/tables/library.table';
|
||||||
|
import 'src/schema/tables/memory-asset.table';
|
||||||
import 'src/schema/tables/memory.table';
|
import 'src/schema/tables/memory.table';
|
||||||
import 'src/schema/tables/memory_asset.table';
|
|
||||||
import 'src/schema/tables/move.table';
|
import 'src/schema/tables/move.table';
|
||||||
import 'src/schema/tables/natural-earth-countries.table';
|
import 'src/schema/tables/natural-earth-countries.table';
|
||||||
import 'src/schema/tables/partner-audit.table';
|
import 'src/schema/tables/partner-audit.table';
|
||||||
|
23
server/src/schema/tables/memory-asset-audit.table.ts
Normal file
23
server/src/schema/tables/memory-asset-audit.table.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
|
||||||
|
import { MemoryTable } from 'src/schema/tables/memory.table';
|
||||||
|
import { Column, CreateDateColumn, ForeignKeyColumn, Table } from 'src/sql-tools';
|
||||||
|
|
||||||
|
@Table('memory_assets_audit')
|
||||||
|
export class MemoryAssetAuditTable {
|
||||||
|
@PrimaryGeneratedUuidV7Column()
|
||||||
|
id!: string;
|
||||||
|
|
||||||
|
@ForeignKeyColumn(() => MemoryTable, {
|
||||||
|
type: 'uuid',
|
||||||
|
indexName: 'IDX_memory_assets_audit_memory_id',
|
||||||
|
onDelete: 'CASCADE',
|
||||||
|
onUpdate: 'CASCADE',
|
||||||
|
})
|
||||||
|
memoryId!: string;
|
||||||
|
|
||||||
|
@Column({ type: 'uuid', indexName: 'IDX_memory_assets_audit_asset_id' })
|
||||||
|
assetId!: string;
|
||||||
|
|
||||||
|
@CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_memory_assets_audit_deleted_at' })
|
||||||
|
deletedAt!: Date;
|
||||||
|
}
|
36
server/src/schema/tables/memory-asset.table.ts
Normal file
36
server/src/schema/tables/memory-asset.table.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { ColumnType } from 'kysely';
|
||||||
|
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||||
|
import { memory_assets_delete_audit } from 'src/schema/functions';
|
||||||
|
import { AssetTable } from 'src/schema/tables/asset.table';
|
||||||
|
import { MemoryTable } from 'src/schema/tables/memory.table';
|
||||||
|
import { AfterDeleteTrigger, CreateDateColumn, ForeignKeyColumn, Table, UpdateDateColumn } from 'src/sql-tools';
|
||||||
|
|
||||||
|
type Timestamp = ColumnType<Date, Date | string, Date | string>;
|
||||||
|
type Generated<T> =
|
||||||
|
T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S, I | undefined, U> : ColumnType<T, T | undefined, T>;
|
||||||
|
|
||||||
|
@Table('memories_assets_assets')
|
||||||
|
@UpdatedAtTrigger('memory_assets_updated_at')
|
||||||
|
@AfterDeleteTrigger({
|
||||||
|
name: 'memory_assets_delete_audit',
|
||||||
|
scope: 'statement',
|
||||||
|
function: memory_assets_delete_audit,
|
||||||
|
referencingOldTableAs: 'old',
|
||||||
|
when: 'pg_trigger_depth() <= 1',
|
||||||
|
})
|
||||||
|
export class MemoryAssetTable {
|
||||||
|
@ForeignKeyColumn(() => MemoryTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE', primary: true })
|
||||||
|
memoriesId!: string;
|
||||||
|
|
||||||
|
@ForeignKeyColumn(() => AssetTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE', primary: true })
|
||||||
|
assetsId!: string;
|
||||||
|
|
||||||
|
@CreateDateColumn()
|
||||||
|
createdAt!: Generated<Timestamp>;
|
||||||
|
|
||||||
|
@UpdateDateColumn()
|
||||||
|
updatedAt!: Generated<Timestamp>;
|
||||||
|
|
||||||
|
@UpdateIdColumn({ indexName: 'IDX_memory_assets_update_id' })
|
||||||
|
updateId!: Generated<string>;
|
||||||
|
}
|
22
server/src/schema/tables/memory-audit.table.ts
Normal file
22
server/src/schema/tables/memory-audit.table.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { ColumnType } from 'kysely';
|
||||||
|
import { PrimaryGeneratedUuidV7Column } from 'src/decorators';
|
||||||
|
import { Column, CreateDateColumn, Table } from 'src/sql-tools';
|
||||||
|
|
||||||
|
type Timestamp = ColumnType<Date, Date | string, Date | string>;
|
||||||
|
type Generated<T> =
|
||||||
|
T extends ColumnType<infer S, infer I, infer U> ? ColumnType<S, I | undefined, U> : ColumnType<T, T | undefined, T>;
|
||||||
|
|
||||||
|
@Table('memories_audit')
|
||||||
|
export class MemoryAuditTable {
|
||||||
|
@PrimaryGeneratedUuidV7Column()
|
||||||
|
id!: Generated<string>;
|
||||||
|
|
||||||
|
@Column({ type: 'uuid', indexName: 'IDX_memories_audit_memory_id' })
|
||||||
|
memoryId!: string;
|
||||||
|
|
||||||
|
@Column({ type: 'uuid', indexName: 'IDX_memories_audit_user_id' })
|
||||||
|
userId!: string;
|
||||||
|
|
||||||
|
@CreateDateColumn({ default: () => 'clock_timestamp()', indexName: 'IDX_memories_audit_deleted_at' })
|
||||||
|
deletedAt!: Timestamp;
|
||||||
|
}
|
@ -1,7 +1,9 @@
|
|||||||
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
import { UpdatedAtTrigger, UpdateIdColumn } from 'src/decorators';
|
||||||
import { MemoryType } from 'src/enum';
|
import { MemoryType } from 'src/enum';
|
||||||
|
import { memories_delete_audit } from 'src/schema/functions';
|
||||||
import { UserTable } from 'src/schema/tables/user.table';
|
import { UserTable } from 'src/schema/tables/user.table';
|
||||||
import {
|
import {
|
||||||
|
AfterDeleteTrigger,
|
||||||
Column,
|
Column,
|
||||||
CreateDateColumn,
|
CreateDateColumn,
|
||||||
DeleteDateColumn,
|
DeleteDateColumn,
|
||||||
@ -10,11 +12,17 @@ import {
|
|||||||
Table,
|
Table,
|
||||||
UpdateDateColumn,
|
UpdateDateColumn,
|
||||||
} from 'src/sql-tools';
|
} from 'src/sql-tools';
|
||||||
import { MemoryData } from 'src/types';
|
|
||||||
|
|
||||||
@Table('memories')
|
@Table('memories')
|
||||||
@UpdatedAtTrigger('memories_updated_at')
|
@UpdatedAtTrigger('memories_updated_at')
|
||||||
export class MemoryTable<T extends MemoryType = MemoryType> {
|
@AfterDeleteTrigger({
|
||||||
|
name: 'memories_delete_audit',
|
||||||
|
scope: 'statement',
|
||||||
|
function: memories_delete_audit,
|
||||||
|
referencingOldTableAs: 'old',
|
||||||
|
when: 'pg_trigger_depth() = 0',
|
||||||
|
})
|
||||||
|
export class MemoryTable {
|
||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
id!: string;
|
id!: string;
|
||||||
|
|
||||||
@ -31,10 +39,10 @@ export class MemoryTable<T extends MemoryType = MemoryType> {
|
|||||||
ownerId!: string;
|
ownerId!: string;
|
||||||
|
|
||||||
@Column()
|
@Column()
|
||||||
type!: T;
|
type!: MemoryType;
|
||||||
|
|
||||||
@Column({ type: 'jsonb' })
|
@Column({ type: 'jsonb' })
|
||||||
data!: MemoryData[T];
|
data!: object;
|
||||||
|
|
||||||
/** unless set to true, will be automatically deleted in the future */
|
/** unless set to true, will be automatically deleted in the future */
|
||||||
@Column({ type: 'boolean', default: false })
|
@Column({ type: 'boolean', default: false })
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
import { AssetTable } from 'src/schema/tables/asset.table';
|
|
||||||
import { MemoryTable } from 'src/schema/tables/memory.table';
|
|
||||||
import { ForeignKeyColumn, Table } from 'src/sql-tools';
|
|
||||||
|
|
||||||
@Table('memories_assets_assets')
|
|
||||||
export class MemoryAssetTable {
|
|
||||||
@ForeignKeyColumn(() => MemoryTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE', primary: true })
|
|
||||||
memoriesId!: string;
|
|
||||||
|
|
||||||
@ForeignKeyColumn(() => AssetTable, { onUpdate: 'CASCADE', onDelete: 'CASCADE', primary: true })
|
|
||||||
assetsId!: string;
|
|
||||||
}
|
|
@ -1,5 +1,6 @@
|
|||||||
import { BadRequestException } from '@nestjs/common';
|
import { BadRequestException } from '@nestjs/common';
|
||||||
import { MemoryService } from 'src/services/memory.service';
|
import { MemoryService } from 'src/services/memory.service';
|
||||||
|
import { OnThisDayData } from 'src/types';
|
||||||
import { factory, newUuid, newUuids } from 'test/small.factory';
|
import { factory, newUuid, newUuids } from 'test/small.factory';
|
||||||
import { newTestService, ServiceMocks } from 'test/utils';
|
import { newTestService, ServiceMocks } from 'test/utils';
|
||||||
|
|
||||||
@ -87,7 +88,7 @@ describe(MemoryService.name, () => {
|
|||||||
await expect(
|
await expect(
|
||||||
sut.create(factory.auth({ user: { id: userId } }), {
|
sut.create(factory.auth({ user: { id: userId } }), {
|
||||||
type: memory.type,
|
type: memory.type,
|
||||||
data: memory.data,
|
data: memory.data as OnThisDayData,
|
||||||
memoryAt: memory.memoryAt,
|
memoryAt: memory.memoryAt,
|
||||||
isSaved: memory.isSaved,
|
isSaved: memory.isSaved,
|
||||||
assetIds: [assetId],
|
assetIds: [assetId],
|
||||||
@ -117,7 +118,7 @@ describe(MemoryService.name, () => {
|
|||||||
await expect(
|
await expect(
|
||||||
sut.create(factory.auth({ user: { id: userId } }), {
|
sut.create(factory.auth({ user: { id: userId } }), {
|
||||||
type: memory.type,
|
type: memory.type,
|
||||||
data: memory.data,
|
data: memory.data as OnThisDayData,
|
||||||
assetIds: memory.assets.map((asset) => asset.id),
|
assetIds: memory.assets.map((asset) => asset.id),
|
||||||
memoryAt: memory.memoryAt,
|
memoryAt: memory.memoryAt,
|
||||||
}),
|
}),
|
||||||
@ -135,7 +136,11 @@ describe(MemoryService.name, () => {
|
|||||||
mocks.memory.create.mockResolvedValue(memory);
|
mocks.memory.create.mockResolvedValue(memory);
|
||||||
|
|
||||||
await expect(
|
await expect(
|
||||||
sut.create(factory.auth(), { type: memory.type, data: memory.data, memoryAt: memory.memoryAt }),
|
sut.create(factory.auth(), {
|
||||||
|
type: memory.type,
|
||||||
|
data: memory.data as OnThisDayData,
|
||||||
|
memoryAt: memory.memoryAt,
|
||||||
|
}),
|
||||||
).resolves.toBeDefined();
|
).resolves.toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -65,6 +65,8 @@ export const SYNC_TYPES_ORDER = [
|
|||||||
SyncRequestType.AssetExifsV1,
|
SyncRequestType.AssetExifsV1,
|
||||||
SyncRequestType.AlbumAssetExifsV1,
|
SyncRequestType.AlbumAssetExifsV1,
|
||||||
SyncRequestType.PartnerAssetExifsV1,
|
SyncRequestType.PartnerAssetExifsV1,
|
||||||
|
SyncRequestType.MemoriesV1,
|
||||||
|
SyncRequestType.MemoryToAssetsV1,
|
||||||
];
|
];
|
||||||
|
|
||||||
const throwSessionRequired = () => {
|
const throwSessionRequired = () => {
|
||||||
@ -120,108 +122,70 @@ export class SyncService extends BaseService {
|
|||||||
|
|
||||||
const checkpoints = await this.syncRepository.getCheckpoints(sessionId);
|
const checkpoints = await this.syncRepository.getCheckpoints(sessionId);
|
||||||
const checkpointMap: CheckpointMap = Object.fromEntries(checkpoints.map(({ type, ack }) => [type, fromAck(ack)]));
|
const checkpointMap: CheckpointMap = Object.fromEntries(checkpoints.map(({ type, ack }) => [type, fromAck(ack)]));
|
||||||
|
const handlers: Record<SyncRequestType, () => Promise<void>> = {
|
||||||
|
[SyncRequestType.UsersV1]: () => this.syncUsersV1(response, checkpointMap),
|
||||||
|
[SyncRequestType.PartnersV1]: () => this.syncPartnersV1(response, checkpointMap, auth),
|
||||||
|
[SyncRequestType.AssetsV1]: () => this.syncAssetsV1(response, checkpointMap, auth),
|
||||||
|
[SyncRequestType.AssetExifsV1]: () => this.syncAssetExifsV1(response, checkpointMap, auth),
|
||||||
|
[SyncRequestType.PartnerAssetsV1]: () => this.syncPartnerAssetsV1(response, checkpointMap, auth, sessionId),
|
||||||
|
[SyncRequestType.PartnerAssetExifsV1]: () =>
|
||||||
|
this.syncPartnerAssetExifsV1(response, checkpointMap, auth, sessionId),
|
||||||
|
[SyncRequestType.AlbumsV1]: () => this.syncAlbumsV1(response, checkpointMap, auth),
|
||||||
|
[SyncRequestType.AlbumUsersV1]: () => this.syncAlbumUsersV1(response, checkpointMap, auth, sessionId),
|
||||||
|
[SyncRequestType.AlbumAssetsV1]: () => this.syncAlbumAssetsV1(response, checkpointMap, auth, sessionId),
|
||||||
|
[SyncRequestType.AlbumToAssetsV1]: () => this.syncAlbumToAssetsV1(response, checkpointMap, auth, sessionId),
|
||||||
|
[SyncRequestType.AlbumAssetExifsV1]: () => this.syncAlbumAssetExifsV1(response, checkpointMap, auth, sessionId),
|
||||||
|
[SyncRequestType.MemoriesV1]: () => this.syncMemoriesV1(response, checkpointMap, auth),
|
||||||
|
[SyncRequestType.MemoryToAssetsV1]: () => this.syncMemoryAssetsV1(response, checkpointMap, auth),
|
||||||
|
};
|
||||||
|
|
||||||
for (const type of SYNC_TYPES_ORDER.filter((type) => dto.types.includes(type))) {
|
for (const type of SYNC_TYPES_ORDER.filter((type) => dto.types.includes(type))) {
|
||||||
switch (type) {
|
const handler = handlers[type];
|
||||||
case SyncRequestType.UsersV1: {
|
await handler();
|
||||||
await this.syncUsersV1(response, checkpointMap);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SyncRequestType.PartnersV1: {
|
|
||||||
await this.syncPartnersV1(response, checkpointMap, auth);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SyncRequestType.AssetsV1: {
|
|
||||||
await this.syncAssetsV1(response, checkpointMap, auth);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SyncRequestType.AssetExifsV1: {
|
|
||||||
await this.syncAssetExifsV1(response, checkpointMap, auth);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SyncRequestType.PartnerAssetsV1: {
|
|
||||||
await this.syncPartnerAssetsV1(response, checkpointMap, auth, sessionId);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SyncRequestType.PartnerAssetExifsV1: {
|
|
||||||
await this.syncPartnerAssetExifsV1(response, checkpointMap, auth, sessionId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SyncRequestType.AlbumsV1: {
|
|
||||||
await this.syncAlbumsV1(response, checkpointMap, auth);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SyncRequestType.AlbumUsersV1: {
|
|
||||||
await this.syncAlbumUsersV1(response, checkpointMap, auth, sessionId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SyncRequestType.AlbumAssetsV1: {
|
|
||||||
await this.syncAlbumAssetsV1(response, checkpointMap, auth, sessionId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SyncRequestType.AlbumToAssetsV1: {
|
|
||||||
await this.syncAlbumToAssetsV1(response, checkpointMap, auth, sessionId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case SyncRequestType.AlbumAssetExifsV1: {
|
|
||||||
await this.syncAlbumAssetExifsV1(response, checkpointMap, auth, sessionId);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
default: {
|
|
||||||
this.logger.warn(`Unsupported sync type: ${type}`);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
response.end();
|
response.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async syncUsersV1(response: Writable, checkpointMap: CheckpointMap) {
|
private async syncUsersV1(response: Writable, checkpointMap: CheckpointMap) {
|
||||||
const deletes = this.syncRepository.getUserDeletes(checkpointMap[SyncEntityType.UserDeleteV1]);
|
const deleteType = SyncEntityType.UserDeleteV1;
|
||||||
|
const deletes = this.syncRepository.getUserDeletes(checkpointMap[deleteType]);
|
||||||
for await (const { id, ...data } of deletes) {
|
for await (const { id, ...data } of deletes) {
|
||||||
send(response, { type: SyncEntityType.UserDeleteV1, ids: [id], data });
|
send(response, { type: deleteType, ids: [id], data });
|
||||||
}
|
}
|
||||||
|
|
||||||
const upserts = this.syncRepository.getUserUpserts(checkpointMap[SyncEntityType.UserV1]);
|
const upsertType = SyncEntityType.UserV1;
|
||||||
|
const upserts = this.syncRepository.getUserUpserts(checkpointMap[upsertType]);
|
||||||
for await (const { updateId, ...data } of upserts) {
|
for await (const { updateId, ...data } of upserts) {
|
||||||
send(response, { type: SyncEntityType.UserV1, ids: [updateId], data });
|
send(response, { type: upsertType, ids: [updateId], data });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async syncPartnersV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) {
|
private async syncPartnersV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) {
|
||||||
const deletes = this.syncRepository.getPartnerDeletes(auth.user.id, checkpointMap[SyncEntityType.PartnerDeleteV1]);
|
const deleteType = SyncEntityType.PartnerDeleteV1;
|
||||||
|
const deletes = this.syncRepository.getPartnerDeletes(auth.user.id, checkpointMap[deleteType]);
|
||||||
for await (const { id, ...data } of deletes) {
|
for await (const { id, ...data } of deletes) {
|
||||||
send(response, { type: SyncEntityType.PartnerDeleteV1, ids: [id], data });
|
send(response, { type: deleteType, ids: [id], data });
|
||||||
}
|
}
|
||||||
|
|
||||||
const upserts = this.syncRepository.getPartnerUpserts(auth.user.id, checkpointMap[SyncEntityType.PartnerV1]);
|
const upsertType = SyncEntityType.PartnerV1;
|
||||||
|
const upserts = this.syncRepository.getPartnerUpserts(auth.user.id, checkpointMap[upsertType]);
|
||||||
for await (const { updateId, ...data } of upserts) {
|
for await (const { updateId, ...data } of upserts) {
|
||||||
send(response, { type: SyncEntityType.PartnerV1, ids: [updateId], data });
|
send(response, { type: upsertType, ids: [updateId], data });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async syncAssetsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) {
|
private async syncAssetsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) {
|
||||||
const deletes = this.syncRepository.getAssetDeletes(auth.user.id, checkpointMap[SyncEntityType.AssetDeleteV1]);
|
const deleteType = SyncEntityType.AssetDeleteV1;
|
||||||
|
const deletes = this.syncRepository.getAssetDeletes(auth.user.id, checkpointMap[deleteType]);
|
||||||
for await (const { id, ...data } of deletes) {
|
for await (const { id, ...data } of deletes) {
|
||||||
send(response, { type: SyncEntityType.AssetDeleteV1, ids: [id], data });
|
send(response, { type: deleteType, ids: [id], data });
|
||||||
}
|
}
|
||||||
|
|
||||||
const upserts = this.syncRepository.getAssetUpserts(auth.user.id, checkpointMap[SyncEntityType.AssetV1]);
|
const upsertType = SyncEntityType.AssetV1;
|
||||||
|
const upserts = this.syncRepository.getAssetUpserts(auth.user.id, checkpointMap[upsertType]);
|
||||||
for await (const { updateId, ...data } of upserts) {
|
for await (const { updateId, ...data } of upserts) {
|
||||||
send(response, { type: SyncEntityType.AssetV1, ids: [updateId], data: mapSyncAssetV1(data) });
|
send(response, { type: upsertType, ids: [updateId], data: mapSyncAssetV1(data) });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -231,21 +195,17 @@ export class SyncService extends BaseService {
|
|||||||
auth: AuthDto,
|
auth: AuthDto,
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
) {
|
) {
|
||||||
const backfillType = SyncEntityType.PartnerAssetBackfillV1;
|
|
||||||
const upsertType = SyncEntityType.PartnerAssetV1;
|
|
||||||
const deleteType = SyncEntityType.PartnerAssetDeleteV1;
|
const deleteType = SyncEntityType.PartnerAssetDeleteV1;
|
||||||
|
|
||||||
const backfillCheckpoint = checkpointMap[backfillType];
|
|
||||||
const upsertCheckpoint = checkpointMap[upsertType];
|
|
||||||
|
|
||||||
const deletes = this.syncRepository.getPartnerAssetDeletes(auth.user.id, checkpointMap[deleteType]);
|
const deletes = this.syncRepository.getPartnerAssetDeletes(auth.user.id, checkpointMap[deleteType]);
|
||||||
|
|
||||||
for await (const { id, ...data } of deletes) {
|
for await (const { id, ...data } of deletes) {
|
||||||
send(response, { type: deleteType, ids: [id], data });
|
send(response, { type: deleteType, ids: [id], data });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const backfillType = SyncEntityType.PartnerAssetBackfillV1;
|
||||||
|
const backfillCheckpoint = checkpointMap[backfillType];
|
||||||
const partners = await this.syncRepository.getPartnerBackfill(auth.user.id, backfillCheckpoint?.updateId);
|
const partners = await this.syncRepository.getPartnerBackfill(auth.user.id, backfillCheckpoint?.updateId);
|
||||||
|
const upsertType = SyncEntityType.PartnerAssetV1;
|
||||||
|
const upsertCheckpoint = checkpointMap[upsertType];
|
||||||
if (upsertCheckpoint) {
|
if (upsertCheckpoint) {
|
||||||
const endId = upsertCheckpoint.updateId;
|
const endId = upsertCheckpoint.updateId;
|
||||||
|
|
||||||
@ -283,9 +243,10 @@ export class SyncService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async syncAssetExifsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) {
|
private async syncAssetExifsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) {
|
||||||
const upserts = this.syncRepository.getAssetExifsUpserts(auth.user.id, checkpointMap[SyncEntityType.AssetExifV1]);
|
const upsertType = SyncEntityType.AssetExifV1;
|
||||||
|
const upserts = this.syncRepository.getAssetExifsUpserts(auth.user.id, checkpointMap[upsertType]);
|
||||||
for await (const { updateId, ...data } of upserts) {
|
for await (const { updateId, ...data } of upserts) {
|
||||||
send(response, { type: SyncEntityType.AssetExifV1, ids: [updateId], data });
|
send(response, { type: upsertType, ids: [updateId], data });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -296,13 +257,11 @@ export class SyncService extends BaseService {
|
|||||||
sessionId: string,
|
sessionId: string,
|
||||||
) {
|
) {
|
||||||
const backfillType = SyncEntityType.PartnerAssetExifBackfillV1;
|
const backfillType = SyncEntityType.PartnerAssetExifBackfillV1;
|
||||||
const upsertType = SyncEntityType.PartnerAssetExifV1;
|
|
||||||
|
|
||||||
const backfillCheckpoint = checkpointMap[backfillType];
|
const backfillCheckpoint = checkpointMap[backfillType];
|
||||||
const upsertCheckpoint = checkpointMap[upsertType];
|
|
||||||
|
|
||||||
const partners = await this.syncRepository.getPartnerBackfill(auth.user.id, backfillCheckpoint?.updateId);
|
const partners = await this.syncRepository.getPartnerBackfill(auth.user.id, backfillCheckpoint?.updateId);
|
||||||
|
|
||||||
|
const upsertType = SyncEntityType.PartnerAssetExifV1;
|
||||||
|
const upsertCheckpoint = checkpointMap[upsertType];
|
||||||
if (upsertCheckpoint) {
|
if (upsertCheckpoint) {
|
||||||
const endId = upsertCheckpoint.updateId;
|
const endId = upsertCheckpoint.updateId;
|
||||||
|
|
||||||
@ -336,33 +295,31 @@ export class SyncService extends BaseService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private async syncAlbumsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) {
|
private async syncAlbumsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) {
|
||||||
const deletes = this.syncRepository.getAlbumDeletes(auth.user.id, checkpointMap[SyncEntityType.AlbumDeleteV1]);
|
const deleteType = SyncEntityType.AlbumDeleteV1;
|
||||||
for await (const { id, ...data } of deletes) {
|
const deletes = this.syncRepository.getAlbumDeletes(auth.user.id, checkpointMap[deleteType]);
|
||||||
send(response, { type: SyncEntityType.AlbumDeleteV1, ids: [id], data });
|
|
||||||
}
|
|
||||||
|
|
||||||
const upserts = this.syncRepository.getAlbumUpserts(auth.user.id, checkpointMap[SyncEntityType.AlbumV1]);
|
|
||||||
for await (const { updateId, ...data } of upserts) {
|
|
||||||
send(response, { type: SyncEntityType.AlbumV1, ids: [updateId], data });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async syncAlbumUsersV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto, sessionId: string) {
|
|
||||||
const backfillType = SyncEntityType.AlbumUserBackfillV1;
|
|
||||||
const upsertType = SyncEntityType.AlbumUserV1;
|
|
||||||
const deleteType = SyncEntityType.AlbumUserDeleteV1;
|
|
||||||
|
|
||||||
const backfillCheckpoint = checkpointMap[backfillType];
|
|
||||||
const upsertCheckpoint = checkpointMap[upsertType];
|
|
||||||
|
|
||||||
const deletes = this.syncRepository.getAlbumUserDeletes(auth.user.id, checkpointMap[deleteType]);
|
|
||||||
|
|
||||||
for await (const { id, ...data } of deletes) {
|
for await (const { id, ...data } of deletes) {
|
||||||
send(response, { type: deleteType, ids: [id], data });
|
send(response, { type: deleteType, ids: [id], data });
|
||||||
}
|
}
|
||||||
|
|
||||||
const albums = await this.syncRepository.getAlbumBackfill(auth.user.id, backfillCheckpoint?.updateId);
|
const upsertType = SyncEntityType.AlbumV1;
|
||||||
|
const upserts = this.syncRepository.getAlbumUpserts(auth.user.id, checkpointMap[upsertType]);
|
||||||
|
for await (const { updateId, ...data } of upserts) {
|
||||||
|
send(response, { type: upsertType, ids: [updateId], data });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async syncAlbumUsersV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto, sessionId: string) {
|
||||||
|
const deleteType = SyncEntityType.AlbumUserDeleteV1;
|
||||||
|
const deletes = this.syncRepository.getAlbumUserDeletes(auth.user.id, checkpointMap[deleteType]);
|
||||||
|
for await (const { id, ...data } of deletes) {
|
||||||
|
send(response, { type: deleteType, ids: [id], data });
|
||||||
|
}
|
||||||
|
|
||||||
|
const backfillType = SyncEntityType.AlbumUserBackfillV1;
|
||||||
|
const backfillCheckpoint = checkpointMap[backfillType];
|
||||||
|
const albums = await this.syncRepository.getAlbumBackfill(auth.user.id, backfillCheckpoint?.updateId);
|
||||||
|
const upsertType = SyncEntityType.AlbumUserV1;
|
||||||
|
const upsertCheckpoint = checkpointMap[upsertType];
|
||||||
if (upsertCheckpoint) {
|
if (upsertCheckpoint) {
|
||||||
const endId = upsertCheckpoint.updateId;
|
const endId = upsertCheckpoint.updateId;
|
||||||
|
|
||||||
@ -397,13 +354,10 @@ export class SyncService extends BaseService {
|
|||||||
|
|
||||||
private async syncAlbumAssetsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto, sessionId: string) {
|
private async syncAlbumAssetsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto, sessionId: string) {
|
||||||
const backfillType = SyncEntityType.AlbumAssetBackfillV1;
|
const backfillType = SyncEntityType.AlbumAssetBackfillV1;
|
||||||
const upsertType = SyncEntityType.AlbumAssetV1;
|
|
||||||
|
|
||||||
const backfillCheckpoint = checkpointMap[backfillType];
|
const backfillCheckpoint = checkpointMap[backfillType];
|
||||||
const upsertCheckpoint = checkpointMap[upsertType];
|
|
||||||
|
|
||||||
const albums = await this.syncRepository.getAlbumBackfill(auth.user.id, backfillCheckpoint?.updateId);
|
const albums = await this.syncRepository.getAlbumBackfill(auth.user.id, backfillCheckpoint?.updateId);
|
||||||
|
const upsertType = SyncEntityType.AlbumAssetV1;
|
||||||
|
const upsertCheckpoint = checkpointMap[upsertType];
|
||||||
if (upsertCheckpoint) {
|
if (upsertCheckpoint) {
|
||||||
const endId = upsertCheckpoint.updateId;
|
const endId = upsertCheckpoint.updateId;
|
||||||
|
|
||||||
@ -443,13 +397,10 @@ export class SyncService extends BaseService {
|
|||||||
sessionId: string,
|
sessionId: string,
|
||||||
) {
|
) {
|
||||||
const backfillType = SyncEntityType.AlbumAssetExifBackfillV1;
|
const backfillType = SyncEntityType.AlbumAssetExifBackfillV1;
|
||||||
const upsertType = SyncEntityType.AlbumAssetExifV1;
|
|
||||||
|
|
||||||
const backfillCheckpoint = checkpointMap[backfillType];
|
const backfillCheckpoint = checkpointMap[backfillType];
|
||||||
const upsertCheckpoint = checkpointMap[upsertType];
|
|
||||||
|
|
||||||
const albums = await this.syncRepository.getAlbumBackfill(auth.user.id, backfillCheckpoint?.updateId);
|
const albums = await this.syncRepository.getAlbumBackfill(auth.user.id, backfillCheckpoint?.updateId);
|
||||||
|
const upsertType = SyncEntityType.AlbumAssetExifV1;
|
||||||
|
const upsertCheckpoint = checkpointMap[upsertType];
|
||||||
if (upsertCheckpoint) {
|
if (upsertCheckpoint) {
|
||||||
const endId = upsertCheckpoint.updateId;
|
const endId = upsertCheckpoint.updateId;
|
||||||
|
|
||||||
@ -488,22 +439,17 @@ export class SyncService extends BaseService {
|
|||||||
auth: AuthDto,
|
auth: AuthDto,
|
||||||
sessionId: string,
|
sessionId: string,
|
||||||
) {
|
) {
|
||||||
const backfillType = SyncEntityType.AlbumToAssetBackfillV1;
|
const deleteType = SyncEntityType.AlbumToAssetDeleteV1;
|
||||||
const upsertType = SyncEntityType.AlbumToAssetV1;
|
const deletes = this.syncRepository.getAlbumToAssetDeletes(auth.user.id, checkpointMap[deleteType]);
|
||||||
|
|
||||||
const backfillCheckpoint = checkpointMap[backfillType];
|
|
||||||
const upsertCheckpoint = checkpointMap[upsertType];
|
|
||||||
|
|
||||||
const deletes = this.syncRepository.getAlbumToAssetDeletes(
|
|
||||||
auth.user.id,
|
|
||||||
checkpointMap[SyncEntityType.AlbumToAssetDeleteV1],
|
|
||||||
);
|
|
||||||
for await (const { id, ...data } of deletes) {
|
for await (const { id, ...data } of deletes) {
|
||||||
send(response, { type: SyncEntityType.AlbumToAssetDeleteV1, ids: [id], data });
|
send(response, { type: deleteType, ids: [id], data });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const backfillType = SyncEntityType.AlbumToAssetBackfillV1;
|
||||||
|
const backfillCheckpoint = checkpointMap[backfillType];
|
||||||
const albums = await this.syncRepository.getAlbumBackfill(auth.user.id, backfillCheckpoint?.updateId);
|
const albums = await this.syncRepository.getAlbumBackfill(auth.user.id, backfillCheckpoint?.updateId);
|
||||||
|
const upsertType = SyncEntityType.AlbumToAssetV1;
|
||||||
|
const upsertCheckpoint = checkpointMap[upsertType];
|
||||||
if (upsertCheckpoint) {
|
if (upsertCheckpoint) {
|
||||||
const endId = upsertCheckpoint.updateId;
|
const endId = upsertCheckpoint.updateId;
|
||||||
|
|
||||||
@ -536,6 +482,34 @@ export class SyncService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async syncMemoriesV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) {
|
||||||
|
const deleteType = SyncEntityType.MemoryDeleteV1;
|
||||||
|
const deletes = this.syncRepository.getMemoryDeletes(auth.user.id, checkpointMap[SyncEntityType.MemoryDeleteV1]);
|
||||||
|
for await (const { id, ...data } of deletes) {
|
||||||
|
send(response, { type: deleteType, ids: [id], data });
|
||||||
|
}
|
||||||
|
|
||||||
|
const upsertType = SyncEntityType.MemoryV1;
|
||||||
|
const upserts = this.syncRepository.getMemoryUpserts(auth.user.id, checkpointMap[upsertType]);
|
||||||
|
for await (const { updateId, ...data } of upserts) {
|
||||||
|
send(response, { type: upsertType, ids: [updateId], data });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async syncMemoryAssetsV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto) {
|
||||||
|
const deleteType = SyncEntityType.MemoryToAssetDeleteV1;
|
||||||
|
const deletes = this.syncRepository.getMemoryAssetDeletes(auth.user.id, checkpointMap[deleteType]);
|
||||||
|
for await (const { id, ...data } of deletes) {
|
||||||
|
send(response, { type: deleteType, ids: [id], data });
|
||||||
|
}
|
||||||
|
|
||||||
|
const upsertType = SyncEntityType.MemoryToAssetV1;
|
||||||
|
const upserts = this.syncRepository.getMemoryAssetUpserts(auth.user.id, checkpointMap[upsertType]);
|
||||||
|
for await (const { updateId, ...data } of upserts) {
|
||||||
|
send(response, { type: upsertType, ids: [updateId], data });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async upsertBackfillCheckpoint(item: { type: SyncEntityType; sessionId: string; createId: string }) {
|
private async upsertBackfillCheckpoint(item: { type: SyncEntityType; sessionId: string; createId: string }) {
|
||||||
const { type, sessionId, createId } = item;
|
const { type, sessionId, createId } = item;
|
||||||
await this.syncRepository.upsertCheckpoints([
|
await this.syncRepository.upsertCheckpoints([
|
||||||
|
@ -4,9 +4,9 @@ import { DateTime } from 'luxon';
|
|||||||
import { createHash, randomBytes } from 'node:crypto';
|
import { createHash, randomBytes } from 'node:crypto';
|
||||||
import { Writable } from 'node:stream';
|
import { Writable } from 'node:stream';
|
||||||
import { AssetFace } from 'src/database';
|
import { AssetFace } from 'src/database';
|
||||||
import { Albums, AssetJobStatus, Assets, DB, Exif, FaceSearch, Person, Sessions } from 'src/db';
|
import { Albums, AssetJobStatus, Assets, DB, Exif, FaceSearch, Memories, Person, Sessions } from 'src/db';
|
||||||
import { AuthDto } from 'src/dtos/auth.dto';
|
import { AuthDto } from 'src/dtos/auth.dto';
|
||||||
import { AlbumUserRole, AssetType, AssetVisibility, SourceType, SyncRequestType } from 'src/enum';
|
import { AlbumUserRole, AssetType, AssetVisibility, MemoryType, SourceType, SyncRequestType } from 'src/enum';
|
||||||
import { AccessRepository } from 'src/repositories/access.repository';
|
import { AccessRepository } from 'src/repositories/access.repository';
|
||||||
import { ActivityRepository } from 'src/repositories/activity.repository';
|
import { ActivityRepository } from 'src/repositories/activity.repository';
|
||||||
import { AlbumUserRepository } from 'src/repositories/album-user.repository';
|
import { AlbumUserRepository } from 'src/repositories/album-user.repository';
|
||||||
@ -129,6 +129,17 @@ export class MediumTestContext<S extends BaseService = BaseService> {
|
|||||||
return { asset, result };
|
return { asset, result };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async newMemory(dto: Partial<Insertable<Memories>> = {}) {
|
||||||
|
const memory = mediumFactory.memoryInsert(dto);
|
||||||
|
const result = await this.get(MemoryRepository).create(memory, new Set<string>());
|
||||||
|
return { memory, result };
|
||||||
|
}
|
||||||
|
|
||||||
|
async newMemoryAsset(dto: { memoryId: string; assetId: string }) {
|
||||||
|
const result = await this.get(MemoryRepository).addAssetIds(dto.memoryId, [dto.assetId]);
|
||||||
|
return { memoryAsset: dto, result };
|
||||||
|
}
|
||||||
|
|
||||||
async newExif(dto: Insertable<Exif>) {
|
async newExif(dto: Insertable<Exif>) {
|
||||||
const result = await this.get(AssetRepository).upsertExif(dto);
|
const result = await this.get(AssetRepository).upsertExif(dto);
|
||||||
return { result };
|
return { result };
|
||||||
@ -452,6 +463,28 @@ const userInsert = (user: Partial<Insertable<UserTable>> = {}) => {
|
|||||||
return { ...defaults, ...user, id };
|
return { ...defaults, ...user, id };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const memoryInsert = (memory: Partial<Insertable<Memories>> = {}) => {
|
||||||
|
const id = memory.id || newUuid();
|
||||||
|
const date = newDate();
|
||||||
|
|
||||||
|
const defaults: Insertable<Memories> = {
|
||||||
|
id,
|
||||||
|
createdAt: date,
|
||||||
|
updatedAt: date,
|
||||||
|
deletedAt: null,
|
||||||
|
type: MemoryType.ON_THIS_DAY,
|
||||||
|
data: { year: 2025 },
|
||||||
|
showAt: null,
|
||||||
|
hideAt: null,
|
||||||
|
seenAt: null,
|
||||||
|
isSaved: false,
|
||||||
|
memoryAt: date,
|
||||||
|
ownerId: memory.ownerId || newUuid(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return { ...defaults, ...memory, id };
|
||||||
|
};
|
||||||
|
|
||||||
class CustomWritable extends Writable {
|
class CustomWritable extends Writable {
|
||||||
private data = '';
|
private data = '';
|
||||||
|
|
||||||
@ -483,4 +516,5 @@ export const mediumFactory = {
|
|||||||
sessionInsert,
|
sessionInsert,
|
||||||
syncStream,
|
syncStream,
|
||||||
userInsert,
|
userInsert,
|
||||||
|
memoryInsert,
|
||||||
};
|
};
|
||||||
|
84
server/test/medium/specs/sync/sync-memory-asset.spec.ts
Normal file
84
server/test/medium/specs/sync/sync-memory-asset.spec.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { Kysely } from 'kysely';
|
||||||
|
import { DB } from 'src/db';
|
||||||
|
import { SyncEntityType, SyncRequestType } from 'src/enum';
|
||||||
|
import { MemoryRepository } from 'src/repositories/memory.repository';
|
||||||
|
import { SyncTestContext } from 'test/medium.factory';
|
||||||
|
import { getKyselyDB } from 'test/utils';
|
||||||
|
|
||||||
|
let defaultDatabase: Kysely<DB>;
|
||||||
|
|
||||||
|
const setup = async (db?: Kysely<DB>) => {
|
||||||
|
const ctx = new SyncTestContext(db || defaultDatabase);
|
||||||
|
const { auth, user, session } = await ctx.newSyncAuthUser();
|
||||||
|
return { auth, user, session, ctx };
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
defaultDatabase = await getKyselyDB();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(SyncEntityType.MemoryToAssetV1, () => {
|
||||||
|
it('should detect and sync a memory to asset relation', async () => {
|
||||||
|
const { auth, user, ctx } = await setup();
|
||||||
|
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||||
|
const { memory } = await ctx.newMemory({ ownerId: user.id });
|
||||||
|
await ctx.newMemoryAsset({ memoryId: memory.id, assetId: asset.id });
|
||||||
|
|
||||||
|
const response = await ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1]);
|
||||||
|
expect(response).toHaveLength(1);
|
||||||
|
expect(response).toEqual([
|
||||||
|
{
|
||||||
|
ack: expect.any(String),
|
||||||
|
data: {
|
||||||
|
memoryId: memory.id,
|
||||||
|
assetId: asset.id,
|
||||||
|
},
|
||||||
|
type: 'MemoryToAssetV1',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
await ctx.syncAckAll(auth, response);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1])).resolves.toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect and sync a deleted memory to asset relation', async () => {
|
||||||
|
const { auth, user, ctx } = await setup();
|
||||||
|
const memoryRepo = ctx.get(MemoryRepository);
|
||||||
|
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||||
|
const { memory } = await ctx.newMemory({ ownerId: user.id });
|
||||||
|
await ctx.newMemoryAsset({ memoryId: memory.id, assetId: asset.id });
|
||||||
|
await memoryRepo.removeAssetIds(memory.id, [asset.id]);
|
||||||
|
|
||||||
|
const response = await ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1]);
|
||||||
|
expect(response).toHaveLength(1);
|
||||||
|
expect(response).toEqual([
|
||||||
|
{
|
||||||
|
ack: expect.any(String),
|
||||||
|
data: {
|
||||||
|
assetId: asset.id,
|
||||||
|
memoryId: memory.id,
|
||||||
|
},
|
||||||
|
type: 'MemoryToAssetDeleteV1',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
await ctx.syncAckAll(auth, response);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1])).resolves.toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not sync a memory to asset relation or delete for an unrelated user', async () => {
|
||||||
|
const { auth, ctx } = await setup();
|
||||||
|
const memoryRepo = ctx.get(MemoryRepository);
|
||||||
|
const { auth: auth2, user: user2 } = await ctx.newSyncAuthUser();
|
||||||
|
const { asset } = await ctx.newAsset({ ownerId: user2.id });
|
||||||
|
const { memory } = await ctx.newMemory({ ownerId: user2.id });
|
||||||
|
await ctx.newMemoryAsset({ memoryId: memory.id, assetId: asset.id });
|
||||||
|
|
||||||
|
expect(await ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1])).toHaveLength(0);
|
||||||
|
expect(await ctx.syncStream(auth2, [SyncRequestType.MemoryToAssetsV1])).toHaveLength(1);
|
||||||
|
|
||||||
|
await memoryRepo.removeAssetIds(memory.id, [asset.id]);
|
||||||
|
expect(await ctx.syncStream(auth, [SyncRequestType.MemoryToAssetsV1])).toHaveLength(0);
|
||||||
|
expect(await ctx.syncStream(auth2, [SyncRequestType.MemoryToAssetsV1])).toHaveLength(1);
|
||||||
|
});
|
||||||
|
});
|
115
server/test/medium/specs/sync/sync-memory.spec.ts
Normal file
115
server/test/medium/specs/sync/sync-memory.spec.ts
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
import { Kysely } from 'kysely';
|
||||||
|
import { DB } from 'src/db';
|
||||||
|
import { SyncEntityType, SyncRequestType } from 'src/enum';
|
||||||
|
import { MemoryRepository } from 'src/repositories/memory.repository';
|
||||||
|
import { SyncTestContext } from 'test/medium.factory';
|
||||||
|
import { getKyselyDB } from 'test/utils';
|
||||||
|
|
||||||
|
let defaultDatabase: Kysely<DB>;
|
||||||
|
|
||||||
|
const setup = async (db?: Kysely<DB>) => {
|
||||||
|
const ctx = new SyncTestContext(db || defaultDatabase);
|
||||||
|
const { auth, user, session } = await ctx.newSyncAuthUser();
|
||||||
|
return { auth, user, session, ctx };
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
defaultDatabase = await getKyselyDB();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe(SyncEntityType.MemoryV1, () => {
|
||||||
|
it('should detect and sync the first memory with the right properties', async () => {
|
||||||
|
const { auth, user: user1, ctx } = await setup();
|
||||||
|
const { memory } = await ctx.newMemory({ ownerId: user1.id });
|
||||||
|
|
||||||
|
const response = await ctx.syncStream(auth, [SyncRequestType.MemoriesV1]);
|
||||||
|
expect(response).toHaveLength(1);
|
||||||
|
expect(response).toEqual([
|
||||||
|
{
|
||||||
|
ack: expect.any(String),
|
||||||
|
data: {
|
||||||
|
id: memory.id,
|
||||||
|
createdAt: expect.any(String),
|
||||||
|
updatedAt: expect.any(String),
|
||||||
|
deletedAt: memory.deletedAt,
|
||||||
|
type: memory.type,
|
||||||
|
data: memory.data,
|
||||||
|
hideAt: memory.hideAt,
|
||||||
|
showAt: memory.showAt,
|
||||||
|
seenAt: memory.seenAt,
|
||||||
|
memoryAt: expect.any(String),
|
||||||
|
isSaved: memory.isSaved,
|
||||||
|
ownerId: memory.ownerId,
|
||||||
|
},
|
||||||
|
type: 'MemoryV1',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
await ctx.syncAckAll(auth, response);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.MemoriesV1])).resolves.toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect and sync a deleted memory', async () => {
|
||||||
|
const { auth, user, ctx } = await setup();
|
||||||
|
const memoryRepo = ctx.get(MemoryRepository);
|
||||||
|
const { memory } = await ctx.newMemory({ ownerId: user.id });
|
||||||
|
await memoryRepo.delete(memory.id);
|
||||||
|
|
||||||
|
const response = await ctx.syncStream(auth, [SyncRequestType.MemoriesV1]);
|
||||||
|
expect(response).toHaveLength(1);
|
||||||
|
expect(response).toEqual([
|
||||||
|
{
|
||||||
|
ack: expect.any(String),
|
||||||
|
data: {
|
||||||
|
memoryId: memory.id,
|
||||||
|
},
|
||||||
|
type: 'MemoryDeleteV1',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
await ctx.syncAckAll(auth, response);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.MemoriesV1])).resolves.toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should sync a memory and then an update to that same memory', async () => {
|
||||||
|
const { auth, user, ctx } = await setup();
|
||||||
|
const memoryRepo = ctx.get(MemoryRepository);
|
||||||
|
const { memory } = await ctx.newMemory({ ownerId: user.id });
|
||||||
|
|
||||||
|
const response = await ctx.syncStream(auth, [SyncRequestType.MemoriesV1]);
|
||||||
|
expect(response).toHaveLength(1);
|
||||||
|
expect(response).toEqual([
|
||||||
|
{
|
||||||
|
ack: expect.any(String),
|
||||||
|
data: expect.objectContaining({ id: memory.id }),
|
||||||
|
type: 'MemoryV1',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
await ctx.syncAckAll(auth, response);
|
||||||
|
await memoryRepo.update(memory.id, { seenAt: new Date() });
|
||||||
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.MemoriesV1]);
|
||||||
|
expect(newResponse).toHaveLength(1);
|
||||||
|
expect(newResponse).toEqual([
|
||||||
|
{
|
||||||
|
ack: expect.any(String),
|
||||||
|
data: expect.objectContaining({ id: memory.id }),
|
||||||
|
type: 'MemoryV1',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.MemoriesV1])).resolves.toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not sync a memory or a memory delete for an unrelated user', async () => {
|
||||||
|
const { auth, ctx } = await setup();
|
||||||
|
const memoryRepo = ctx.get(MemoryRepository);
|
||||||
|
const { user: user2 } = await ctx.newUser();
|
||||||
|
const { memory } = await ctx.newMemory({ ownerId: user2.id });
|
||||||
|
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.MemoriesV1])).resolves.toEqual([]);
|
||||||
|
await memoryRepo.delete(memory.id);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.MemoriesV1])).resolves.toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
Reference in New Issue
Block a user