github.com/altipla-consulting/ravendb-go-client@v0.1.3/document_info.go (about) 1 package ravendb 2 3 import "reflect" 4 5 // ConcurrencyCheckMode describes concurrency check 6 type ConcurrencyCheckMode int 7 8 const ( 9 // ConcurrencyCheckAuto is automatic optimistic concurrency check depending on UseOptimisticConcurrency setting or provided Change Vector 10 ConcurrencyCheckAuto ConcurrencyCheckMode = iota 11 // ConcurrencyCheckForced forces optimistic concurrency check even if UseOptimisticConcurrency is not set 12 ConcurrencyCheckForced 13 // ConcurrencyCheckDisabled disables optimistic concurrency check even if UseOptimisticConcurrency is set 14 ConcurrencyCheckDisabled 15 ) 16 17 // documentInfo stores information about entity in a session 18 // TODO: maybe route all places where we compare enity for equality via 19 // documentInfo.Equal(other interface{}), so that we can catch 20 // mismatches of *struct and **struct 21 type documentInfo struct { 22 id string 23 changeVector *string 24 concurrencyCheckMode ConcurrencyCheckMode 25 ignoreChanges bool 26 metadata map[string]interface{} 27 document map[string]interface{} 28 metadataInstance *MetadataAsDictionary 29 entity interface{} 30 newDocument bool 31 collection string 32 } 33 34 // we want to route assignments to entity through this functions 35 // so that we can maintain invariant that entity is *struct (and 36 // not, e.g., **struct). It's hard to track the difference between 37 // *struct and **struct otherwise 38 func (d *documentInfo) setEntity(value interface{}) { 39 // TODO: maybe also support value of type *map[string]interface{}? 40 if _, ok := value.(map[string]interface{}); ok { 41 d.entity = value 42 return 43 } 44 45 tp := reflect.TypeOf(value) 46 if tp.Kind() == reflect.Struct { 47 panicIf(true, "trying to set struct %T", value) 48 d.entity = value 49 } 50 51 if tp.Kind() != reflect.Ptr || tp.Elem() == nil { 52 panicIf(tp.Kind() != reflect.Ptr || tp.Elem() == nil, "expected value to be *struct or **struct, is %T", value) 53 d.entity = value 54 return 55 } 56 tp = tp.Elem() 57 if tp.Kind() == reflect.Struct { 58 // if it's *struct, just assign 59 d.entity = value 60 return 61 } 62 if tp.Kind() != reflect.Ptr || tp.Elem() == nil || tp.Elem().Kind() != reflect.Struct { 63 //panicIf(tp.Kind() != reflect.Ptr || tp.Elem() == nil || tp.Elem().Kind() != reflect.Struct, "expected value to be *struct or **struct, is %T", value) 64 //TODO: re-enable this panic and fix places that trigger it 65 d.entity = value 66 return 67 68 } 69 // it's **struct, so extract *struct 70 rv := reflect.ValueOf(value) 71 rv = rv.Elem() // it's *struct now 72 d.entity = rv.Interface() 73 } 74 75 func getNewDocumentInfo(document map[string]interface{}) *documentInfo { 76 metadataV, ok := document[MetadataKey] 77 // TODO: maybe convert to errors 78 panicIf(!ok, "Document must have a metadata") 79 metadata, ok := metadataV.(map[string]interface{}) 80 panicIf(!ok, "Document metadata is not a valid type %T", metadataV) 81 82 // TODO: return an error? 83 84 id, ok := jsonGetAsText(metadata, MetadataID) 85 // TODO: return an error? 86 panicIf(!ok || id == "", "Document must have an id") 87 88 changeVector := jsonGetAsTextPointer(metadata, MetadataChangeVector) 89 // TODO: return an error? 90 panicIf(changeVector == nil, "Document must have a Change Vector") 91 92 newDocumentInfo := &documentInfo{} 93 newDocumentInfo.id = id 94 newDocumentInfo.document = document 95 newDocumentInfo.metadata = metadata 96 97 newDocumentInfo.changeVector = changeVector 98 return newDocumentInfo 99 }