forked from ravendb/ravendb-go-client
-
Notifications
You must be signed in to change notification settings - Fork 0
/
document_info.go
99 lines (85 loc) · 3.32 KB
/
document_info.go
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
91
92
93
94
95
96
97
98
99
package ravendb
import "reflect"
// ConcurrencyCheckMode describes concurrency check
type ConcurrencyCheckMode int
const (
// ConcurrencyCheckAuto is automatic optimistic concurrency check depending on UseOptimisticConcurrency setting or provided Change Vector
ConcurrencyCheckAuto ConcurrencyCheckMode = iota
// ConcurrencyCheckForced forces optimistic concurrency check even if UseOptimisticConcurrency is not set
ConcurrencyCheckForced
// ConcurrencyCheckDisabled disables optimistic concurrency check even if UseOptimisticConcurrency is set
ConcurrencyCheckDisabled
)
// documentInfo stores information about entity in a session
// TODO: maybe route all places where we compare enity for equality via
// documentInfo.Equal(other interface{}), so that we can catch
// mismatches of *struct and **struct
type documentInfo struct {
id string
changeVector *string
concurrencyCheckMode ConcurrencyCheckMode
ignoreChanges bool
metadata map[string]interface{}
document map[string]interface{}
metadataInstance *MetadataAsDictionary
entity interface{}
newDocument bool
collection string
}
// we want to route assignments to entity through this functions
// so that we can maintain invariant that entity is *struct (and
// not, e.g., **struct). It's hard to track the difference between
// *struct and **struct otherwise
func (d *documentInfo) setEntity(value interface{}) {
// TODO: maybe also support value of type *map[string]interface{}?
if _, ok := value.(map[string]interface{}); ok {
d.entity = value
return
}
tp := reflect.TypeOf(value)
if tp.Kind() == reflect.Struct {
panicIf(true, "trying to set struct %T", value)
d.entity = value
}
if tp.Kind() != reflect.Ptr || tp.Elem() == nil {
panicIf(tp.Kind() != reflect.Ptr || tp.Elem() == nil, "expected value to be *struct or **struct, is %T", value)
d.entity = value
return
}
tp = tp.Elem()
if tp.Kind() == reflect.Struct {
// if it's *struct, just assign
d.entity = value
return
}
if tp.Kind() != reflect.Ptr || tp.Elem() == nil || tp.Elem().Kind() != reflect.Struct {
//panicIf(tp.Kind() != reflect.Ptr || tp.Elem() == nil || tp.Elem().Kind() != reflect.Struct, "expected value to be *struct or **struct, is %T", value)
//TODO: re-enable this panic and fix places that trigger it
d.entity = value
return
}
// it's **struct, so extract *struct
rv := reflect.ValueOf(value)
rv = rv.Elem() // it's *struct now
d.entity = rv.Interface()
}
func getNewDocumentInfo(document map[string]interface{}) *documentInfo {
metadataV, ok := document[MetadataKey]
// TODO: maybe convert to errors
panicIf(!ok, "Document must have a metadata")
metadata, ok := metadataV.(map[string]interface{})
panicIf(!ok, "Document metadata is not a valid type %T", metadataV)
// TODO: return an error?
id, ok := jsonGetAsText(metadata, MetadataID)
// TODO: return an error?
panicIf(!ok || id == "", "Document must have an id")
changeVector := jsonGetAsTextPointer(metadata, MetadataChangeVector)
// TODO: return an error?
panicIf(changeVector == nil, "Document must have a Change Vector")
newDocumentInfo := &documentInfo{}
newDocumentInfo.id = id
newDocumentInfo.document = document
newDocumentInfo.metadata = metadata
newDocumentInfo.changeVector = changeVector
return newDocumentInfo
}