go.mercari.io/datastore@v1.8.2/boom/boom.go (about)

     1  package boom
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"reflect"
     7  	"strings"
     8  
     9  	"go.mercari.io/datastore"
    10  )
    11  
    12  var typeOfKey = reflect.TypeOf((*datastore.Key)(nil)).Elem()
    13  
    14  // Boom is a datastore client wrapper to make it easy to understand the handling of Key.
    15  type Boom struct {
    16  	Context context.Context
    17  	Client  datastore.Client
    18  }
    19  
    20  func (bm *Boom) extractKeys(src interface{}) ([]datastore.Key, error) {
    21  	v := reflect.Indirect(reflect.ValueOf(src))
    22  	if v.Kind() != reflect.Slice {
    23  		return nil, fmt.Errorf("boom: value must be a slice or pointer-to-slice or key-slice")
    24  	}
    25  	l := v.Len()
    26  
    27  	keys := make([]datastore.Key, 0, l)
    28  	for i := 0; i < l; i++ {
    29  		v := v.Index(i)
    30  		obj := v.Interface()
    31  
    32  		key, ok := obj.(datastore.Key)
    33  		if ok {
    34  			keys = append(keys, key)
    35  			continue
    36  		}
    37  
    38  		key, err := bm.KeyError(obj)
    39  		if err != nil {
    40  			return nil, err
    41  		}
    42  		keys = append(keys, key)
    43  	}
    44  	return keys, nil
    45  }
    46  
    47  func (bm *Boom) setStructKey(src interface{}, key datastore.Key) error {
    48  	v := reflect.ValueOf(src)
    49  	t := v.Type()
    50  	k := t.Kind()
    51  
    52  	if k != reflect.Ptr {
    53  		return fmt.Errorf("boom: Expected pointer to struct, got instead: %v", k)
    54  	}
    55  
    56  	v = reflect.Indirect(v)
    57  	t = v.Type()
    58  	k = t.Kind()
    59  
    60  	if k != reflect.Struct {
    61  		return fmt.Errorf(fmt.Sprintf("boom: Expected struct, got instead: %v", k))
    62  	}
    63  
    64  	idSet := false
    65  	kindSet := false
    66  	parentSet := false
    67  	for i := 0; i < v.NumField(); i++ {
    68  		tf := t.Field(i)
    69  		vf := v.Field(i)
    70  
    71  		if !vf.CanSet() {
    72  			continue
    73  		}
    74  
    75  		tag := tf.Tag.Get("boom")
    76  		if tag == "" {
    77  			tag = tf.Tag.Get("goon")
    78  		}
    79  		tagValues := strings.SplitN(tag, ",", 2)
    80  		if len(tagValues) == 0 {
    81  			continue
    82  		}
    83  
    84  		switch tagValues[0] {
    85  		case "id":
    86  			if idSet {
    87  				return fmt.Errorf("boom: Only one field may be marked id")
    88  			}
    89  
    90  			pt, ok := vf.Interface().(datastore.PropertyTranslator)
    91  			if ok {
    92  				pv, err := pt.FromPropertyValue(bm.Context, datastore.Property{Value: key})
    93  				if err != nil {
    94  					return err
    95  				}
    96  
    97  				vf.Set(reflect.ValueOf(pv))
    98  
    99  			} else {
   100  				switch vf.Kind() {
   101  				case reflect.Int64:
   102  					vf.SetInt(key.ID())
   103  				case reflect.String:
   104  					vf.SetString(key.Name())
   105  				}
   106  			}
   107  
   108  			idSet = true
   109  
   110  		case "kind":
   111  			if kindSet {
   112  				return fmt.Errorf("boom: Only one field may be marked kind")
   113  			}
   114  			if vf.Kind() == reflect.String {
   115  				if (len(tagValues) <= 1 || key.Kind() != tagValues[1]) && t.Name() != key.Kind() {
   116  					vf.Set(reflect.ValueOf(key.Kind()))
   117  				}
   118  				kindSet = true
   119  			}
   120  
   121  		case "parent":
   122  			if parentSet {
   123  				return fmt.Errorf("boom: Only one field may be marked parent")
   124  			}
   125  
   126  			pt, ok := vf.Interface().(datastore.PropertyTranslator)
   127  			if ok {
   128  				pv, err := pt.FromPropertyValue(bm.Context, datastore.Property{Value: key.ParentKey()})
   129  				if err != nil {
   130  					return err
   131  				}
   132  
   133  				vf.Set(reflect.ValueOf(pv))
   134  				parentSet = true
   135  
   136  			} else {
   137  				vfType := vf.Type()
   138  				if vfType.ConvertibleTo(typeOfKey) {
   139  					if key.ParentKey() != nil {
   140  						vf.Set(reflect.ValueOf(key.ParentKey()).Convert(vfType))
   141  					}
   142  					parentSet = true
   143  				}
   144  			}
   145  		}
   146  	}
   147  
   148  	if !idSet {
   149  		return fmt.Errorf("boom: Could not set id field")
   150  	}
   151  
   152  	return nil
   153  }
   154  
   155  // Kind retrieves kind name from struct.
   156  func (bm *Boom) Kind(src interface{}) string {
   157  	// bm.KeyError を使うと id が PropertyTranslator だった場合に無限再起する場合がある
   158  	kind, err := bm.kindErr(src)
   159  	if err != nil {
   160  		return ""
   161  	}
   162  	return kind
   163  }
   164  
   165  func (bm *Boom) kindErr(src interface{}) (string, error) {
   166  	v := reflect.Indirect(reflect.ValueOf(src))
   167  	t := v.Type()
   168  	k := t.Kind()
   169  
   170  	if k != reflect.Struct {
   171  		return "", fmt.Errorf("boom: Expected struct, got instead: %v", k)
   172  	}
   173  
   174  	var kind string
   175  
   176  	for i := 0; i < v.NumField(); i++ {
   177  		tf := t.Field(i)
   178  		vf := v.Field(i)
   179  
   180  		tag := tf.Tag.Get("boom")
   181  		if tag == "" {
   182  			tag = tf.Tag.Get("goon")
   183  		}
   184  		tagValues := strings.SplitN(tag, ",", 2)
   185  		if len(tagValues) > 0 {
   186  			switch tagValues[0] {
   187  			case "kind":
   188  				if vf.Kind() == reflect.String {
   189  					if kind != "" {
   190  						return "", fmt.Errorf("boom: Only one field may be marked kind")
   191  					}
   192  					kind = vf.String()
   193  					if kind == "" && len(tagValues) > 1 && tagValues[1] != "" {
   194  						kind = tagValues[1]
   195  					}
   196  				}
   197  			}
   198  		}
   199  	}
   200  
   201  	if kind == "" {
   202  		kind = t.Name()
   203  	}
   204  
   205  	return kind, nil
   206  }
   207  
   208  // Key retrieves datastore key from struct without error occurred.
   209  func (bm *Boom) Key(src interface{}) datastore.Key {
   210  	key, err := bm.KeyError(src)
   211  	if err != nil {
   212  		return nil
   213  	}
   214  
   215  	return key
   216  }
   217  
   218  // KeyError retrieves datastore key from struct with error occurred.
   219  func (bm *Boom) KeyError(src interface{}) (datastore.Key, error) {
   220  	v := reflect.Indirect(reflect.ValueOf(src))
   221  	t := v.Type()
   222  	k := t.Kind()
   223  
   224  	if k != reflect.Struct {
   225  		return nil, fmt.Errorf("boom: Expected struct, got instead: %v", k)
   226  	}
   227  
   228  	var parent datastore.Key
   229  	var key datastore.Key
   230  	var keyName string
   231  	var keyID int64
   232  	var kind string
   233  
   234  	for i := 0; i < v.NumField(); i++ {
   235  		tf := t.Field(i)
   236  		vf := v.Field(i)
   237  
   238  		tag := tf.Tag.Get("boom")
   239  		if tag == "" {
   240  			tag = tf.Tag.Get("goon")
   241  		}
   242  		tagValues := strings.SplitN(tag, ",", 2)
   243  		if len(tagValues) > 0 {
   244  			switch tagValues[0] {
   245  			case "id":
   246  
   247  				pt, ok := vf.Interface().(datastore.PropertyTranslator)
   248  				if ok {
   249  					pv, err := pt.ToPropertyValue(bm.Context)
   250  					if err != nil {
   251  						return nil, err
   252  					}
   253  					if id, ok := pv.(int64); ok {
   254  						if key != nil || keyID != 0 || keyName != "" {
   255  							return nil, fmt.Errorf("boom: Only one field may be marked id")
   256  						}
   257  						keyID = id
   258  					} else if name, ok := pv.(string); ok {
   259  						if key != nil || keyID != 0 || keyName != "" {
   260  							return nil, fmt.Errorf("boom: Only one field may be marked id")
   261  						}
   262  						keyName = name
   263  					} else if propertyKey, ok := pv.(datastore.Key); ok {
   264  						if key != nil || keyID != 0 || keyName != "" {
   265  							return nil, fmt.Errorf("boom: Only one field may be marked id")
   266  						}
   267  						key = propertyKey
   268  					}
   269  				} else {
   270  					switch vf.Kind() {
   271  					case reflect.Int64:
   272  						if key != nil || keyID != 0 || keyName != "" {
   273  							return nil, fmt.Errorf("boom: Only one field may be marked id")
   274  						}
   275  						keyID = vf.Int()
   276  					case reflect.String:
   277  						if key != nil || keyID != 0 || keyName != "" {
   278  							return nil, fmt.Errorf("boom: Only one field may be marked id")
   279  						}
   280  						keyName = vf.String()
   281  					default:
   282  						return nil, fmt.Errorf("boom: ID field must be int64 or string in %v", t.Name())
   283  					}
   284  				}
   285  
   286  			case "kind":
   287  				if vf.Kind() == reflect.String {
   288  					if kind != "" {
   289  						return nil, fmt.Errorf("boom: Only one field may be marked kind")
   290  					}
   291  					kind = vf.String()
   292  					if kind == "" && len(tagValues) > 1 && tagValues[1] != "" {
   293  						kind = tagValues[1]
   294  					}
   295  				}
   296  
   297  			case "parent":
   298  				pt, ok := vf.Interface().(datastore.PropertyTranslator)
   299  				if ok {
   300  					pv, err := pt.ToPropertyValue(bm.Context)
   301  					if err != nil {
   302  						return nil, err
   303  					}
   304  					if key, ok := pv.(datastore.Key); ok {
   305  						if parent != nil {
   306  							return nil, fmt.Errorf("boom: Only one field may be marked parent")
   307  						}
   308  						parent = key
   309  					}
   310  				} else {
   311  					vfType := vf.Type()
   312  					if !vf.IsNil() && vfType.ConvertibleTo(typeOfKey) {
   313  						if parent != nil {
   314  							return nil, fmt.Errorf("boom: Only one field may be marked parent")
   315  						}
   316  						parent = vf.Convert(typeOfKey).Interface().(datastore.Key)
   317  					}
   318  				}
   319  			}
   320  		}
   321  	}
   322  
   323  	if kind == "" {
   324  		kind = t.Name()
   325  	}
   326  
   327  	if key != nil {
   328  		if key.ParentKey() != nil && parent != nil {
   329  			return nil, fmt.Errorf("boom: ID field returns key. don't use parent annotated field at same time")
   330  		}
   331  		if key.Kind() != kind {
   332  			return nil, fmt.Errorf("boom: ID field returns key that contains unexpected kind")
   333  		}
   334  
   335  		if key.ParentKey() != nil {
   336  			return key, nil
   337  		}
   338  
   339  		if keyName := key.Name(); keyName != "" {
   340  			return bm.Client.NameKey(kind, keyName, parent), nil
   341  		}
   342  
   343  		return bm.Client.IDKey(kind, key.ID(), parent), nil
   344  	}
   345  
   346  	if keyName != "" {
   347  		return bm.Client.NameKey(kind, keyName, parent), nil
   348  	}
   349  
   350  	return bm.Client.IDKey(kind, keyID, parent), nil
   351  }
   352  
   353  // AllocateID takes a struct whose key has not yet been set as an argument,
   354  // allocates the Key of the relevant Kind, and sets it to a struct.
   355  func (bm *Boom) AllocateID(src interface{}) (datastore.Key, error) {
   356  	srcs := []interface{}{src}
   357  	keys, err := bm.AllocateIDs(srcs)
   358  	if merr, ok := err.(datastore.MultiError); ok {
   359  		return nil, merr[0]
   360  	} else if err != nil {
   361  		return nil, err
   362  	}
   363  
   364  	return keys[0], nil
   365  }
   366  
   367  // AllocateIDs takes a slice of a struct whose key has not yet been set as an argument,
   368  // secures the Key of the relevant Kind, and sets it to each struct.
   369  func (bm *Boom) AllocateIDs(src interface{}) ([]datastore.Key, error) {
   370  	v := reflect.Indirect(reflect.ValueOf(src))
   371  	if v.Kind() != reflect.Slice {
   372  		return nil, fmt.Errorf("boom: value must be a slice or pointer-to-slice or incompletekey-slice or string-slice")
   373  	}
   374  	l := v.Len()
   375  
   376  	keys := make([]datastore.Key, 0, l)
   377  	structIndex := make([]int, 0, l)
   378  	for i := 0; i < l; i++ {
   379  		v := v.Index(i)
   380  		obj := v.Interface()
   381  
   382  		key, ok := obj.(datastore.Key)
   383  		if ok {
   384  			keys = append(keys, key)
   385  			continue
   386  		}
   387  
   388  		kind, ok := obj.(string)
   389  		if ok {
   390  			keys = append(keys, bm.Client.IncompleteKey(kind, nil))
   391  			continue
   392  		}
   393  
   394  		key, err := bm.KeyError(obj)
   395  		if err != nil {
   396  			return nil, err
   397  		}
   398  		keys = append(keys, key)
   399  		structIndex = append(structIndex, i)
   400  	}
   401  
   402  	keys, err := bm.Client.AllocateIDs(bm.Context, keys)
   403  	if err != nil {
   404  		return nil, err
   405  	}
   406  
   407  	for _, sIdx := range structIndex {
   408  		v := v.Index(sIdx)
   409  		obj := v.Interface()
   410  
   411  		err = bm.setStructKey(obj, keys[sIdx])
   412  		if err != nil {
   413  			return nil, err
   414  		}
   415  	}
   416  
   417  	return keys, nil
   418  }
   419  
   420  // Get loads the entity stored for key into dst, which must be a struct pointer or implement PropertyLoadSaver.
   421  // key will be extracted from dst.
   422  //
   423  // If there is no such entity for the key, Get returns ErrNoSuchEntity.
   424  // The values of dst's unmatched struct fields are not modified, and matching slice-typed fields are not reset before appending to them.
   425  // In particular, it is recommended to pass a pointer to a zero valued struct on each Get call.
   426  func (bm *Boom) Get(dst interface{}) error {
   427  	dsts := []interface{}{dst}
   428  	err := bm.GetMulti(dsts)
   429  	if merr, ok := err.(datastore.MultiError); ok {
   430  		return merr[0]
   431  	} else if err != nil {
   432  		return err
   433  	}
   434  
   435  	return nil
   436  }
   437  
   438  // GetMulti is a batch version of Get.
   439  // key will be extracted from each struct of dst.
   440  //
   441  // dst must be a []S, []*S, []I or []P, for some struct type S, some interface type I, or some non-interface non-pointer type P such that P or *P implements PropertyLoadSaver.
   442  // If an []I, each element must be a valid dst for Get: it must be a struct pointer or implement PropertyLoadSaver.
   443  func (bm *Boom) GetMulti(dst interface{}) error {
   444  	keys, err := bm.extractKeys(dst)
   445  	if err != nil {
   446  		return err
   447  	}
   448  
   449  	return bm.Client.GetMulti(bm.Context, keys, dst)
   450  }
   451  
   452  // Put saves the entity src into the datastore.
   453  // key will be extract from src struct.
   454  // src must be a struct pointer or implement PropertyLoadSaver; if a struct pointer then any unexported fields of that struct will be skipped.
   455  // If k is an incomplete key, the returned key will be a unique key generated by the datastore,
   456  // and inject key to src struct.
   457  func (bm *Boom) Put(src interface{}) (datastore.Key, error) {
   458  	srcs := []interface{}{src}
   459  	keys, err := bm.PutMulti(srcs)
   460  	if merr, ok := err.(datastore.MultiError); ok {
   461  		return nil, merr[0]
   462  	} else if err != nil {
   463  		return nil, err
   464  	}
   465  
   466  	return keys[0], nil
   467  }
   468  
   469  // PutMulti is a batch version of Put.
   470  //
   471  // src must satisfy the same conditions as the dst argument to GetMulti.
   472  func (bm *Boom) PutMulti(src interface{}) ([]datastore.Key, error) {
   473  	keys, err := bm.extractKeys(src)
   474  	if err != nil {
   475  		return nil, err
   476  	}
   477  
   478  	keys, err = bm.Client.PutMulti(bm.Context, keys, src)
   479  	if err != nil {
   480  		return nil, err
   481  	}
   482  
   483  	v := reflect.Indirect(reflect.ValueOf(src))
   484  	for idx, key := range keys {
   485  		err = bm.setStructKey(v.Index(idx).Interface(), key)
   486  		if err != nil {
   487  			return nil, err
   488  		}
   489  	}
   490  
   491  	return keys, nil
   492  }
   493  
   494  // Delete deletes the entity.
   495  // key will be extract from src struct.
   496  func (bm *Boom) Delete(src interface{}) error {
   497  	srcs := []interface{}{src}
   498  	err := bm.DeleteMulti(srcs)
   499  	if merr, ok := err.(datastore.MultiError); ok {
   500  		return merr[0]
   501  	} else if err != nil {
   502  		return err
   503  	}
   504  
   505  	return nil
   506  }
   507  
   508  // DeleteMulti is a batch version of Delete.
   509  func (bm *Boom) DeleteMulti(src interface{}) error {
   510  	keys, err := bm.extractKeys(src)
   511  	if err != nil {
   512  		return err
   513  	}
   514  
   515  	return bm.Client.DeleteMulti(bm.Context, keys)
   516  }
   517  
   518  // NewTransaction starts a new transaction.
   519  func (bm *Boom) NewTransaction() (*Transaction, error) {
   520  	tx, err := bm.Client.NewTransaction(bm.Context)
   521  	if err != nil {
   522  		return nil, err
   523  	}
   524  
   525  	return &Transaction{bm: bm, tx: tx}, nil
   526  }
   527  
   528  // RunInTransaction runs f in a transaction. f is invoked with a Transaction that f should use for all the transaction's datastore operations.
   529  //
   530  // f must not call Commit or Rollback on the provided Transaction.
   531  //
   532  // If f returns nil, RunInTransaction commits the transaction, returning the Commit and a nil error if it succeeds.
   533  // If the commit fails due to a conflicting transaction, RunInTransaction gives up and returns ErrConcurrentTransaction immediately.
   534  // If you want to retry operation, You have to retry by yourself.
   535  //
   536  // If f returns non-nil, then the transaction will be rolled back and RunInTransaction will return the same error.
   537  //
   538  // Note that when f returns, the transaction is not committed. Calling code must not assume that any of f's changes have been committed until RunInTransaction returns nil.
   539  func (bm *Boom) RunInTransaction(f func(tx *Transaction) error) (datastore.Commit, error) {
   540  	var tx *Transaction
   541  	commit, err := bm.Client.RunInTransaction(bm.Context, func(origTx datastore.Transaction) error {
   542  		tx = &Transaction{bm: bm, tx: origTx}
   543  		return f(tx)
   544  	})
   545  	if err != nil {
   546  		return nil, err
   547  	}
   548  
   549  	for _, s := range tx.pendingKeysLater {
   550  		key := commit.Key(s.pendingKey)
   551  		err = tx.bm.setStructKey(s.src, key)
   552  		if err != nil {
   553  			return nil, err
   554  		}
   555  	}
   556  
   557  	return commit, nil
   558  }
   559  
   560  // Run runs the given query.
   561  func (bm *Boom) Run(q datastore.Query) *Iterator {
   562  	it := bm.Client.Run(bm.Context, q)
   563  	return &Iterator{bm: bm, it: it}
   564  }
   565  
   566  // Count returns the number of results for the given query.
   567  //
   568  // The running time and number of API calls made by Count scale linearly with with the sum of the query's offset and limit.
   569  // Unless the result count is expected to be small, it is best to specify a limit; otherwise Count will continue until it finishes counting or the provided context expires.
   570  func (bm *Boom) Count(q datastore.Query) (int, error) {
   571  	return bm.Client.Count(bm.Context, q)
   572  }
   573  
   574  // GetAll runs the provided query that it returns all entities that match that query, as well as appending the values to dst.
   575  //
   576  // dst must have type *[]S or *[]*S or *[]P, for some struct type S or some non-interface, non-pointer type P such that P or *P implements PropertyLoadSaver.
   577  //
   578  // As a special case, *PropertyList is an invalid type for dst, even though a PropertyList is a slice of structs.
   579  // It is treated as invalid to avoid being mistakenly passed when *[]PropertyList was intended.
   580  //
   581  // The keys are injected to each dst struct.
   582  //
   583  // If q is a “keys-only” query, GetAll ignores dst and only returns the keys.
   584  //
   585  // The running time and number of API calls made by GetAll scale linearly with with the sum of the query's offset and limit.
   586  // Unless the result count is expected to be small, it is best to specify a limit; otherwise GetAll will continue until it finishes collecting results or the provided context expires.
   587  func (bm *Boom) GetAll(q datastore.Query, dst interface{}) ([]datastore.Key, error) {
   588  	keys, err := bm.Client.GetAll(bm.Context, q, dst)
   589  	if err != nil {
   590  		return nil, err
   591  	}
   592  
   593  	if dst == nil {
   594  		return keys, nil
   595  	}
   596  
   597  	v := reflect.Indirect(reflect.ValueOf(dst))
   598  	for idx, key := range keys {
   599  		err = bm.setStructKey(v.Index(idx).Interface(), key)
   600  		if err != nil {
   601  			return nil, err
   602  		}
   603  	}
   604  
   605  	return keys, nil
   606  }
   607  
   608  // Batch creates batch mode objects.
   609  func (bm *Boom) Batch() *Batch {
   610  	b := bm.Client.Batch()
   611  	return &Batch{bm: bm, b: b}
   612  }
   613  
   614  // DecodeCursor from its base-64 string representation.
   615  func (bm *Boom) DecodeCursor(s string) (datastore.Cursor, error) {
   616  	return bm.Client.DecodeCursor(s)
   617  }
   618  
   619  // NewQuery creates a new Query for a specific entity kind.
   620  //
   621  // An empty kind means to return all entities, including entities created and managed by other App Engine features, and is called a kindless query.
   622  // Kindless queries cannot include filters or sort orders on property values.
   623  func (bm *Boom) NewQuery(k string) datastore.Query {
   624  	return bm.Client.NewQuery(k)
   625  }