-
Notifications
You must be signed in to change notification settings - Fork 35
/
RawEntityDereferencer.kt
90 lines (80 loc) · 3.25 KB
/
RawEntityDereferencer.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/*
* Copyright 2020 Google LLC.
*
* This code may only be used under the BSD style license found at
* http://polymer.github.io/LICENSE.txt
*
* Code distributed by Google as part of this project is also subject to an additional IP rights
* grant found at
* http://polymer.github.io/PATENTS.txt
*/
package arcs.core.storage.handle
import arcs.core.crdt.CrdtEntity
import arcs.core.data.EntityType
import arcs.core.data.RawEntity
import arcs.core.data.Schema
import arcs.core.storage.Dereferencer
import arcs.core.storage.EntityActivationFactory
import arcs.core.storage.ProxyCallback
import arcs.core.storage.ProxyMessage
import arcs.core.storage.Reference
import arcs.core.storage.StorageMode
import arcs.core.storage.Store
import arcs.core.storage.StoreOptions
import arcs.core.util.TaggedLog
import kotlin.coroutines.CoroutineContext
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/**
* [Dereferencer] to use when de-referencing a [Reference] to an [Entity].
*
* [Handle] implementations should inject this into any [Reference] objects they encounter.
*/
class RawEntityDereferencer(
private val schema: Schema,
private val entityActivationFactory: EntityActivationFactory? = null
) : Dereferencer<RawEntity> {
private val log = TaggedLog { "Dereferencer(${schema.names})" }
override suspend fun dereference(
reference: Reference,
coroutineContext: CoroutineContext
): RawEntity? = withContext(coroutineContext) {
log.debug { "De-referencing $reference" }
val storageKey = reference.storageKey.childKeyWithComponent(reference.id)
val options = StoreOptions<CrdtEntity.Data, CrdtEntity.Operation, RawEntity>(
storageKey,
EntityType(schema),
StorageMode.Direct
)
val store = Store(options).activate(entityActivationFactory)
val deferred = CompletableDeferred<RawEntity>()
var token = -1
token = store.on(
ProxyCallback { message ->
when (message) {
is ProxyMessage.ModelUpdate<*, *, *> -> {
deferred.complete((message.model as CrdtEntity.Data).toRawEntity())
store.off(token)
}
is ProxyMessage.SyncRequest -> Unit
is ProxyMessage.Operations -> Unit
}
true
}
)
launch { store.onProxyMessage(ProxyMessage.SyncRequest(token)) }
// Only return the item if we've actually managed to pull it out of the database.
deferred.await().takeIf { it matches schema }?.copy(id = reference.id)
}
}
/* internal */
infix fun RawEntity.matches(schema: Schema): Boolean {
// Only allow empty to match if the Schema is also empty.
// TODO: Is this a correct assumption?
if (singletons.isEmpty() && collections.isEmpty())
return schema.fields.singletons.isEmpty() && schema.fields.collections.isEmpty()
// Return true if any of the RawEntity's fields are part of the Schema.
return (singletons.isEmpty() || singletons.keys.any { it in schema.fields.singletons }) &&
(collections.isEmpty() || collections.keys.any { it in schema.fields.collections })
}