| // Copyright 2019 Google Inc. All rights reserved. |
| // Use of this source code is governed by the Apache 2.0 |
| // license that can be found in the LICENSE file. |
| |
| // Package cloudpb is a subset of types and functions, copied from cloud.google.com/go/datastore. |
| // |
| // They are copied here to provide compatibility to decode keys generated by the cloud.google.com/go/datastore package. |
| package cloudkey |
| |
| import ( |
| "encoding/base64" |
| "errors" |
| "strings" |
| |
| "github.com/golang/protobuf/proto" |
| cloudpb "google.golang.org/appengine/datastore/internal/cloudpb" |
| ) |
| |
| ///////////////////////////////////////////////////////////////////// |
| // Code below is copied from https://github.com/googleapis/google-cloud-go/blob/master/datastore/datastore.go |
| ///////////////////////////////////////////////////////////////////// |
| |
| var ( |
| // ErrInvalidKey is returned when an invalid key is presented. |
| ErrInvalidKey = errors.New("datastore: invalid key") |
| ) |
| |
| ///////////////////////////////////////////////////////////////////// |
| // Code below is copied from https://github.com/googleapis/google-cloud-go/blob/master/datastore/key.go |
| ///////////////////////////////////////////////////////////////////// |
| |
| // Key represents the datastore key for a stored entity. |
| type Key struct { |
| // Kind cannot be empty. |
| Kind string |
| // Either ID or Name must be zero for the Key to be valid. |
| // If both are zero, the Key is incomplete. |
| ID int64 |
| Name string |
| // Parent must either be a complete Key or nil. |
| Parent *Key |
| |
| // Namespace provides the ability to partition your data for multiple |
| // tenants. In most cases, it is not necessary to specify a namespace. |
| // See docs on datastore multitenancy for details: |
| // https://cloud.google.com/datastore/docs/concepts/multitenancy |
| Namespace string |
| } |
| |
| // DecodeKey decodes a key from the opaque representation returned by Encode. |
| func DecodeKey(encoded string) (*Key, error) { |
| // Re-add padding. |
| if m := len(encoded) % 4; m != 0 { |
| encoded += strings.Repeat("=", 4-m) |
| } |
| |
| b, err := base64.URLEncoding.DecodeString(encoded) |
| if err != nil { |
| return nil, err |
| } |
| |
| pKey := new(cloudpb.Key) |
| if err := proto.Unmarshal(b, pKey); err != nil { |
| return nil, err |
| } |
| return protoToKey(pKey) |
| } |
| |
| // valid returns whether the key is valid. |
| func (k *Key) valid() bool { |
| if k == nil { |
| return false |
| } |
| for ; k != nil; k = k.Parent { |
| if k.Kind == "" { |
| return false |
| } |
| if k.Name != "" && k.ID != 0 { |
| return false |
| } |
| if k.Parent != nil { |
| if k.Parent.Incomplete() { |
| return false |
| } |
| if k.Parent.Namespace != k.Namespace { |
| return false |
| } |
| } |
| } |
| return true |
| } |
| |
| // Incomplete reports whether the key does not refer to a stored entity. |
| func (k *Key) Incomplete() bool { |
| return k.Name == "" && k.ID == 0 |
| } |
| |
| // protoToKey decodes a protocol buffer representation of a key into an |
| // equivalent *Key object. If the key is invalid, protoToKey will return the |
| // invalid key along with ErrInvalidKey. |
| func protoToKey(p *cloudpb.Key) (*Key, error) { |
| var key *Key |
| var namespace string |
| if partition := p.PartitionId; partition != nil { |
| namespace = partition.NamespaceId |
| } |
| for _, el := range p.Path { |
| key = &Key{ |
| Namespace: namespace, |
| Kind: el.Kind, |
| ID: el.GetId(), |
| Name: el.GetName(), |
| Parent: key, |
| } |
| } |
| if !key.valid() { // Also detects key == nil. |
| return key, ErrInvalidKey |
| } |
| return key, nil |
| } |