You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

323 lines
10 KiB

  1. // Copyright 2017 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package firestore
  15. import (
  16. "errors"
  17. "fmt"
  18. "reflect"
  19. "time"
  20. "github.com/golang/protobuf/ptypes"
  21. tspb "github.com/golang/protobuf/ptypes/timestamp"
  22. pb "google.golang.org/genproto/googleapis/firestore/v1"
  23. "google.golang.org/grpc/codes"
  24. "google.golang.org/grpc/status"
  25. )
  26. // A DocumentSnapshot contains document data and metadata.
  27. type DocumentSnapshot struct {
  28. // The DocumentRef for this document.
  29. Ref *DocumentRef
  30. // Read-only. The time at which the document was created.
  31. // Increases monotonically when a document is deleted then
  32. // recreated. It can also be compared to values from other documents and
  33. // the read time of a query.
  34. CreateTime time.Time
  35. // Read-only. The time at which the document was last changed. This value
  36. // is initially set to CreateTime then increases monotonically with each
  37. // change to the document. It can also be compared to values from other
  38. // documents and the read time of a query.
  39. UpdateTime time.Time
  40. // Read-only. The time at which the document was read.
  41. ReadTime time.Time
  42. c *Client
  43. proto *pb.Document
  44. }
  45. // Exists reports whether the DocumentSnapshot represents an existing document.
  46. // Even if Exists returns false, the Ref and ReadTime fields of the DocumentSnapshot
  47. // are valid.
  48. func (d *DocumentSnapshot) Exists() bool {
  49. return d.proto != nil
  50. }
  51. // Data returns the DocumentSnapshot's fields as a map.
  52. // It is equivalent to
  53. // var m map[string]interface{}
  54. // d.DataTo(&m)
  55. // except that it returns nil if the document does not exist.
  56. func (d *DocumentSnapshot) Data() map[string]interface{} {
  57. if !d.Exists() {
  58. return nil
  59. }
  60. m, err := createMapFromValueMap(d.proto.Fields, d.c)
  61. // Any error here is a bug in the client.
  62. if err != nil {
  63. panic(fmt.Sprintf("firestore: %v", err))
  64. }
  65. return m
  66. }
  67. // DataTo uses the document's fields to populate p, which can be a pointer to a
  68. // map[string]interface{} or a pointer to a struct.
  69. //
  70. // Firestore field values are converted to Go values as follows:
  71. // - Null converts to nil.
  72. // - Bool converts to bool.
  73. // - String converts to string.
  74. // - Integer converts int64. When setting a struct field, any signed or unsigned
  75. // integer type is permitted except uint, uint64 or uintptr. Overflow is detected
  76. // and results in an error.
  77. // - Double converts to float64. When setting a struct field, float32 is permitted.
  78. // Overflow is detected and results in an error.
  79. // - Bytes is converted to []byte.
  80. // - Timestamp converts to time.Time.
  81. // - GeoPoint converts to *latlng.LatLng, where latlng is the package
  82. // "google.golang.org/genproto/googleapis/type/latlng".
  83. // - Arrays convert to []interface{}. When setting a struct field, the field
  84. // may be a slice or array of any type and is populated recursively.
  85. // Slices are resized to the incoming value's size, while arrays that are too
  86. // long have excess elements filled with zero values. If the array is too short,
  87. // excess incoming values will be dropped.
  88. // - Maps convert to map[string]interface{}. When setting a struct field,
  89. // maps of key type string and any value type are permitted, and are populated
  90. // recursively.
  91. // - References are converted to *firestore.DocumentRefs.
  92. //
  93. // Field names given by struct field tags are observed, as described in
  94. // DocumentRef.Create.
  95. //
  96. // Only the fields actually present in the document are used to populate p. Other fields
  97. // of p are left unchanged.
  98. //
  99. // If the document does not exist, DataTo returns a NotFound error.
  100. func (d *DocumentSnapshot) DataTo(p interface{}) error {
  101. if !d.Exists() {
  102. return status.Errorf(codes.NotFound, "document %s does not exist", d.Ref.Path)
  103. }
  104. return setFromProtoValue(p, &pb.Value{ValueType: &pb.Value_MapValue{&pb.MapValue{Fields: d.proto.Fields}}}, d.c)
  105. }
  106. // DataAt returns the data value denoted by path.
  107. //
  108. // The path argument can be a single field or a dot-separated sequence of
  109. // fields, and must not contain any of the runes "˜*/[]". Use DataAtPath instead for
  110. // such a path.
  111. //
  112. // See DocumentSnapshot.DataTo for how Firestore values are converted to Go values.
  113. //
  114. // If the document does not exist, DataAt returns a NotFound error.
  115. func (d *DocumentSnapshot) DataAt(path string) (interface{}, error) {
  116. if !d.Exists() {
  117. return nil, status.Errorf(codes.NotFound, "document %s does not exist", d.Ref.Path)
  118. }
  119. fp, err := parseDotSeparatedString(path)
  120. if err != nil {
  121. return nil, err
  122. }
  123. return d.DataAtPath(fp)
  124. }
  125. // DataAtPath returns the data value denoted by the FieldPath fp.
  126. // If the document does not exist, DataAtPath returns a NotFound error.
  127. func (d *DocumentSnapshot) DataAtPath(fp FieldPath) (interface{}, error) {
  128. if !d.Exists() {
  129. return nil, status.Errorf(codes.NotFound, "document %s does not exist", d.Ref.Path)
  130. }
  131. v, err := valueAtPath(fp, d.proto.Fields)
  132. if err != nil {
  133. return nil, err
  134. }
  135. return createFromProtoValue(v, d.c)
  136. }
  137. // valueAtPath returns the value of m referred to by fp.
  138. func valueAtPath(fp FieldPath, m map[string]*pb.Value) (*pb.Value, error) {
  139. for _, k := range fp[:len(fp)-1] {
  140. v := m[k]
  141. if v == nil {
  142. return nil, fmt.Errorf("firestore: no field %q", k)
  143. }
  144. mv := v.GetMapValue()
  145. if mv == nil {
  146. return nil, fmt.Errorf("firestore: value for field %q is not a map", k)
  147. }
  148. m = mv.Fields
  149. }
  150. k := fp[len(fp)-1]
  151. v := m[k]
  152. if v == nil {
  153. return nil, fmt.Errorf("firestore: no field %q", k)
  154. }
  155. return v, nil
  156. }
  157. // toProtoDocument converts a Go value to a Document proto.
  158. // Valid values are: map[string]T, struct, or pointer to a valid value.
  159. // It also returns a list DocumentTransforms.
  160. func toProtoDocument(x interface{}) (*pb.Document, []*pb.DocumentTransform_FieldTransform, error) {
  161. if x == nil {
  162. return nil, nil, errors.New("firestore: nil document contents")
  163. }
  164. v := reflect.ValueOf(x)
  165. pv, _, err := toProtoValue(v)
  166. if err != nil {
  167. return nil, nil, err
  168. }
  169. var transforms []*pb.DocumentTransform_FieldTransform
  170. transforms, err = extractTransforms(v, nil)
  171. if err != nil {
  172. return nil, nil, err
  173. }
  174. var fields map[string]*pb.Value
  175. if pv != nil {
  176. m := pv.GetMapValue()
  177. if m == nil {
  178. return nil, nil, fmt.Errorf("firestore: cannot convert value of type %T into a map", x)
  179. }
  180. fields = m.Fields
  181. }
  182. return &pb.Document{Fields: fields}, transforms, nil
  183. }
  184. func extractTransforms(v reflect.Value, prefix FieldPath) ([]*pb.DocumentTransform_FieldTransform, error) {
  185. switch v.Kind() {
  186. case reflect.Map:
  187. return extractTransformsFromMap(v, prefix)
  188. case reflect.Struct:
  189. return extractTransformsFromStruct(v, prefix)
  190. case reflect.Ptr:
  191. if v.IsNil() {
  192. return nil, nil
  193. }
  194. return extractTransforms(v.Elem(), prefix)
  195. case reflect.Interface:
  196. if v.NumMethod() == 0 { // empty interface: recurse on its contents
  197. return extractTransforms(v.Elem(), prefix)
  198. }
  199. return nil, nil
  200. default:
  201. return nil, nil
  202. }
  203. }
  204. func extractTransformsFromMap(v reflect.Value, prefix FieldPath) ([]*pb.DocumentTransform_FieldTransform, error) {
  205. var transforms []*pb.DocumentTransform_FieldTransform
  206. for _, k := range v.MapKeys() {
  207. sk := k.Interface().(string) // assume keys are strings; checked in toProtoValue
  208. path := prefix.with(sk)
  209. mi := v.MapIndex(k)
  210. if mi.Interface() == ServerTimestamp {
  211. transforms = append(transforms, serverTimestamp(path.toServiceFieldPath()))
  212. } else if au, ok := mi.Interface().(arrayUnion); ok {
  213. t, err := arrayUnionTransform(au, path)
  214. if err != nil {
  215. return nil, err
  216. }
  217. transforms = append(transforms, t)
  218. } else if ar, ok := mi.Interface().(arrayRemove); ok {
  219. t, err := arrayRemoveTransform(ar, path)
  220. if err != nil {
  221. return nil, err
  222. }
  223. transforms = append(transforms, t)
  224. } else {
  225. ps, err := extractTransforms(mi, path)
  226. if err != nil {
  227. return nil, err
  228. }
  229. transforms = append(transforms, ps...)
  230. }
  231. }
  232. return transforms, nil
  233. }
  234. func extractTransformsFromStruct(v reflect.Value, prefix FieldPath) ([]*pb.DocumentTransform_FieldTransform, error) {
  235. var transforms []*pb.DocumentTransform_FieldTransform
  236. fields, err := fieldCache.Fields(v.Type())
  237. if err != nil {
  238. return nil, err
  239. }
  240. for _, f := range fields {
  241. fv := v.FieldByIndex(f.Index)
  242. path := prefix.with(f.Name)
  243. opts := f.ParsedTag.(tagOptions)
  244. if opts.serverTimestamp {
  245. var isZero bool
  246. switch f.Type {
  247. case typeOfGoTime:
  248. isZero = fv.Interface().(time.Time).IsZero()
  249. case reflect.PtrTo(typeOfGoTime):
  250. isZero = fv.IsNil() || fv.Elem().Interface().(time.Time).IsZero()
  251. default:
  252. return nil, fmt.Errorf("firestore: field %s of struct %s with serverTimestamp tag must be of type time.Time or *time.Time",
  253. f.Name, v.Type())
  254. }
  255. if isZero {
  256. transforms = append(transforms, serverTimestamp(path.toServiceFieldPath()))
  257. }
  258. } else {
  259. ps, err := extractTransforms(fv, path)
  260. if err != nil {
  261. return nil, err
  262. }
  263. transforms = append(transforms, ps...)
  264. }
  265. }
  266. return transforms, nil
  267. }
  268. func newDocumentSnapshot(ref *DocumentRef, proto *pb.Document, c *Client, readTime *tspb.Timestamp) (*DocumentSnapshot, error) {
  269. d := &DocumentSnapshot{
  270. Ref: ref,
  271. c: c,
  272. proto: proto,
  273. }
  274. if proto != nil {
  275. ts, err := ptypes.Timestamp(proto.CreateTime)
  276. if err != nil {
  277. return nil, err
  278. }
  279. d.CreateTime = ts
  280. ts, err = ptypes.Timestamp(proto.UpdateTime)
  281. if err != nil {
  282. return nil, err
  283. }
  284. d.UpdateTime = ts
  285. }
  286. if readTime != nil {
  287. ts, err := ptypes.Timestamp(readTime)
  288. if err != nil {
  289. return nil, err
  290. }
  291. d.ReadTime = ts
  292. }
  293. return d, nil
  294. }
  295. func serverTimestamp(path string) *pb.DocumentTransform_FieldTransform {
  296. return &pb.DocumentTransform_FieldTransform{
  297. FieldPath: path,
  298. TransformType: &pb.DocumentTransform_FieldTransform_SetToServerValue{
  299. SetToServerValue: pb.DocumentTransform_FieldTransform_REQUEST_TIME,
  300. },
  301. }
  302. }