keycompat.go 2.5 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788
  1. // Copyright 2019 Google Inc. All rights reserved.
  2. // Use of this source code is governed by the Apache 2.0
  3. // license that can be found in the LICENSE file.
  4. package datastore
  5. import (
  6. "context"
  7. "sync"
  8. "google.golang.org/appengine/datastore/internal/cloudkey"
  9. "google.golang.org/appengine/internal"
  10. )
  11. var keyConversion struct {
  12. mu sync.RWMutex
  13. appID string // read using getKeyConversionAppID
  14. }
  15. // EnableKeyConversion enables encoded key compatibility with the Cloud
  16. // Datastore client library (cloud.google.com/go/datastore). Encoded keys
  17. // generated by the Cloud Datastore client library will be decoded into App
  18. // Engine datastore keys.
  19. //
  20. // The context provided must be an App Engine context if running in App Engine
  21. // first generation runtime. This can be called in the /_ah/start handler. It is
  22. // safe to call multiple times, and is cheap to call, so can also be inserted as
  23. // middleware.
  24. //
  25. // Enabling key compatibility does not affect the encoding format used by
  26. // Key.Encode, it only expands the type of keys that are able to be decoded with
  27. // DecodeKey.
  28. func EnableKeyConversion(ctx context.Context) {
  29. // Only attempt to set appID if it's unset.
  30. // If already set, ignore.
  31. if getKeyConversionAppID() != "" {
  32. return
  33. }
  34. keyConversion.mu.Lock()
  35. // Check again to avoid race where another goroutine set appID between the call
  36. // to getKeyConversionAppID above and taking the write lock.
  37. if keyConversion.appID == "" {
  38. keyConversion.appID = internal.FullyQualifiedAppID(ctx)
  39. }
  40. keyConversion.mu.Unlock()
  41. }
  42. func getKeyConversionAppID() string {
  43. keyConversion.mu.RLock()
  44. appID := keyConversion.appID
  45. keyConversion.mu.RUnlock()
  46. return appID
  47. }
  48. // decodeCloudKey attempts to decode the given encoded key generated by the
  49. // Cloud Datastore client library (cloud.google.com/go/datastore), returning nil
  50. // if the key couldn't be decoded.
  51. func decodeCloudKey(encoded string) *Key {
  52. appID := getKeyConversionAppID()
  53. if appID == "" {
  54. return nil
  55. }
  56. k, err := cloudkey.DecodeKey(encoded)
  57. if err != nil {
  58. return nil
  59. }
  60. return convertCloudKey(k, appID)
  61. }
  62. // convertCloudKey converts a Cloud Datastore key and converts it to an App
  63. // Engine Datastore key. Cloud Datastore keys don't include the project/app ID,
  64. // so we must add it back in.
  65. func convertCloudKey(key *cloudkey.Key, appID string) *Key {
  66. if key == nil {
  67. return nil
  68. }
  69. k := &Key{
  70. intID: key.ID,
  71. kind: key.Kind,
  72. namespace: key.Namespace,
  73. parent: convertCloudKey(key.Parent, appID),
  74. stringID: key.Name,
  75. appID: appID,
  76. }
  77. return k
  78. }