func TestPutPropertyLoadSaver(t *testing.T) { c, closeFunc := NewContext(t) defer closeFunc() type testEntity struct { IntVal int } te := &testEntity{2} pl, err := datastore.SaveStruct(te) if err != nil { t.Fatal(err) } keys := []*datastore.Key{datastore.NewKey(c, "Test", "", 1, nil)} pls := datastore.PropertyList(pl) if _, err := nds.PutMulti(c, keys, []datastore.PropertyLoadSaver{&pls}); err != nil { t.Fatal(err) } getPl := datastore.PropertyList{} if err := nds.GetMulti(c, keys, []datastore.PropertyLoadSaver{&getPl}); err != nil { t.Fatal(err) } getTe := &testEntity{} if err := datastore.LoadStruct(getTe, getPl); err != nil { t.Fatal(err) } if te.IntVal != getTe.IntVal { t.Fatal("expected same IntVal", getTe.IntVal) } }
// retrieve obtains the application configuration as a datastore.PropertyList. func retrieve(ctx context.Context) (datastore.PropertyList, error) { p := datastore.PropertyList(make([]datastore.Property, 0, 8)) key := datastore.NewKey(ctx, Entity, Entity, 0, nil) err := nds.Get(ctx, key, &p) return p, err }
// Save changes the application configuration to // the values in conf. All HTTP requests subsequent to this one // are guaranteed to use the new values in their configuration. // // Note that subsequent calls to Get with the same request context // will continue to retrieve the old version of the configuration. // // As a special case, calling Save with a *config.Config will replace // the entire contents of the configuration with the contents of Config. func Save(ctx context.Context, conf interface{}) error { if typedConfig, ok := conf.(*Config); ok { pl := datastore.PropertyList(*typedConfig) replaceKey := datastore.NewKey(ctx, Entity, Entity, 0, nil) _, replaceErr := nds.Put(ctx, replaceKey, &pl) return replaceErr } return datastore.RunInTransaction(ctx, func(txCtx context.Context) error { props := datastore.PropertyList{} key := datastore.NewKey(txCtx, Entity, Entity, 0, nil) if err := nds.Get(txCtx, key, &props); err != nil && err != datastore.ErrNoSuchEntity { return err } // merge existing config with the new values if newProps, err := datastore.SaveStruct(conf); err != nil { return err } else { for _, newProp := range newProps { newProp.NoIndex = true replacing := false for _, prop := range props { // make sure NoIndex is set prop.NoIndex = true if prop.Name == newProp.Name { replacing = true prop.Value = newProp.Value break } } if !replacing { // append props = append(props, newProp) } } } _, err := nds.Put(txCtx, key, &props) return err }, nil) }
// memcacheLockTime is the maximum length of time a memcache lock will be // held for. 32 seconds is chosen as 30 seconds is the maximum amount of // time an underlying datastore call will retry even if the API reports a // success to the user. memcacheLockTime = 32 * time.Second // memcacheMaxKeySize is the maximum size a memcache item key can be. Keys // greater than this size are automatically hashed to a smaller size. memcacheMaxKeySize = 250 ) var ( typeOfPropertyLoadSaver = reflect.TypeOf( (*datastore.PropertyLoadSaver)(nil)).Elem() typeOfPropertyList = reflect.TypeOf(datastore.PropertyList(nil)) ) // The variables in this block are here so that we can test all error code // paths by substituting them with error producing ones. var ( datastoreDeleteMulti = datastore.DeleteMulti datastoreGetMulti = datastore.GetMulti datastorePutMulti = datastore.PutMulti memcacheAddMulti = memcache.AddMulti memcacheCompareAndSwapMulti = memcache.CompareAndSwapMulti memcacheDeleteMulti = memcache.DeleteMulti memcacheGetMulti = memcache.GetMulti memcacheSetMulti = memcache.SetMulti