Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(261)

Side by Side Diff: service/datastore/properties.go

Issue 1516173002: Fix error message from KeyForObj when passing an invalid struct. (Closed) Base URL: https://github.com/luci/gae.git@master
Patch Set: more simplification Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package datastore 5 package datastore
6 6
7 import ( 7 import (
8 "encoding/base64" 8 "encoding/base64"
9 "errors" 9 "errors"
10 "fmt" 10 "fmt"
(...skipping 603 matching lines...) Expand 10 before | Expand all | Expand 10 after
614 } 614 }
615 panic(fmt.Errorf("Unknown property type: %s", p.Type().String())) 615 panic(fmt.Errorf("Unknown property type: %s", p.Type().String()))
616 } 616 }
617 617
618 // MetaGetter is a subinterface of PropertyLoadSaver, but is also used to 618 // MetaGetter is a subinterface of PropertyLoadSaver, but is also used to
619 // abstract the meta argument for RawInterface.GetMulti. 619 // abstract the meta argument for RawInterface.GetMulti.
620 type MetaGetter interface { 620 type MetaGetter interface {
621 // GetMeta will get information about the field which has the struct tag in 621 // GetMeta will get information about the field which has the struct tag in
622 // the form of `gae:"$<key>[,<default>]?"`. 622 // the form of `gae:"$<key>[,<default>]?"`.
623 // 623 //
624 // It returns the value, if any, and true iff the value was retrieved.
625 //
624 // Supported metadata types are: 626 // Supported metadata types are:
625 // int64 - may have default (ascii encoded base-10) 627 // int64 - may have default (ascii encoded base-10)
626 // string - may have default 628 // string - may have default
627 // Toggle - MUST have default ("true" or "false") 629 // Toggle - MUST have default ("true" or "false")
628 // *Key - NO default allowed 630 // *Key - NO default allowed
629 // 631 //
630 // Struct fields of type Toggle (which is an Auto/On/Off) require you to 632 // Struct fields of type Toggle (which is an Auto/On/Off) require you to
631 // specify a value of 'true' or 'false' for the default value of the str uct 633 // specify a value of 'true' or 'false' for the default value of the str uct
632 // tag, and GetMeta will return the combined value as a regular boolean true 634 // tag, and GetMeta will return the combined value as a regular boolean true
633 // or false value. 635 // or false value.
634 // Example: 636 // Example:
635 // type MyStruct struct { 637 // type MyStruct struct {
636 // CoolField int64 `gae:"$id,1"` 638 // CoolField int64 `gae:"$id,1"`
637 // } 639 // }
638 // val, err := helper.GetPLS(&MyStruct{}).GetMeta("id") 640 // val, err := helper.GetPLS(&MyStruct{}).GetMeta("id")
639 // // val == 1 641 // // val == 1
640 // // err == nil 642 // // err == nil
641 // 643 //
642 // val, err := helper.GetPLS(&MyStruct{10}).GetMeta("id") 644 // val, err := helper.GetPLS(&MyStruct{10}).GetMeta("id")
643 // // val == 10 645 // // val == 10
644 // // err == nil 646 // // err == nil
645 // 647 //
646 // type MyStruct struct { 648 // type MyStruct struct {
647 // TFlag Toggle `gae:"$flag1,true"` // defaults to true 649 // TFlag Toggle `gae:"$flag1,true"` // defaults to true
648 // FFlag Toggle `gae:"$flag2,false"` // defaults to false 650 // FFlag Toggle `gae:"$flag2,false"` // defaults to false
649 // // BadFlag Toggle `gae:"$flag3"` // ILLEGAL 651 // // BadFlag Toggle `gae:"$flag3"` // ILLEGAL
650 // } 652 // }
651 » GetMeta(key string) (interface{}, error) 653 » GetMeta(key string) (interface{}, bool)
652
653 » // GetMetaDefault is GetMeta, but with a default.
654 » //
655 » // If the metadata key is not available, or its type doesn't equal the
656 » // homogenized type of dflt, then dflt will be returned.
657 » //
658 » // Type homogenization:
659 » // signed integer types -> int64
660 » // bool -> Toggle fields (bool)
661 » //
662 » // Example:
663 » // pls.GetMetaDefault("foo", 100).(int64)
664 » GetMetaDefault(key string, dflt interface{}) interface{}
dnj 2015/12/12 02:47:50 w00t
665 } 654 }
666 655
667 // PropertyLoadSaver may be implemented by a user type, and Interface will 656 // PropertyLoadSaver may be implemented by a user type, and Interface will
668 // use this interface to serialize the type instead of trying to automatically 657 // use this interface to serialize the type instead of trying to automatically
669 // create a serialization codec for it with helper.GetPLS. 658 // create a serialization codec for it with helper.GetPLS.
670 type PropertyLoadSaver interface { 659 type PropertyLoadSaver interface {
671 // Load takes the values from the given map and attempts to save them in to 660 // Load takes the values from the given map and attempts to save them in to
672 // the underlying object (usually a struct or a PropertyMap). If a fatal 661 // the underlying object (usually a struct or a PropertyMap). If a fatal
673 // error occurs, it's returned via error. If non-fatal conversion errors 662 // error occurs, it's returned via error. If non-fatal conversion errors
674 // occur, error will be a MultiError containing one or more ErrFieldMism atch 663 // occur, error will be a MultiError containing one or more ErrFieldMism atch
675 // objects. 664 // objects.
676 Load(PropertyMap) error 665 Load(PropertyMap) error
677 666
678 // Save returns the current property as a PropertyMap. if withMeta is tr ue, 667 // Save returns the current property as a PropertyMap. if withMeta is tr ue,
679 // then the PropertyMap contains all the metadata (e.g. '$meta' fields) 668 // then the PropertyMap contains all the metadata (e.g. '$meta' fields)
680 // which was held by this PropertyLoadSaver. 669 // which was held by this PropertyLoadSaver.
681 Save(withMeta bool) (PropertyMap, error) 670 Save(withMeta bool) (PropertyMap, error)
682
683 // Problem indicates that this PLS has a fatal problem. Usually this is
684 // set when the underlying struct has recursion, invalid field types, ne sted
685 // slices, etc.
686 Problem() error
687 } 671 }
688 672
689 // MetaGetterSetter is the subset of PropertyLoadSaver which pertains to 673 // MetaGetterSetter is the subset of PropertyLoadSaver which pertains to
690 // getting and saving metadata. 674 // getting and saving metadata.
691 // 675 //
692 // A *struct may implement this interface to provide metadata which is 676 // A *struct may implement this interface to provide metadata which is
693 // supplimental to the variety described by GetPLS. For example, this could be 677 // supplimental to the variety described by GetPLS. For example, this could be
694 // used to implement a parsed-out $kind or $id. 678 // used to implement a parsed-out $kind or $id.
695 type MetaGetterSetter interface { 679 type MetaGetterSetter interface {
696 MetaGetter 680 MetaGetter
697 681
698 // GetAllMeta returns a PropertyMap with all of the metadata in this 682 // GetAllMeta returns a PropertyMap with all of the metadata in this
699 // MetaGetterSetter. If a metadata field has an error during serializati on, 683 // MetaGetterSetter. If a metadata field has an error during serializati on,
700 // it is skipped. 684 // it is skipped.
701 // 685 //
702 // If a *struct is implementing this, then it only needs to return the 686 // If a *struct is implementing this, then it only needs to return the
703 // metadata fields which would be returned by its GetMeta implementation , and 687 // metadata fields which would be returned by its GetMeta implementation , and
704 // the `GetPLS` implementation will add any statically-defined metadata 688 // the `GetPLS` implementation will add any statically-defined metadata
705 // fields. So if GetMeta provides $id, but there's a simple tagged field for 689 // fields. So if GetMeta provides $id, but there's a simple tagged field for
706 // $kind, this method is only expected to return a PropertyMap with "$id ". 690 // $kind, this method is only expected to return a PropertyMap with "$id ".
707 GetAllMeta() PropertyMap 691 GetAllMeta() PropertyMap
708 692
709 // SetMeta allows you to set the current value of the meta-keyed field. 693 // SetMeta allows you to set the current value of the meta-keyed field.
710 » SetMeta(key string, val interface{}) error 694 » // It returns true iff the field was set.
695 » SetMeta(key string, val interface{}) bool
711 } 696 }
712 697
713 // PropertyMap represents the contents of a datastore entity in a generic way. 698 // PropertyMap represents the contents of a datastore entity in a generic way.
714 // It maps from property name to a list of property values which correspond to 699 // It maps from property name to a list of property values which correspond to
715 // that property name. It is the spiritual successor to PropertyList from the 700 // that property name. It is the spiritual successor to PropertyList from the
716 // original SDK. 701 // original SDK.
717 // 702 //
718 // PropertyMap may contain "meta" values, which are keyed with a '$' prefix. 703 // PropertyMap may contain "meta" values, which are keyed with a '$' prefix.
719 // Technically the datastore allows arbitrary property names, but all of the 704 // Technically the datastore allows arbitrary property names, but all of the
720 // SDKs go out of their way to try to make all property names valid programming 705 // SDKs go out of their way to try to make all property names valid programming
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
754 ret := make(PropertyMap, len(pm)) 739 ret := make(PropertyMap, len(pm))
755 for k, v := range pm { 740 for k, v := range pm {
756 if withMeta || !isMetaKey(k) { 741 if withMeta || !isMetaKey(k) {
757 ret[k] = append(ret[k], v...) 742 ret[k] = append(ret[k], v...)
758 } 743 }
759 } 744 }
760 return ret, nil 745 return ret, nil
761 } 746 }
762 747
763 // GetMeta implements PropertyLoadSaver.GetMeta, and returns the current value 748 // GetMeta implements PropertyLoadSaver.GetMeta, and returns the current value
764 // associated with the metadata key. It may return ErrMetaFieldUnset if the 749 // associated with the metadata key.
765 // key doesn't exist. 750 func (pm PropertyMap) GetMeta(key string) (interface{}, bool) {
766 func (pm PropertyMap) GetMeta(key string) (interface{}, error) {
767 v, ok := pm["$"+key] 751 v, ok := pm["$"+key]
768 if !ok || len(v) == 0 { 752 if !ok || len(v) == 0 {
769 » » return nil, ErrMetaFieldUnset 753 » » return nil, false
770 } 754 }
771 » if len(v) > 1 { 755 » return v[0].Value(), true
772 » » return nil, errors.New("gae: too many values for Meta key")
773 » }
774 » return v[0].Value(), nil
775 } 756 }
776 757
777 // GetAllMeta implements PropertyLoadSaver.GetAllMeta. 758 // GetAllMeta implements PropertyLoadSaver.GetAllMeta.
778 func (pm PropertyMap) GetAllMeta() PropertyMap { 759 func (pm PropertyMap) GetAllMeta() PropertyMap {
779 ret := make(PropertyMap, 8) 760 ret := make(PropertyMap, 8)
780 for k, v := range pm { 761 for k, v := range pm {
781 if isMetaKey(k) { 762 if isMetaKey(k) {
782 newV := make([]Property, len(v)) 763 newV := make([]Property, len(v))
783 copy(newV, v) 764 copy(newV, v)
784 ret[k] = newV 765 ret[k] = newV
785 } 766 }
786 } 767 }
787 return ret 768 return ret
788 } 769 }
789 770
790 // GetMetaDefault is the implementation of PropertyLoadSaver.GetMetaDefault.
791 func (pm PropertyMap) GetMetaDefault(key string, dflt interface{}) interface{} {
792 return GetMetaDefaultImpl(pm.GetMeta, key, dflt)
793 }
794
795 // SetMeta implements PropertyLoadSaver.SetMeta. It will only return an error 771 // SetMeta implements PropertyLoadSaver.SetMeta. It will only return an error
796 // if `val` has an invalid type (e.g. not one supported by Property). 772 // if `val` has an invalid type (e.g. not one supported by Property).
797 func (pm PropertyMap) SetMeta(key string, val interface{}) error { 773 func (pm PropertyMap) SetMeta(key string, val interface{}) bool {
798 prop := Property{} 774 prop := Property{}
799 if err := prop.SetValue(val, NoIndex); err != nil { 775 if err := prop.SetValue(val, NoIndex); err != nil {
800 » » return err 776 » » return false
801 } 777 }
802 pm["$"+key] = []Property{prop} 778 pm["$"+key] = []Property{prop}
803 » return nil 779 » return true
804 } 780 }
805 781
806 // Problem implements PropertyLoadSaver.Problem. It ALWAYS returns nil. 782 // Problem implements PropertyLoadSaver.Problem. It ALWAYS returns nil.
807 func (pm PropertyMap) Problem() error { 783 func (pm PropertyMap) Problem() error {
808 return nil 784 return nil
809 } 785 }
810 786
811 // EstimateSize estimates the size that it would take to encode this PropertyMap 787 // EstimateSize estimates the size that it would take to encode this PropertyMap
812 // in the production Appengine datastore. The calculation excludes metadata 788 // in the production Appengine datastore. The calculation excludes metadata
813 // fields in the map. 789 // fields in the map.
(...skipping 12 matching lines...) Expand all
826 } 802 }
827 return ret 803 return ret
828 } 804 }
829 805
830 func isMetaKey(k string) bool { 806 func isMetaKey(k string) bool {
831 // empty counts as a metakey since it's not a valid data key, but it's 807 // empty counts as a metakey since it's not a valid data key, but it's
832 // not really a valid metakey either. 808 // not really a valid metakey either.
833 return k == "" || k[0] == '$' 809 return k == "" || k[0] == '$'
834 } 810 }
835 811
836 // GetMetaDefaultImpl is the implementation of PropertyLoadSaver.GetMetaDefault. 812 // GetMetaDefault is a helper for GetMeta, allowing a default value.
837 // 813 //
838 // It takes the normal GetMeta function, the key and the default, and returns 814 // If the metadata key is not available, or its type doesn't equal the
839 // the value according to PropertyLoadSaver.GetMetaDefault. 815 // homogenized type of dflt, then dflt will be returned.
840 func GetMetaDefaultImpl(gm func(string) (interface{}, error), key string, dflt i nterface{}) interface{} { 816 //
817 // Type homogenization:
818 // signed integer types -> int64
819 // bool -> Toggle fields (bool)
820 //
821 // Example:
822 // pls.GetMetaDefault("foo", 100).(int64)
823 func GetMetaDefault(getter MetaGetter, key string, dflt interface{}) interface{} {
841 dflt = UpconvertUnderlyingType(dflt) 824 dflt = UpconvertUnderlyingType(dflt)
842 » cur, err := gm(key) 825 » cur, ok := getter.GetMeta(key)
843 » if err != nil { 826 » if !ok || (dflt != nil && reflect.TypeOf(cur) != reflect.TypeOf(dflt)) {
844 » » return dflt
845 » }
846 » if dflt != nil && reflect.TypeOf(cur) != reflect.TypeOf(dflt) {
847 return dflt 827 return dflt
848 } 828 }
849 return cur 829 return cur
850 } 830 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698