mirror of
https://github.com/immich-app/immich.git
synced 2025-07-09 03:04:16 -04:00
feat: sync partner stacks (#19635)
This commit is contained in:
parent
32a7087883
commit
58ca1402ed
9
mobile/openapi/lib/model/sync_entity_type.dart
generated
9
mobile/openapi/lib/model/sync_entity_type.dart
generated
@ -35,6 +35,9 @@ class SyncEntityType {
|
|||||||
static const partnerAssetDeleteV1 = SyncEntityType._(r'PartnerAssetDeleteV1');
|
static const partnerAssetDeleteV1 = SyncEntityType._(r'PartnerAssetDeleteV1');
|
||||||
static const partnerAssetExifV1 = SyncEntityType._(r'PartnerAssetExifV1');
|
static const partnerAssetExifV1 = SyncEntityType._(r'PartnerAssetExifV1');
|
||||||
static const partnerAssetExifBackfillV1 = SyncEntityType._(r'PartnerAssetExifBackfillV1');
|
static const partnerAssetExifBackfillV1 = SyncEntityType._(r'PartnerAssetExifBackfillV1');
|
||||||
|
static const partnerStackBackfillV1 = SyncEntityType._(r'PartnerStackBackfillV1');
|
||||||
|
static const partnerStackDeleteV1 = SyncEntityType._(r'PartnerStackDeleteV1');
|
||||||
|
static const partnerStackV1 = SyncEntityType._(r'PartnerStackV1');
|
||||||
static const albumV1 = SyncEntityType._(r'AlbumV1');
|
static const albumV1 = SyncEntityType._(r'AlbumV1');
|
||||||
static const albumDeleteV1 = SyncEntityType._(r'AlbumDeleteV1');
|
static const albumDeleteV1 = SyncEntityType._(r'AlbumDeleteV1');
|
||||||
static const albumUserV1 = SyncEntityType._(r'AlbumUserV1');
|
static const albumUserV1 = SyncEntityType._(r'AlbumUserV1');
|
||||||
@ -69,6 +72,9 @@ class SyncEntityType {
|
|||||||
partnerAssetDeleteV1,
|
partnerAssetDeleteV1,
|
||||||
partnerAssetExifV1,
|
partnerAssetExifV1,
|
||||||
partnerAssetExifBackfillV1,
|
partnerAssetExifBackfillV1,
|
||||||
|
partnerStackBackfillV1,
|
||||||
|
partnerStackDeleteV1,
|
||||||
|
partnerStackV1,
|
||||||
albumV1,
|
albumV1,
|
||||||
albumDeleteV1,
|
albumDeleteV1,
|
||||||
albumUserV1,
|
albumUserV1,
|
||||||
@ -138,6 +144,9 @@ class SyncEntityTypeTypeTransformer {
|
|||||||
case r'PartnerAssetDeleteV1': return SyncEntityType.partnerAssetDeleteV1;
|
case r'PartnerAssetDeleteV1': return SyncEntityType.partnerAssetDeleteV1;
|
||||||
case r'PartnerAssetExifV1': return SyncEntityType.partnerAssetExifV1;
|
case r'PartnerAssetExifV1': return SyncEntityType.partnerAssetExifV1;
|
||||||
case r'PartnerAssetExifBackfillV1': return SyncEntityType.partnerAssetExifBackfillV1;
|
case r'PartnerAssetExifBackfillV1': return SyncEntityType.partnerAssetExifBackfillV1;
|
||||||
|
case r'PartnerStackBackfillV1': return SyncEntityType.partnerStackBackfillV1;
|
||||||
|
case r'PartnerStackDeleteV1': return SyncEntityType.partnerStackDeleteV1;
|
||||||
|
case r'PartnerStackV1': return SyncEntityType.partnerStackV1;
|
||||||
case r'AlbumV1': return SyncEntityType.albumV1;
|
case r'AlbumV1': return SyncEntityType.albumV1;
|
||||||
case r'AlbumDeleteV1': return SyncEntityType.albumDeleteV1;
|
case r'AlbumDeleteV1': return SyncEntityType.albumDeleteV1;
|
||||||
case r'AlbumUserV1': return SyncEntityType.albumUserV1;
|
case r'AlbumUserV1': return SyncEntityType.albumUserV1;
|
||||||
|
39
mobile/openapi/lib/model/sync_request_type.dart
generated
39
mobile/openapi/lib/model/sync_request_type.dart
generated
@ -23,37 +23,39 @@ class SyncRequestType {
|
|||||||
|
|
||||||
String toJson() => value;
|
String toJson() => value;
|
||||||
|
|
||||||
static const usersV1 = SyncRequestType._(r'UsersV1');
|
|
||||||
static const partnersV1 = SyncRequestType._(r'PartnersV1');
|
|
||||||
static const assetsV1 = SyncRequestType._(r'AssetsV1');
|
|
||||||
static const assetExifsV1 = SyncRequestType._(r'AssetExifsV1');
|
|
||||||
static const partnerAssetsV1 = SyncRequestType._(r'PartnerAssetsV1');
|
|
||||||
static const partnerAssetExifsV1 = SyncRequestType._(r'PartnerAssetExifsV1');
|
|
||||||
static const albumsV1 = SyncRequestType._(r'AlbumsV1');
|
static const albumsV1 = SyncRequestType._(r'AlbumsV1');
|
||||||
static const albumUsersV1 = SyncRequestType._(r'AlbumUsersV1');
|
static const albumUsersV1 = SyncRequestType._(r'AlbumUsersV1');
|
||||||
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 assetsV1 = SyncRequestType._(r'AssetsV1');
|
||||||
|
static const assetExifsV1 = SyncRequestType._(r'AssetExifsV1');
|
||||||
static const memoriesV1 = SyncRequestType._(r'MemoriesV1');
|
static const memoriesV1 = SyncRequestType._(r'MemoriesV1');
|
||||||
static const memoryToAssetsV1 = SyncRequestType._(r'MemoryToAssetsV1');
|
static const memoryToAssetsV1 = SyncRequestType._(r'MemoryToAssetsV1');
|
||||||
|
static const partnersV1 = SyncRequestType._(r'PartnersV1');
|
||||||
|
static const partnerAssetsV1 = SyncRequestType._(r'PartnerAssetsV1');
|
||||||
|
static const partnerAssetExifsV1 = SyncRequestType._(r'PartnerAssetExifsV1');
|
||||||
|
static const partnerStacksV1 = SyncRequestType._(r'PartnerStacksV1');
|
||||||
static const stacksV1 = SyncRequestType._(r'StacksV1');
|
static const stacksV1 = SyncRequestType._(r'StacksV1');
|
||||||
|
static const usersV1 = SyncRequestType._(r'UsersV1');
|
||||||
|
|
||||||
/// 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>[
|
||||||
usersV1,
|
|
||||||
partnersV1,
|
|
||||||
assetsV1,
|
|
||||||
assetExifsV1,
|
|
||||||
partnerAssetsV1,
|
|
||||||
partnerAssetExifsV1,
|
|
||||||
albumsV1,
|
albumsV1,
|
||||||
albumUsersV1,
|
albumUsersV1,
|
||||||
albumToAssetsV1,
|
albumToAssetsV1,
|
||||||
albumAssetsV1,
|
albumAssetsV1,
|
||||||
albumAssetExifsV1,
|
albumAssetExifsV1,
|
||||||
|
assetsV1,
|
||||||
|
assetExifsV1,
|
||||||
memoriesV1,
|
memoriesV1,
|
||||||
memoryToAssetsV1,
|
memoryToAssetsV1,
|
||||||
|
partnersV1,
|
||||||
|
partnerAssetsV1,
|
||||||
|
partnerAssetExifsV1,
|
||||||
|
partnerStacksV1,
|
||||||
stacksV1,
|
stacksV1,
|
||||||
|
usersV1,
|
||||||
];
|
];
|
||||||
|
|
||||||
static SyncRequestType? fromJson(dynamic value) => SyncRequestTypeTypeTransformer().decode(value);
|
static SyncRequestType? fromJson(dynamic value) => SyncRequestTypeTypeTransformer().decode(value);
|
||||||
@ -92,20 +94,21 @@ class SyncRequestTypeTypeTransformer {
|
|||||||
SyncRequestType? decode(dynamic data, {bool allowNull = true}) {
|
SyncRequestType? decode(dynamic data, {bool allowNull = true}) {
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
switch (data) {
|
switch (data) {
|
||||||
case r'UsersV1': return SyncRequestType.usersV1;
|
|
||||||
case r'PartnersV1': return SyncRequestType.partnersV1;
|
|
||||||
case r'AssetsV1': return SyncRequestType.assetsV1;
|
|
||||||
case r'AssetExifsV1': return SyncRequestType.assetExifsV1;
|
|
||||||
case r'PartnerAssetsV1': return SyncRequestType.partnerAssetsV1;
|
|
||||||
case r'PartnerAssetExifsV1': return SyncRequestType.partnerAssetExifsV1;
|
|
||||||
case r'AlbumsV1': return SyncRequestType.albumsV1;
|
case r'AlbumsV1': return SyncRequestType.albumsV1;
|
||||||
case r'AlbumUsersV1': return SyncRequestType.albumUsersV1;
|
case r'AlbumUsersV1': return SyncRequestType.albumUsersV1;
|
||||||
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'AssetsV1': return SyncRequestType.assetsV1;
|
||||||
|
case r'AssetExifsV1': return SyncRequestType.assetExifsV1;
|
||||||
case r'MemoriesV1': return SyncRequestType.memoriesV1;
|
case r'MemoriesV1': return SyncRequestType.memoriesV1;
|
||||||
case r'MemoryToAssetsV1': return SyncRequestType.memoryToAssetsV1;
|
case r'MemoryToAssetsV1': return SyncRequestType.memoryToAssetsV1;
|
||||||
|
case r'PartnersV1': return SyncRequestType.partnersV1;
|
||||||
|
case r'PartnerAssetsV1': return SyncRequestType.partnerAssetsV1;
|
||||||
|
case r'PartnerAssetExifsV1': return SyncRequestType.partnerAssetExifsV1;
|
||||||
|
case r'PartnerStacksV1': return SyncRequestType.partnerStacksV1;
|
||||||
case r'StacksV1': return SyncRequestType.stacksV1;
|
case r'StacksV1': return SyncRequestType.stacksV1;
|
||||||
|
case r'UsersV1': return SyncRequestType.usersV1;
|
||||||
default:
|
default:
|
||||||
if (!allowNull) {
|
if (!allowNull) {
|
||||||
throw ArgumentError('Unknown enum value to decode: $data');
|
throw ArgumentError('Unknown enum value to decode: $data');
|
||||||
|
@ -13804,6 +13804,9 @@
|
|||||||
"PartnerAssetDeleteV1",
|
"PartnerAssetDeleteV1",
|
||||||
"PartnerAssetExifV1",
|
"PartnerAssetExifV1",
|
||||||
"PartnerAssetExifBackfillV1",
|
"PartnerAssetExifBackfillV1",
|
||||||
|
"PartnerStackBackfillV1",
|
||||||
|
"PartnerStackDeleteV1",
|
||||||
|
"PartnerStackV1",
|
||||||
"AlbumV1",
|
"AlbumV1",
|
||||||
"AlbumDeleteV1",
|
"AlbumDeleteV1",
|
||||||
"AlbumUserV1",
|
"AlbumUserV1",
|
||||||
@ -13973,20 +13976,21 @@
|
|||||||
},
|
},
|
||||||
"SyncRequestType": {
|
"SyncRequestType": {
|
||||||
"enum": [
|
"enum": [
|
||||||
"UsersV1",
|
|
||||||
"PartnersV1",
|
|
||||||
"AssetsV1",
|
|
||||||
"AssetExifsV1",
|
|
||||||
"PartnerAssetsV1",
|
|
||||||
"PartnerAssetExifsV1",
|
|
||||||
"AlbumsV1",
|
"AlbumsV1",
|
||||||
"AlbumUsersV1",
|
"AlbumUsersV1",
|
||||||
"AlbumToAssetsV1",
|
"AlbumToAssetsV1",
|
||||||
"AlbumAssetsV1",
|
"AlbumAssetsV1",
|
||||||
"AlbumAssetExifsV1",
|
"AlbumAssetExifsV1",
|
||||||
|
"AssetsV1",
|
||||||
|
"AssetExifsV1",
|
||||||
"MemoriesV1",
|
"MemoriesV1",
|
||||||
"MemoryToAssetsV1",
|
"MemoryToAssetsV1",
|
||||||
"StacksV1"
|
"PartnersV1",
|
||||||
|
"PartnerAssetsV1",
|
||||||
|
"PartnerAssetExifsV1",
|
||||||
|
"PartnerStacksV1",
|
||||||
|
"StacksV1",
|
||||||
|
"UsersV1"
|
||||||
],
|
],
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
@ -4073,6 +4073,9 @@ export enum SyncEntityType {
|
|||||||
PartnerAssetDeleteV1 = "PartnerAssetDeleteV1",
|
PartnerAssetDeleteV1 = "PartnerAssetDeleteV1",
|
||||||
PartnerAssetExifV1 = "PartnerAssetExifV1",
|
PartnerAssetExifV1 = "PartnerAssetExifV1",
|
||||||
PartnerAssetExifBackfillV1 = "PartnerAssetExifBackfillV1",
|
PartnerAssetExifBackfillV1 = "PartnerAssetExifBackfillV1",
|
||||||
|
PartnerStackBackfillV1 = "PartnerStackBackfillV1",
|
||||||
|
PartnerStackDeleteV1 = "PartnerStackDeleteV1",
|
||||||
|
PartnerStackV1 = "PartnerStackV1",
|
||||||
AlbumV1 = "AlbumV1",
|
AlbumV1 = "AlbumV1",
|
||||||
AlbumDeleteV1 = "AlbumDeleteV1",
|
AlbumDeleteV1 = "AlbumDeleteV1",
|
||||||
AlbumUserV1 = "AlbumUserV1",
|
AlbumUserV1 = "AlbumUserV1",
|
||||||
@ -4094,20 +4097,21 @@ export enum SyncEntityType {
|
|||||||
SyncAckV1 = "SyncAckV1"
|
SyncAckV1 = "SyncAckV1"
|
||||||
}
|
}
|
||||||
export enum SyncRequestType {
|
export enum SyncRequestType {
|
||||||
UsersV1 = "UsersV1",
|
|
||||||
PartnersV1 = "PartnersV1",
|
|
||||||
AssetsV1 = "AssetsV1",
|
|
||||||
AssetExifsV1 = "AssetExifsV1",
|
|
||||||
PartnerAssetsV1 = "PartnerAssetsV1",
|
|
||||||
PartnerAssetExifsV1 = "PartnerAssetExifsV1",
|
|
||||||
AlbumsV1 = "AlbumsV1",
|
AlbumsV1 = "AlbumsV1",
|
||||||
AlbumUsersV1 = "AlbumUsersV1",
|
AlbumUsersV1 = "AlbumUsersV1",
|
||||||
AlbumToAssetsV1 = "AlbumToAssetsV1",
|
AlbumToAssetsV1 = "AlbumToAssetsV1",
|
||||||
AlbumAssetsV1 = "AlbumAssetsV1",
|
AlbumAssetsV1 = "AlbumAssetsV1",
|
||||||
AlbumAssetExifsV1 = "AlbumAssetExifsV1",
|
AlbumAssetExifsV1 = "AlbumAssetExifsV1",
|
||||||
|
AssetsV1 = "AssetsV1",
|
||||||
|
AssetExifsV1 = "AssetExifsV1",
|
||||||
MemoriesV1 = "MemoriesV1",
|
MemoriesV1 = "MemoriesV1",
|
||||||
MemoryToAssetsV1 = "MemoryToAssetsV1",
|
MemoryToAssetsV1 = "MemoryToAssetsV1",
|
||||||
StacksV1 = "StacksV1"
|
PartnersV1 = "PartnersV1",
|
||||||
|
PartnerAssetsV1 = "PartnerAssetsV1",
|
||||||
|
PartnerAssetExifsV1 = "PartnerAssetExifsV1",
|
||||||
|
PartnerStacksV1 = "PartnerStacksV1",
|
||||||
|
StacksV1 = "StacksV1",
|
||||||
|
UsersV1 = "UsersV1"
|
||||||
}
|
}
|
||||||
export enum TranscodeHWAccel {
|
export enum TranscodeHWAccel {
|
||||||
Nvenc = "nvenc",
|
Nvenc = "nvenc",
|
||||||
|
@ -356,6 +356,13 @@ export const columns = {
|
|||||||
'assets.duration',
|
'assets.duration',
|
||||||
],
|
],
|
||||||
syncAlbumUser: ['album_users.albumsId as albumId', 'album_users.usersId as userId', 'album_users.role'],
|
syncAlbumUser: ['album_users.albumsId as albumId', 'album_users.usersId as userId', 'album_users.role'],
|
||||||
|
syncStack: [
|
||||||
|
'asset_stack.id',
|
||||||
|
'asset_stack.createdAt',
|
||||||
|
'asset_stack.updatedAt',
|
||||||
|
'asset_stack.primaryAssetId',
|
||||||
|
'asset_stack.ownerId',
|
||||||
|
],
|
||||||
stack: ['stack.id', 'stack.primaryAssetId', 'ownerId'],
|
stack: ['stack.id', 'stack.primaryAssetId', 'ownerId'],
|
||||||
syncAssetExif: [
|
syncAssetExif: [
|
||||||
'exif.assetId',
|
'exif.assetId',
|
||||||
|
@ -267,6 +267,9 @@ export type SyncItem = {
|
|||||||
[SyncEntityType.MemoryToAssetDeleteV1]: SyncMemoryAssetDeleteV1;
|
[SyncEntityType.MemoryToAssetDeleteV1]: SyncMemoryAssetDeleteV1;
|
||||||
[SyncEntityType.StackV1]: SyncStackV1;
|
[SyncEntityType.StackV1]: SyncStackV1;
|
||||||
[SyncEntityType.StackDeleteV1]: SyncStackDeleteV1;
|
[SyncEntityType.StackDeleteV1]: SyncStackDeleteV1;
|
||||||
|
[SyncEntityType.PartnerStackBackfillV1]: SyncStackV1;
|
||||||
|
[SyncEntityType.PartnerStackDeleteV1]: SyncStackDeleteV1;
|
||||||
|
[SyncEntityType.PartnerStackV1]: SyncStackV1;
|
||||||
[SyncEntityType.SyncAckV1]: SyncAckV1;
|
[SyncEntityType.SyncAckV1]: SyncAckV1;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -573,20 +573,21 @@ export enum DatabaseLock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum SyncRequestType {
|
export enum SyncRequestType {
|
||||||
UsersV1 = 'UsersV1',
|
|
||||||
PartnersV1 = 'PartnersV1',
|
|
||||||
AssetsV1 = 'AssetsV1',
|
|
||||||
AssetExifsV1 = 'AssetExifsV1',
|
|
||||||
PartnerAssetsV1 = 'PartnerAssetsV1',
|
|
||||||
PartnerAssetExifsV1 = 'PartnerAssetExifsV1',
|
|
||||||
AlbumsV1 = 'AlbumsV1',
|
AlbumsV1 = 'AlbumsV1',
|
||||||
AlbumUsersV1 = 'AlbumUsersV1',
|
AlbumUsersV1 = 'AlbumUsersV1',
|
||||||
AlbumToAssetsV1 = 'AlbumToAssetsV1',
|
AlbumToAssetsV1 = 'AlbumToAssetsV1',
|
||||||
AlbumAssetsV1 = 'AlbumAssetsV1',
|
AlbumAssetsV1 = 'AlbumAssetsV1',
|
||||||
AlbumAssetExifsV1 = 'AlbumAssetExifsV1',
|
AlbumAssetExifsV1 = 'AlbumAssetExifsV1',
|
||||||
|
AssetsV1 = 'AssetsV1',
|
||||||
|
AssetExifsV1 = 'AssetExifsV1',
|
||||||
MemoriesV1 = 'MemoriesV1',
|
MemoriesV1 = 'MemoriesV1',
|
||||||
MemoryToAssetsV1 = 'MemoryToAssetsV1',
|
MemoryToAssetsV1 = 'MemoryToAssetsV1',
|
||||||
|
PartnersV1 = 'PartnersV1',
|
||||||
|
PartnerAssetsV1 = 'PartnerAssetsV1',
|
||||||
|
PartnerAssetExifsV1 = 'PartnerAssetExifsV1',
|
||||||
|
PartnerStacksV1 = 'PartnerStacksV1',
|
||||||
StacksV1 = 'StacksV1',
|
StacksV1 = 'StacksV1',
|
||||||
|
UsersV1 = 'UsersV1',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum SyncEntityType {
|
export enum SyncEntityType {
|
||||||
@ -605,6 +606,9 @@ export enum SyncEntityType {
|
|||||||
PartnerAssetDeleteV1 = 'PartnerAssetDeleteV1',
|
PartnerAssetDeleteV1 = 'PartnerAssetDeleteV1',
|
||||||
PartnerAssetExifV1 = 'PartnerAssetExifV1',
|
PartnerAssetExifV1 = 'PartnerAssetExifV1',
|
||||||
PartnerAssetExifBackfillV1 = 'PartnerAssetExifBackfillV1',
|
PartnerAssetExifBackfillV1 = 'PartnerAssetExifBackfillV1',
|
||||||
|
PartnerStackBackfillV1 = 'PartnerStackBackfillV1',
|
||||||
|
PartnerStackDeleteV1 = 'PartnerStackDeleteV1',
|
||||||
|
PartnerStackV1 = 'PartnerStackV1',
|
||||||
|
|
||||||
AlbumV1 = 'AlbumV1',
|
AlbumV1 = 'AlbumV1',
|
||||||
AlbumDeleteV1 = 'AlbumDeleteV1',
|
AlbumDeleteV1 = 'AlbumDeleteV1',
|
||||||
|
@ -689,6 +689,66 @@ where
|
|||||||
order by
|
order by
|
||||||
"updateId" asc
|
"updateId" asc
|
||||||
|
|
||||||
|
-- SyncRepository.partnerStack.getDeletes
|
||||||
|
select
|
||||||
|
"id",
|
||||||
|
"stackId"
|
||||||
|
from
|
||||||
|
"stacks_audit"
|
||||||
|
where
|
||||||
|
"userId" in (
|
||||||
|
select
|
||||||
|
"sharedById"
|
||||||
|
from
|
||||||
|
"partners"
|
||||||
|
where
|
||||||
|
"sharedWithId" = $1
|
||||||
|
)
|
||||||
|
and "deletedAt" < now() - interval '1 millisecond'
|
||||||
|
order by
|
||||||
|
"id" asc
|
||||||
|
|
||||||
|
-- SyncRepository.partnerStack.getBackfill
|
||||||
|
select
|
||||||
|
"asset_stack"."id",
|
||||||
|
"asset_stack"."createdAt",
|
||||||
|
"asset_stack"."updatedAt",
|
||||||
|
"asset_stack"."primaryAssetId",
|
||||||
|
"asset_stack"."ownerId",
|
||||||
|
"updateId"
|
||||||
|
from
|
||||||
|
"asset_stack"
|
||||||
|
where
|
||||||
|
"ownerId" = $1
|
||||||
|
and "updatedAt" < now() - interval '1 millisecond'
|
||||||
|
and "updateId" <= $2
|
||||||
|
and "updateId" >= $3
|
||||||
|
order by
|
||||||
|
"updateId" asc
|
||||||
|
|
||||||
|
-- SyncRepository.partnerStack.getUpserts
|
||||||
|
select
|
||||||
|
"asset_stack"."id",
|
||||||
|
"asset_stack"."createdAt",
|
||||||
|
"asset_stack"."updatedAt",
|
||||||
|
"asset_stack"."primaryAssetId",
|
||||||
|
"asset_stack"."ownerId",
|
||||||
|
"updateId"
|
||||||
|
from
|
||||||
|
"asset_stack"
|
||||||
|
where
|
||||||
|
"ownerId" in (
|
||||||
|
select
|
||||||
|
"sharedById"
|
||||||
|
from
|
||||||
|
"partners"
|
||||||
|
where
|
||||||
|
"sharedWithId" = $1
|
||||||
|
)
|
||||||
|
and "updatedAt" < now() - interval '1 millisecond'
|
||||||
|
order by
|
||||||
|
"updateId" asc
|
||||||
|
|
||||||
-- SyncRepository.stack.getDeletes
|
-- SyncRepository.stack.getDeletes
|
||||||
select
|
select
|
||||||
"id",
|
"id",
|
||||||
@ -703,11 +763,11 @@ order by
|
|||||||
|
|
||||||
-- SyncRepository.stack.getUpserts
|
-- SyncRepository.stack.getUpserts
|
||||||
select
|
select
|
||||||
"id",
|
"asset_stack"."id",
|
||||||
"createdAt",
|
"asset_stack"."createdAt",
|
||||||
"updatedAt",
|
"asset_stack"."updatedAt",
|
||||||
"primaryAssetId",
|
"asset_stack"."primaryAssetId",
|
||||||
"ownerId",
|
"asset_stack"."ownerId",
|
||||||
"updateId"
|
"updateId"
|
||||||
from
|
from
|
||||||
"asset_stack"
|
"asset_stack"
|
||||||
|
@ -41,6 +41,7 @@ export class SyncRepository {
|
|||||||
partner: PartnerSync;
|
partner: PartnerSync;
|
||||||
partnerAsset: PartnerAssetsSync;
|
partnerAsset: PartnerAssetsSync;
|
||||||
partnerAssetExif: PartnerAssetExifsSync;
|
partnerAssetExif: PartnerAssetExifsSync;
|
||||||
|
partnerStack: PartnerStackSync;
|
||||||
stack: StackSync;
|
stack: StackSync;
|
||||||
user: UserSync;
|
user: UserSync;
|
||||||
|
|
||||||
@ -57,6 +58,7 @@ export class SyncRepository {
|
|||||||
this.partner = new PartnerSync(this.db);
|
this.partner = new PartnerSync(this.db);
|
||||||
this.partnerAsset = new PartnerAssetsSync(this.db);
|
this.partnerAsset = new PartnerAssetsSync(this.db);
|
||||||
this.partnerAssetExif = new PartnerAssetExifsSync(this.db);
|
this.partnerAssetExif = new PartnerAssetExifsSync(this.db);
|
||||||
|
this.partnerStack = new PartnerStackSync(this.db);
|
||||||
this.stack = new StackSync(this.db);
|
this.stack = new StackSync(this.db);
|
||||||
this.user = new UserSync(this.db);
|
this.user = new UserSync(this.db);
|
||||||
}
|
}
|
||||||
@ -552,13 +554,54 @@ class StackSync extends BaseSync {
|
|||||||
getUpserts(userId: string, ack?: SyncAck) {
|
getUpserts(userId: string, ack?: SyncAck) {
|
||||||
return this.db
|
return this.db
|
||||||
.selectFrom('asset_stack')
|
.selectFrom('asset_stack')
|
||||||
.select(['id', 'createdAt', 'updatedAt', 'primaryAssetId', 'ownerId', 'updateId'])
|
.select(columns.syncStack)
|
||||||
|
.select('updateId')
|
||||||
.where('ownerId', '=', userId)
|
.where('ownerId', '=', userId)
|
||||||
.$call((qb) => this.upsertTableFilters(qb, ack))
|
.$call((qb) => this.upsertTableFilters(qb, ack))
|
||||||
.stream();
|
.stream();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class PartnerStackSync extends BaseSync {
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||||
|
getDeletes(userId: string, ack?: SyncAck) {
|
||||||
|
return this.db
|
||||||
|
.selectFrom('stacks_audit')
|
||||||
|
.select(['id', 'stackId'])
|
||||||
|
.where('userId', 'in', (eb) =>
|
||||||
|
eb.selectFrom('partners').select(['sharedById']).where('sharedWithId', '=', userId),
|
||||||
|
)
|
||||||
|
.$call((qb) => this.auditTableFilters(qb, ack))
|
||||||
|
.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID, DummyValue.UUID, DummyValue.UUID], stream: true })
|
||||||
|
getBackfill(partnerId: string, afterUpdateId: string | undefined, beforeUpdateId: string) {
|
||||||
|
return this.db
|
||||||
|
.selectFrom('asset_stack')
|
||||||
|
.select(columns.syncStack)
|
||||||
|
.select('updateId')
|
||||||
|
.where('ownerId', '=', partnerId)
|
||||||
|
.where('updatedAt', '<', sql.raw<Date>("now() - interval '1 millisecond'"))
|
||||||
|
.where('updateId', '<=', beforeUpdateId)
|
||||||
|
.$if(!!afterUpdateId, (eb) => eb.where('updateId', '>=', afterUpdateId!))
|
||||||
|
.orderBy('updateId', 'asc')
|
||||||
|
.stream();
|
||||||
|
}
|
||||||
|
|
||||||
|
@GenerateSql({ params: [DummyValue.UUID], stream: true })
|
||||||
|
getUpserts(userId: string, ack?: SyncAck) {
|
||||||
|
return this.db
|
||||||
|
.selectFrom('asset_stack')
|
||||||
|
.select(columns.syncStack)
|
||||||
|
.select('updateId')
|
||||||
|
.where('ownerId', 'in', (eb) =>
|
||||||
|
eb.selectFrom('partners').select(['sharedById']).where('sharedWithId', '=', userId),
|
||||||
|
)
|
||||||
|
.$call((qb) => this.upsertTableFilters(qb, ack))
|
||||||
|
.stream();
|
||||||
|
}
|
||||||
|
}
|
||||||
class UserSync extends BaseSync {
|
class UserSync extends BaseSync {
|
||||||
@GenerateSql({ params: [], stream: true })
|
@GenerateSql({ params: [], stream: true })
|
||||||
getDeletes(ack?: SyncAck) {
|
getDeletes(ack?: SyncAck) {
|
||||||
|
@ -59,6 +59,7 @@ export const SYNC_TYPES_ORDER = [
|
|||||||
SyncRequestType.AssetsV1,
|
SyncRequestType.AssetsV1,
|
||||||
SyncRequestType.StacksV1,
|
SyncRequestType.StacksV1,
|
||||||
SyncRequestType.PartnerAssetsV1,
|
SyncRequestType.PartnerAssetsV1,
|
||||||
|
SyncRequestType.PartnerStacksV1,
|
||||||
SyncRequestType.AlbumAssetsV1,
|
SyncRequestType.AlbumAssetsV1,
|
||||||
SyncRequestType.AlbumsV1,
|
SyncRequestType.AlbumsV1,
|
||||||
SyncRequestType.AlbumUsersV1,
|
SyncRequestType.AlbumUsersV1,
|
||||||
@ -139,6 +140,7 @@ export class SyncService extends BaseService {
|
|||||||
[SyncRequestType.MemoriesV1]: () => this.syncMemoriesV1(response, checkpointMap, auth),
|
[SyncRequestType.MemoriesV1]: () => this.syncMemoriesV1(response, checkpointMap, auth),
|
||||||
[SyncRequestType.MemoryToAssetsV1]: () => this.syncMemoryAssetsV1(response, checkpointMap, auth),
|
[SyncRequestType.MemoryToAssetsV1]: () => this.syncMemoryAssetsV1(response, checkpointMap, auth),
|
||||||
[SyncRequestType.StacksV1]: () => this.syncStackV1(response, checkpointMap, auth),
|
[SyncRequestType.StacksV1]: () => this.syncStackV1(response, checkpointMap, auth),
|
||||||
|
[SyncRequestType.PartnerStacksV1]: () => this.syncPartnerStackV1(response, checkpointMap, auth, sessionId),
|
||||||
};
|
};
|
||||||
|
|
||||||
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))) {
|
||||||
@ -526,6 +528,54 @@ export class SyncService extends BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async syncPartnerStackV1(response: Writable, checkpointMap: CheckpointMap, auth: AuthDto, sessionId: string) {
|
||||||
|
const deleteType = SyncEntityType.PartnerStackDeleteV1;
|
||||||
|
const deletes = this.syncRepository.partnerStack.getDeletes(auth.user.id, checkpointMap[deleteType]);
|
||||||
|
for await (const { id, ...data } of deletes) {
|
||||||
|
send(response, { type: deleteType, ids: [id], data });
|
||||||
|
}
|
||||||
|
|
||||||
|
const backfillType = SyncEntityType.PartnerStackBackfillV1;
|
||||||
|
const backfillCheckpoint = checkpointMap[backfillType];
|
||||||
|
const partners = await this.syncRepository.partner.getCreatedAfter(auth.user.id, backfillCheckpoint?.updateId);
|
||||||
|
const upsertType = SyncEntityType.PartnerStackV1;
|
||||||
|
const upsertCheckpoint = checkpointMap[upsertType];
|
||||||
|
if (upsertCheckpoint) {
|
||||||
|
const endId = upsertCheckpoint.updateId;
|
||||||
|
|
||||||
|
for (const partner of partners) {
|
||||||
|
const createId = partner.createId;
|
||||||
|
if (isEntityBackfillComplete(createId, backfillCheckpoint)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const startId = getStartId(createId, backfillCheckpoint);
|
||||||
|
const backfill = this.syncRepository.partnerStack.getBackfill(partner.sharedById, startId, endId);
|
||||||
|
|
||||||
|
for await (const { updateId, ...data } of backfill) {
|
||||||
|
send(response, {
|
||||||
|
type: backfillType,
|
||||||
|
ids: [createId, updateId],
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sendEntityBackfillCompleteAck(response, backfillType, createId);
|
||||||
|
}
|
||||||
|
} else if (partners.length > 0) {
|
||||||
|
await this.upsertBackfillCheckpoint({
|
||||||
|
type: backfillType,
|
||||||
|
sessionId,
|
||||||
|
createId: partners.at(-1)!.createId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const upserts = this.syncRepository.partnerStack.getUpserts(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.syncCheckpointRepository.upsertAll([
|
await this.syncCheckpointRepository.upsertAll([
|
||||||
|
232
server/test/medium/specs/sync/sync-partner-stack.spec.ts
Normal file
232
server/test/medium/specs/sync/sync-partner-stack.spec.ts
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
import { Kysely } from 'kysely';
|
||||||
|
import { SyncEntityType, SyncRequestType } from 'src/enum';
|
||||||
|
import { PartnerRepository } from 'src/repositories/partner.repository';
|
||||||
|
import { StackRepository } from 'src/repositories/stack.repository';
|
||||||
|
import { UserRepository } from 'src/repositories/user.repository';
|
||||||
|
import { DB } from 'src/schema';
|
||||||
|
import { SyncTestContext } from 'test/medium.factory';
|
||||||
|
import { factory } from 'test/small.factory';
|
||||||
|
import { getKyselyDB, wait } 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(SyncRequestType.PartnerStacksV1, () => {
|
||||||
|
it('should detect and sync the first partner stack', async () => {
|
||||||
|
const { auth, user, ctx } = await setup();
|
||||||
|
const { user: user2 } = await ctx.newUser();
|
||||||
|
await ctx.newPartner({ sharedById: user2.id, sharedWithId: user.id });
|
||||||
|
const { asset } = await ctx.newAsset({ ownerId: user2.id });
|
||||||
|
const { stack } = await ctx.newStack({ ownerId: user2.id }, [asset.id]);
|
||||||
|
|
||||||
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
|
expect(response).toHaveLength(1);
|
||||||
|
expect(response).toEqual([
|
||||||
|
{
|
||||||
|
ack: expect.any(String),
|
||||||
|
data: {
|
||||||
|
id: stack.id,
|
||||||
|
ownerId: stack.ownerId,
|
||||||
|
createdAt: (stack.createdAt as Date).toISOString(),
|
||||||
|
updatedAt: (stack.updatedAt as Date).toISOString(),
|
||||||
|
primaryAssetId: stack.primaryAssetId,
|
||||||
|
},
|
||||||
|
type: SyncEntityType.PartnerStackV1,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
await ctx.syncAckAll(auth, response);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should detect and sync a deleted partner stack', async () => {
|
||||||
|
const { auth, user, ctx } = await setup();
|
||||||
|
const stackRepo = ctx.get(StackRepository);
|
||||||
|
const { user: user2 } = await ctx.newUser();
|
||||||
|
await ctx.newPartner({ sharedById: user2.id, sharedWithId: user.id });
|
||||||
|
const { asset } = await ctx.newAsset({ ownerId: user2.id });
|
||||||
|
const { stack } = await ctx.newStack({ ownerId: user2.id }, [asset.id]);
|
||||||
|
await stackRepo.delete(stack.id);
|
||||||
|
|
||||||
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
|
expect(response).toHaveLength(1);
|
||||||
|
expect(response).toEqual([
|
||||||
|
{
|
||||||
|
ack: expect.stringContaining('PartnerStackDeleteV1'),
|
||||||
|
data: {
|
||||||
|
stackId: stack.id,
|
||||||
|
},
|
||||||
|
type: SyncEntityType.PartnerStackDeleteV1,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
await ctx.syncAckAll(auth, response);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not sync a deleted partner stack due to a user delete', async () => {
|
||||||
|
const { auth, user, ctx } = await setup();
|
||||||
|
const userRepo = ctx.get(UserRepository);
|
||||||
|
const { user: user2 } = await ctx.newUser();
|
||||||
|
await ctx.newPartner({ sharedById: user2.id, sharedWithId: user.id });
|
||||||
|
const { asset } = await ctx.newAsset({ ownerId: user2.id });
|
||||||
|
await ctx.newStack({ ownerId: user2.id }, [asset.id]);
|
||||||
|
await userRepo.delete({ id: user2.id }, true);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not sync a deleted partner stack due to a partner delete (unshare)', async () => {
|
||||||
|
const { auth, user, ctx } = await setup();
|
||||||
|
const partnerRepo = ctx.get(PartnerRepository);
|
||||||
|
const { user: user2 } = await ctx.newUser();
|
||||||
|
const { asset } = await ctx.newAsset({ ownerId: user2.id });
|
||||||
|
await ctx.newStack({ ownerId: user2.id }, [asset.id]);
|
||||||
|
const { partner } = await ctx.newPartner({ sharedById: user2.id, sharedWithId: user.id });
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toHaveLength(1);
|
||||||
|
await partnerRepo.remove(partner);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not sync a stack or stack delete for own user', async () => {
|
||||||
|
const { auth, user, ctx } = await setup();
|
||||||
|
const stackRepo = ctx.get(StackRepository);
|
||||||
|
const { user: user2 } = await ctx.newUser();
|
||||||
|
const { asset } = await ctx.newAsset({ ownerId: user.id });
|
||||||
|
const { stack } = await ctx.newStack({ ownerId: user.id }, [asset.id]);
|
||||||
|
await ctx.newPartner({ sharedById: user2.id, sharedWithId: user.id });
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toHaveLength(1);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toHaveLength(0);
|
||||||
|
await stackRepo.delete(stack.id);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.StacksV1])).resolves.toHaveLength(1);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not sync a stack or stack delete for unrelated user', async () => {
|
||||||
|
const { auth, ctx } = await setup();
|
||||||
|
const stackRepo = ctx.get(StackRepository);
|
||||||
|
const { user: user2 } = await ctx.newUser();
|
||||||
|
const { session } = await ctx.newSession({ userId: user2.id });
|
||||||
|
const { asset } = await ctx.newAsset({ ownerId: user2.id });
|
||||||
|
const { stack } = await ctx.newStack({ ownerId: user2.id }, [asset.id]);
|
||||||
|
const auth2 = factory.auth({ session, user: user2 });
|
||||||
|
|
||||||
|
await expect(ctx.syncStream(auth2, [SyncRequestType.StacksV1])).resolves.toHaveLength(1);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toHaveLength(0);
|
||||||
|
|
||||||
|
await stackRepo.delete(stack.id);
|
||||||
|
|
||||||
|
await expect(ctx.syncStream(auth2, [SyncRequestType.StacksV1])).resolves.toHaveLength(1);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toHaveLength(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should backfill partner stacks when a partner shared their library with you', async () => {
|
||||||
|
const { auth, user, ctx } = await setup();
|
||||||
|
const { user: user2 } = await ctx.newUser();
|
||||||
|
const { user: user3 } = await ctx.newUser();
|
||||||
|
const { asset: asset3 } = await ctx.newAsset({ ownerId: user3.id });
|
||||||
|
const { stack: stack3 } = await ctx.newStack({ ownerId: user3.id }, [asset3.id]);
|
||||||
|
await wait(2);
|
||||||
|
const { asset: asset2 } = await ctx.newAsset({ ownerId: user2.id });
|
||||||
|
const { stack: stack2 } = await ctx.newStack({ ownerId: user2.id }, [asset2.id]);
|
||||||
|
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
||||||
|
|
||||||
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
|
expect(response).toHaveLength(1);
|
||||||
|
expect(response).toEqual([
|
||||||
|
{
|
||||||
|
ack: expect.stringContaining('PartnerStackV1'),
|
||||||
|
data: expect.objectContaining({
|
||||||
|
id: stack2.id,
|
||||||
|
}),
|
||||||
|
type: SyncEntityType.PartnerStackV1,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
await ctx.syncAckAll(auth, response);
|
||||||
|
await ctx.newPartner({ sharedById: user3.id, sharedWithId: user.id });
|
||||||
|
|
||||||
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
|
expect(newResponse).toHaveLength(2);
|
||||||
|
expect(newResponse).toEqual([
|
||||||
|
{
|
||||||
|
ack: expect.stringContaining(SyncEntityType.PartnerStackBackfillV1),
|
||||||
|
data: expect.objectContaining({
|
||||||
|
id: stack3.id,
|
||||||
|
}),
|
||||||
|
type: SyncEntityType.PartnerStackBackfillV1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ack: expect.stringContaining(SyncEntityType.PartnerStackBackfillV1),
|
||||||
|
data: {},
|
||||||
|
type: SyncEntityType.SyncAckV1,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should only backfill partner stacks created prior to the current partner stack checkpoint', async () => {
|
||||||
|
const { auth, ctx } = await setup();
|
||||||
|
const { user: user2 } = await ctx.newUser();
|
||||||
|
const { user: user3 } = await ctx.newUser();
|
||||||
|
const { asset: asset3 } = await ctx.newAsset({ ownerId: user3.id });
|
||||||
|
const { stack: stack3 } = await ctx.newStack({ ownerId: user3.id }, [asset3.id]);
|
||||||
|
await wait(2);
|
||||||
|
const { asset: asset2 } = await ctx.newAsset({ ownerId: user2.id });
|
||||||
|
const { stack: stack2 } = await ctx.newStack({ ownerId: user2.id }, [asset2.id]);
|
||||||
|
await wait(2);
|
||||||
|
const { asset: asset4 } = await ctx.newAsset({ ownerId: user3.id });
|
||||||
|
const { stack: stack4 } = await ctx.newStack({ ownerId: user3.id }, [asset4.id]);
|
||||||
|
await ctx.newPartner({ sharedById: user2.id, sharedWithId: auth.user.id });
|
||||||
|
|
||||||
|
const response = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
|
expect(response).toHaveLength(1);
|
||||||
|
expect(response).toEqual([
|
||||||
|
{
|
||||||
|
ack: expect.stringContaining(SyncEntityType.PartnerStackV1),
|
||||||
|
data: expect.objectContaining({
|
||||||
|
id: stack2.id,
|
||||||
|
}),
|
||||||
|
type: SyncEntityType.PartnerStackV1,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
await ctx.syncAckAll(auth, response);
|
||||||
|
|
||||||
|
await ctx.newPartner({ sharedById: user3.id, sharedWithId: auth.user.id });
|
||||||
|
const newResponse = await ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1]);
|
||||||
|
expect(newResponse).toHaveLength(3);
|
||||||
|
expect(newResponse).toEqual([
|
||||||
|
{
|
||||||
|
ack: expect.any(String),
|
||||||
|
data: expect.objectContaining({
|
||||||
|
id: stack3.id,
|
||||||
|
}),
|
||||||
|
type: SyncEntityType.PartnerStackBackfillV1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ack: expect.stringContaining(SyncEntityType.PartnerStackBackfillV1),
|
||||||
|
data: {},
|
||||||
|
type: SyncEntityType.SyncAckV1,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ack: expect.any(String),
|
||||||
|
data: expect.objectContaining({
|
||||||
|
id: stack4.id,
|
||||||
|
}),
|
||||||
|
type: SyncEntityType.PartnerStackV1,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
await ctx.syncAckAll(auth, newResponse);
|
||||||
|
await expect(ctx.syncStream(auth, [SyncRequestType.PartnerStacksV1])).resolves.toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
Loading…
x
Reference in New Issue
Block a user