2023-01-17 12:21:29 +01:00
|
|
|
import {BlobElementEntity, ElementEntity, ListElementEntity, SomeEntity, TypeModel} from "../../common/EntityTypes.js"
|
2022-12-27 15:37:40 +01:00
|
|
|
import { EntityRestClient, typeRefToPath } from "./EntityRestClient.js"
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
import { firstBiggerThanSecond, getElementId, getListId } from "../../common/utils/EntityUtils.js"
|
2022-12-27 15:37:40 +01:00
|
|
|
import { CacheStorage, LastUpdateTime } from "./DefaultEntityRestCache.js"
|
|
|
|
|
import { assertNotNull, clone, getFromMap, remove, TypeRef } from "@tutao/tutanota-utils"
|
|
|
|
|
import { CustomCacheHandlerMap } from "./CustomCacheHandler.js"
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
import { resolveTypeReference } from "../../common/EntityFunctions.js"
|
|
|
|
|
import { Type as TypeId } from "../../common/EntityConstants.js"
|
2023-01-12 16:48:28 +01:00
|
|
|
import { ProgrammingError } from "../../common/error/ProgrammingError.js"
|
2022-01-12 14:43:01 +01:00
|
|
|
|
2022-10-21 15:53:39 +02:00
|
|
|
/** Cache for a single list. */
|
|
|
|
|
type ListCache = {
|
2022-01-12 14:43:01 +01:00
|
|
|
/** All entities loaded inside the range. */
|
2022-12-27 15:37:40 +01:00
|
|
|
allRange: Id[]
|
|
|
|
|
lowerRangeId: Id
|
|
|
|
|
upperRangeId: Id
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
/** All the entities loaded, inside or outside the range (e.g. load for a single entity). */
|
2022-01-12 14:43:01 +01:00
|
|
|
elements: Map<Id, ListElementEntity>
|
2022-10-21 15:53:39 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Map from list id to list cache. */
|
|
|
|
|
type ListTypeCache = Map<Id, ListCache>
|
|
|
|
|
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
type BlobElementCache = {
|
|
|
|
|
/** All the entities loaded, inside or outside the range (e.g. load for a single entity). */
|
|
|
|
|
elements: Map<Id, BlobElementEntity>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Map from list id to list cache. */
|
|
|
|
|
type BlobElementTypeCache = Map<Id, BlobElementCache>
|
|
|
|
|
|
2022-10-21 15:53:39 +02:00
|
|
|
export interface EphemeralStorageInitArgs {
|
2022-12-27 15:37:40 +01:00
|
|
|
userId: Id
|
2022-10-21 15:53:39 +02:00
|
|
|
}
|
2022-01-12 14:43:01 +01:00
|
|
|
|
|
|
|
|
export class EphemeralCacheStorage implements CacheStorage {
|
2022-10-21 15:53:39 +02:00
|
|
|
/** Path to id to entity map. */
|
2022-01-12 14:43:01 +01:00
|
|
|
private readonly entities: Map<string, Map<Id, ElementEntity>> = new Map()
|
|
|
|
|
private readonly lists: Map<string, ListTypeCache> = new Map()
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
private readonly blobEntities: Map<string, BlobElementTypeCache> = new Map()
|
2022-07-04 14:55:17 +02:00
|
|
|
private readonly customCacheHandlerMap: CustomCacheHandlerMap = new CustomCacheHandlerMap()
|
2022-02-28 17:23:22 +01:00
|
|
|
private lastUpdateTime: number | null = null
|
2022-10-21 15:53:39 +02:00
|
|
|
private userId: Id | null = null
|
2022-11-03 11:04:26 +01:00
|
|
|
private lastBatchIdPerGroup = new Map<Id, Id>()
|
2022-10-21 15:53:39 +02:00
|
|
|
|
2022-12-27 15:37:40 +01:00
|
|
|
init({ userId }: EphemeralStorageInitArgs) {
|
2022-10-21 15:53:39 +02:00
|
|
|
this.userId = userId
|
|
|
|
|
}
|
2022-01-12 14:43:01 +01:00
|
|
|
|
2022-07-20 15:28:38 +02:00
|
|
|
deinit() {
|
2022-10-21 15:53:39 +02:00
|
|
|
this.userId = null
|
2022-07-20 15:28:38 +02:00
|
|
|
this.entities.clear()
|
|
|
|
|
this.lists.clear()
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
this.blobEntities.clear()
|
2022-07-20 15:28:38 +02:00
|
|
|
this.lastUpdateTime = null
|
2022-11-03 11:04:26 +01:00
|
|
|
this.lastBatchIdPerGroup.clear()
|
2022-07-20 15:28:38 +02:00
|
|
|
}
|
|
|
|
|
|
2022-01-12 14:43:01 +01:00
|
|
|
/**
|
|
|
|
|
* Get a given entity from the cache, expects that you have already checked for existence
|
|
|
|
|
*/
|
|
|
|
|
async get<T extends SomeEntity>(typeRef: TypeRef<T>, listId: Id | null, id: Id): Promise<T | null> {
|
|
|
|
|
// We downcast because we can't prove that map has correct entity on the type level
|
|
|
|
|
const path = typeRefToPath(typeRef)
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
const typeModel = await resolveTypeReference(typeRef)
|
|
|
|
|
switch (typeModel.type) {
|
2023-01-12 16:48:28 +01:00
|
|
|
case TypeId.Element:
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
return clone((this.entities.get(path)?.get(id) as T | undefined) ?? null)
|
2023-01-12 16:48:28 +01:00
|
|
|
case TypeId.ListElement:
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
return clone((this.lists.get(path)?.get(assertNotNull(listId))?.elements.get(id) as T | undefined) ?? null)
|
2023-01-12 16:48:28 +01:00
|
|
|
case TypeId.BlobElement:
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
return clone((this.blobEntities.get(path)?.get(assertNotNull(listId))?.elements.get(id) as T | undefined) ?? null)
|
|
|
|
|
default:
|
2023-01-12 16:48:28 +01:00
|
|
|
throw new ProgrammingError("must be a persistent type")
|
2022-01-12 14:43:01 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async deleteIfExists<T>(typeRef: TypeRef<T>, listId: Id | null, id: Id): Promise<void> {
|
|
|
|
|
const path = typeRefToPath(typeRef)
|
2023-01-17 12:21:29 +01:00
|
|
|
let typeModel: TypeModel
|
|
|
|
|
try {
|
|
|
|
|
typeModel = await resolveTypeReference(typeRef)
|
|
|
|
|
} catch (e) {
|
|
|
|
|
// prevent failed lookup for BlobToFileMapping - this catch block can be removed after May 2023
|
|
|
|
|
console.log("couldn't resolve typeRef ", typeRef)
|
|
|
|
|
return
|
|
|
|
|
}
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
switch (typeModel.type) {
|
2023-01-12 16:48:28 +01:00
|
|
|
case TypeId.Element:
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
this.entities.get(path)?.delete(id)
|
|
|
|
|
break
|
2023-01-12 16:48:28 +01:00
|
|
|
case TypeId.ListElement:
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
const cache = this.lists.get(path)?.get(assertNotNull(listId))
|
|
|
|
|
if (cache != null) {
|
|
|
|
|
cache.elements.delete(id)
|
|
|
|
|
remove(cache.allRange, id)
|
|
|
|
|
}
|
|
|
|
|
break
|
2023-01-12 16:48:28 +01:00
|
|
|
case TypeId.BlobElement:
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
this.blobEntities.get(path)?.get(assertNotNull(listId))?.elements.delete(id)
|
|
|
|
|
break
|
|
|
|
|
default:
|
2023-01-12 16:48:28 +01:00
|
|
|
throw new ProgrammingError("must be a persistent type")
|
2022-01-12 14:43:01 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private addElementEntity<T extends ElementEntity>(typeRef: TypeRef<T>, id: Id, entity: T) {
|
|
|
|
|
getFromMap(this.entities, typeRefToPath(typeRef), () => new Map()).set(id, entity)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async isElementIdInCacheRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, id: Id): Promise<boolean> {
|
|
|
|
|
const cache = this.lists.get(typeRefToPath(typeRef))?.get(listId)
|
2022-12-27 15:37:40 +01:00
|
|
|
return cache != null && !firstBiggerThanSecond(id, cache.upperRangeId) && !firstBiggerThanSecond(cache.lowerRangeId, id)
|
2022-01-12 14:43:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async put(originalEntity: SomeEntity): Promise<void> {
|
|
|
|
|
const entity = clone(originalEntity)
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
const typeRef = entity._type
|
|
|
|
|
const typeModel = await resolveTypeReference(typeRef)
|
|
|
|
|
switch (typeModel.type) {
|
2023-01-12 16:48:28 +01:00
|
|
|
case TypeId.Element:
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
const elementEntity = entity as ElementEntity
|
|
|
|
|
this.addElementEntity(elementEntity._type, elementEntity._id, elementEntity)
|
|
|
|
|
break
|
2023-01-12 16:48:28 +01:00
|
|
|
case TypeId.ListElement:
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
const listElementEntity = entity as ListElementEntity
|
|
|
|
|
const listElementTypeRef = typeRef as TypeRef<ListElementEntity>
|
|
|
|
|
await this.putListElement(listElementEntity, listElementTypeRef)
|
|
|
|
|
break
|
2023-01-12 16:48:28 +01:00
|
|
|
case TypeId.BlobElement:
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
const blobElementEntity = entity as BlobElementEntity
|
|
|
|
|
const blobTypeRef = typeRef as TypeRef<BlobElementEntity>
|
|
|
|
|
await this.putBlobElement(blobElementEntity, blobTypeRef)
|
|
|
|
|
break
|
|
|
|
|
default:
|
2023-01-12 16:48:28 +01:00
|
|
|
throw new ProgrammingError("must be a persistent type")
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async putBlobElement(entity: BlobElementEntity, typeRef: TypeRef<BlobElementEntity>) {
|
|
|
|
|
const listId = getListId(entity)
|
|
|
|
|
const elementId = getElementId(entity)
|
|
|
|
|
const cache = this.blobEntities.get(typeRefToPath(typeRef))?.get(listId)
|
|
|
|
|
if (cache == null) {
|
|
|
|
|
// first element in this list
|
|
|
|
|
const newCache = {
|
|
|
|
|
elements: new Map([[elementId, entity]]),
|
|
|
|
|
}
|
|
|
|
|
getFromMap(this.blobEntities, typeRefToPath(typeRef), () => new Map()).set(listId, newCache)
|
2022-01-12 14:43:01 +01:00
|
|
|
} else {
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
// if the element already exists in the cache, overwrite it
|
|
|
|
|
cache.elements.set(elementId, entity)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private async putListElement(entity: ListElementEntity, typeRef: TypeRef<ListElementEntity>) {
|
|
|
|
|
const listId = getListId(entity)
|
|
|
|
|
const elementId = getElementId(entity)
|
|
|
|
|
const cache = this.lists.get(typeRefToPath(typeRef))?.get(listId)
|
|
|
|
|
if (cache == null) {
|
|
|
|
|
// first element in this list
|
|
|
|
|
const newCache = {
|
|
|
|
|
allRange: [elementId],
|
|
|
|
|
lowerRangeId: elementId,
|
|
|
|
|
upperRangeId: elementId,
|
|
|
|
|
elements: new Map([[elementId, entity]]),
|
|
|
|
|
}
|
|
|
|
|
getFromMap(this.lists, typeRefToPath(typeRef), () => new Map()).set(listId, newCache)
|
|
|
|
|
} else {
|
|
|
|
|
// if the element already exists in the cache, overwrite it
|
|
|
|
|
// add new element to existing list if necessary
|
|
|
|
|
cache.elements.set(elementId, entity)
|
|
|
|
|
if (await this.isElementIdInCacheRange(typeRef, listId, elementId)) {
|
|
|
|
|
this.insertIntoRange(cache.allRange, elementId)
|
2022-01-12 14:43:01 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
private insertIntoRange(allRange: Array<Id>, elementId: Id) {
|
2022-01-12 14:43:01 +01:00
|
|
|
for (let i = 0; i < allRange.length; i++) {
|
|
|
|
|
const rangeElement = allRange[i]
|
|
|
|
|
if (firstBiggerThanSecond(rangeElement, elementId)) {
|
|
|
|
|
allRange.splice(i, 0, elementId)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (rangeElement === elementId) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
allRange.push(elementId)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async provideFromRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, start: Id, count: number, reverse: boolean): Promise<T[]> {
|
|
|
|
|
const listCache = this.lists.get(typeRefToPath(typeRef))?.get(listId)
|
|
|
|
|
|
|
|
|
|
if (listCache == null) {
|
|
|
|
|
return []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let range = listCache.allRange
|
|
|
|
|
let ids: Id[] = []
|
|
|
|
|
if (reverse) {
|
|
|
|
|
let i
|
|
|
|
|
for (i = range.length - 1; i >= 0; i--) {
|
|
|
|
|
if (firstBiggerThanSecond(start, range[i])) {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (i >= 0) {
|
|
|
|
|
let startIndex = i + 1 - count
|
2022-12-27 15:37:40 +01:00
|
|
|
if (startIndex < 0) {
|
|
|
|
|
// start index may be negative if more elements have been requested than available when getting elements reverse.
|
2022-01-12 14:43:01 +01:00
|
|
|
startIndex = 0
|
|
|
|
|
}
|
|
|
|
|
ids = range.slice(startIndex, i + 1)
|
|
|
|
|
ids.reverse()
|
|
|
|
|
} else {
|
|
|
|
|
ids = []
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2022-12-27 15:37:40 +01:00
|
|
|
const i = range.findIndex((id) => firstBiggerThanSecond(id, start))
|
2022-01-12 14:43:01 +01:00
|
|
|
ids = range.slice(i, i + count)
|
|
|
|
|
}
|
|
|
|
|
let result: T[] = []
|
|
|
|
|
for (let a = 0; a < ids.length; a++) {
|
2022-12-27 15:37:40 +01:00
|
|
|
result.push(clone(listCache.elements.get(ids[a]) as T))
|
2022-01-12 14:43:01 +01:00
|
|
|
}
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-27 15:37:40 +01:00
|
|
|
async getRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id): Promise<{ lower: Id; upper: Id } | null> {
|
2022-01-12 14:43:01 +01:00
|
|
|
const listCache = this.lists.get(typeRefToPath(typeRef))?.get(listId)
|
|
|
|
|
|
|
|
|
|
if (listCache == null) {
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-27 15:37:40 +01:00
|
|
|
return { lower: listCache.lowerRangeId, upper: listCache.upperRangeId }
|
2022-01-12 14:43:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async setUpperRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, id: Id): Promise<void> {
|
|
|
|
|
const listCache = this.lists.get(typeRefToPath(typeRef))?.get(listId)
|
|
|
|
|
if (listCache == null) {
|
|
|
|
|
throw new Error("list does not exist")
|
|
|
|
|
}
|
|
|
|
|
listCache.upperRangeId = id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async setLowerRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, id: Id): Promise<void> {
|
|
|
|
|
const listCache = this.lists.get(typeRefToPath(typeRef))?.get(listId)
|
|
|
|
|
if (listCache == null) {
|
|
|
|
|
throw new Error("list does not exist")
|
|
|
|
|
}
|
|
|
|
|
listCache.lowerRangeId = id
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Creates a new list cache if there is none. Resets everything but elements.
|
|
|
|
|
* @param typeRef
|
|
|
|
|
* @param listId
|
|
|
|
|
* @param lower
|
|
|
|
|
* @param upper
|
|
|
|
|
*/
|
|
|
|
|
async setNewRangeForList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id, lower: Id, upper: Id): Promise<void> {
|
|
|
|
|
const listCache = this.lists.get(typeRefToPath(typeRef))?.get(listId)
|
|
|
|
|
if (listCache == null) {
|
|
|
|
|
getFromMap(this.lists, typeRefToPath(typeRef), () => new Map()).set(listId, {
|
|
|
|
|
allRange: [],
|
|
|
|
|
lowerRangeId: lower,
|
|
|
|
|
upperRangeId: upper,
|
2022-12-27 15:37:40 +01:00
|
|
|
elements: new Map(),
|
2022-01-12 14:43:01 +01:00
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
listCache.lowerRangeId = lower
|
|
|
|
|
listCache.upperRangeId = upper
|
|
|
|
|
listCache.allRange = []
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async getIdsInRange<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id): Promise<Array<Id>> {
|
|
|
|
|
return this.lists.get(typeRefToPath(typeRef))?.get(listId)?.allRange ?? []
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-03 11:04:26 +01:00
|
|
|
async getLastBatchIdForGroup(groupId: Id): Promise<Id | null> {
|
|
|
|
|
return this.lastBatchIdPerGroup.get(groupId) ?? null
|
2022-01-12 14:43:01 +01:00
|
|
|
}
|
|
|
|
|
|
2022-11-03 11:04:26 +01:00
|
|
|
async putLastBatchIdForGroup(groupId: Id, batchId: Id): Promise<void> {
|
|
|
|
|
this.lastBatchIdPerGroup.set(groupId, batchId)
|
2022-01-12 14:43:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
purgeStorage(): Promise<void> {
|
2022-12-27 15:37:40 +01:00
|
|
|
return Promise.resolve()
|
2022-01-12 14:43:01 +01:00
|
|
|
}
|
|
|
|
|
|
2022-09-07 17:09:25 +02:00
|
|
|
async getLastUpdateTime(): Promise<LastUpdateTime> {
|
2022-12-27 15:37:40 +01:00
|
|
|
return this.lastUpdateTime ? { type: "recorded", time: this.lastUpdateTime } : { type: "never" }
|
2022-01-12 14:43:01 +01:00
|
|
|
}
|
|
|
|
|
|
2022-02-28 17:23:22 +01:00
|
|
|
async putLastUpdateTime(value: number): Promise<void> {
|
|
|
|
|
this.lastUpdateTime = value
|
2022-01-12 14:43:01 +01:00
|
|
|
}
|
2022-05-24 18:31:01 +02:00
|
|
|
|
|
|
|
|
async getWholeList<T extends ListElementEntity>(typeRef: TypeRef<T>, listId: Id): Promise<Array<T>> {
|
|
|
|
|
const listCache = this.lists.get(typeRefToPath(typeRef))?.get(listId)
|
|
|
|
|
|
|
|
|
|
if (listCache == null) {
|
|
|
|
|
return []
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-27 15:37:40 +01:00
|
|
|
return listCache.allRange.map((id) => clone(listCache.elements.get(id) as T))
|
2022-05-24 18:31:01 +02:00
|
|
|
}
|
2022-07-04 14:55:17 +02:00
|
|
|
|
|
|
|
|
getCustomCacheHandlerMap(entityRestClient: EntityRestClient): CustomCacheHandlerMap {
|
|
|
|
|
return this.customCacheHandlerMap
|
|
|
|
|
}
|
2022-10-21 15:53:39 +02:00
|
|
|
|
|
|
|
|
getUserId(): Id {
|
|
|
|
|
return assertNotNull(this.userId, "No user id, not initialized?")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async deleteAllOwnedBy(owner: Id): Promise<void> {
|
|
|
|
|
for (const typeMap of this.entities.values()) {
|
|
|
|
|
for (const [id, entity] of typeMap.entries()) {
|
|
|
|
|
if (entity._ownerGroup === owner) {
|
|
|
|
|
typeMap.delete(id)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (const cacheForType of this.lists.values()) {
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
this.deleteAllOwnedByFromCache(cacheForType, owner)
|
|
|
|
|
}
|
|
|
|
|
for (const cacheForType of this.blobEntities.values()) {
|
|
|
|
|
this.deleteAllOwnedByFromCache(cacheForType, owner)
|
|
|
|
|
}
|
|
|
|
|
this.lastBatchIdPerGroup.delete(owner)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private deleteAllOwnedByFromCache(cacheForType: Map<Id, ListCache | BlobElementCache>, owner: string) {
|
|
|
|
|
// If we find at least one element in the list that is owned by our target owner, we delete the entire list.
|
|
|
|
|
// This is OK in most cases because the vast majority of lists are single owner.
|
|
|
|
|
// For the other cases, we are just clearing the cache a bit sooner than needed.
|
|
|
|
|
const listIdsToDelete: string[] = []
|
|
|
|
|
for (const [listId, listCache] of cacheForType.entries()) {
|
|
|
|
|
for (const [id, element] of listCache.elements.entries()) {
|
|
|
|
|
if (element._ownerGroup === owner) {
|
|
|
|
|
listIdsToDelete.push(listId)
|
|
|
|
|
break
|
2022-10-21 15:53:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
add MailDetails feature, #4719
server issues: 1276, 1271, 1279, 1272, 1270, 1258, 1254, 1253, 1242, 1241
2022-11-03 19:03:54 +01:00
|
|
|
for (const listId of listIdsToDelete) {
|
|
|
|
|
cacheForType.delete(listId)
|
|
|
|
|
}
|
2022-10-21 15:53:39 +02:00
|
|
|
}
|
2022-11-28 17:38:17 +01:00
|
|
|
|
|
|
|
|
clearExcludedData(): Promise<void> {
|
|
|
|
|
return Promise.resolve()
|
|
|
|
|
}
|
2022-11-30 17:15:08 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* We want to lock the access to the "ranges" db when updating / reading the
|
|
|
|
|
* offline available mail list ranges for each mail list (referenced using the listId)
|
|
|
|
|
* @param listId the mail list that we want to lock
|
|
|
|
|
*/
|
|
|
|
|
lockRangesDbAccess(listId: string): Promise<void> {
|
|
|
|
|
return Promise.resolve()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* This is the counterpart to the function "lockRangesDbAccess(listId)"
|
|
|
|
|
* @param listId the mail list that we want to unlock
|
|
|
|
|
*/
|
|
|
|
|
unlockRangesDbAccess(listId: string): Promise<void> {
|
|
|
|
|
return Promise.resolve()
|
|
|
|
|
}
|
2022-12-27 15:37:40 +01:00
|
|
|
}
|