github.com/go-generalize/volcago@v1.7.0/generator/testfiles/not_auto/task_gen.go (about)

     1  // Code generated by volcago. DO NOT EDIT.
     2  // generated version: devel
     3  package model
     4  
     5  import (
     6  	"context"
     7  
     8  	"cloud.google.com/go/firestore"
     9  	"golang.org/x/xerrors"
    10  	"google.golang.org/api/iterator"
    11  	"google.golang.org/grpc/codes"
    12  	"google.golang.org/grpc/status"
    13  )
    14  
    15  //go:generate mockgen -source $GOFILE -destination mock/mock_task_gen/mock_task_gen.go
    16  
    17  // TaskRepository - Repository of Task
    18  type TaskRepository interface {
    19  	// Single
    20  	Get(ctx context.Context, identity string, opts ...GetOption) (*Task, error)
    21  	GetWithDoc(ctx context.Context, doc *firestore.DocumentRef, opts ...GetOption) (*Task, error)
    22  	Insert(ctx context.Context, subject *Task) (_ string, err error)
    23  	Update(ctx context.Context, subject *Task) (err error)
    24  	StrictUpdate(ctx context.Context, id string, param *TaskUpdateParam, opts ...firestore.Precondition) error
    25  	Delete(ctx context.Context, subject *Task, opts ...DeleteOption) (err error)
    26  	DeleteByIdentity(ctx context.Context, identity string, opts ...DeleteOption) (err error)
    27  	// Multiple
    28  	GetMulti(ctx context.Context, identities []string, opts ...GetOption) ([]*Task, error)
    29  	InsertMulti(ctx context.Context, subjects []*Task) (_ []string, er error)
    30  	UpdateMulti(ctx context.Context, subjects []*Task) (er error)
    31  	DeleteMulti(ctx context.Context, subjects []*Task, opts ...DeleteOption) (er error)
    32  	DeleteMultiByIdentities(ctx context.Context, identities []string, opts ...DeleteOption) (er error)
    33  	// Single(Transaction)
    34  	GetWithTx(tx *firestore.Transaction, identity string, opts ...GetOption) (*Task, error)
    35  	GetWithDocWithTx(tx *firestore.Transaction, doc *firestore.DocumentRef, opts ...GetOption) (*Task, error)
    36  	InsertWithTx(ctx context.Context, tx *firestore.Transaction, subject *Task) (_ string, err error)
    37  	UpdateWithTx(ctx context.Context, tx *firestore.Transaction, subject *Task) (err error)
    38  	StrictUpdateWithTx(tx *firestore.Transaction, id string, param *TaskUpdateParam, opts ...firestore.Precondition) error
    39  	DeleteWithTx(ctx context.Context, tx *firestore.Transaction, subject *Task, opts ...DeleteOption) (err error)
    40  	DeleteByIdentityWithTx(ctx context.Context, tx *firestore.Transaction, identity string, opts ...DeleteOption) (err error)
    41  	// Multiple(Transaction)
    42  	GetMultiWithTx(tx *firestore.Transaction, identities []string, opts ...GetOption) ([]*Task, error)
    43  	InsertMultiWithTx(ctx context.Context, tx *firestore.Transaction, subjects []*Task) (_ []string, er error)
    44  	UpdateMultiWithTx(ctx context.Context, tx *firestore.Transaction, subjects []*Task) (er error)
    45  	DeleteMultiWithTx(ctx context.Context, tx *firestore.Transaction, subjects []*Task, opts ...DeleteOption) (er error)
    46  	DeleteMultiByIdentitiesWithTx(ctx context.Context, tx *firestore.Transaction, identities []string, opts ...DeleteOption) (er error)
    47  	// Search
    48  	Search(ctx context.Context, param *TaskSearchParam, q *firestore.Query) ([]*Task, error)
    49  	SearchWithTx(tx *firestore.Transaction, param *TaskSearchParam, q *firestore.Query) ([]*Task, error)
    50  	SearchByParam(ctx context.Context, param *TaskSearchParam) ([]*Task, *PagingResult, error)
    51  	SearchByParamWithTx(tx *firestore.Transaction, param *TaskSearchParam) ([]*Task, *PagingResult, error)
    52  	// misc
    53  	GetCollection() *firestore.CollectionRef
    54  	GetCollectionName() string
    55  	GetDocRef(identity string) *firestore.DocumentRef
    56  	RunInTransaction() func(ctx context.Context, f func(context.Context, *firestore.Transaction) error, opts ...firestore.TransactionOption) (err error)
    57  	// get by unique field
    58  	GetByDesc(ctx context.Context, description string) (*Task, error)
    59  	GetByDescWithTx(tx *firestore.Transaction, description string) (*Task, error)
    60  }
    61  
    62  // TaskRepositoryMiddleware - middleware of TaskRepository
    63  type TaskRepositoryMiddleware interface {
    64  	BeforeInsert(ctx context.Context, subject *Task) (bool, error)
    65  	BeforeUpdate(ctx context.Context, old, subject *Task) (bool, error)
    66  	BeforeDelete(ctx context.Context, subject *Task, opts ...DeleteOption) (bool, error)
    67  	BeforeDeleteByIdentity(ctx context.Context, identities []string, opts ...DeleteOption) (bool, error)
    68  }
    69  
    70  type taskRepository struct {
    71  	collectionName   string
    72  	firestoreClient  *firestore.Client
    73  	middleware       []TaskRepositoryMiddleware
    74  	uniqueRepository *uniqueRepository
    75  }
    76  
    77  // NewTaskRepository - constructor
    78  func NewTaskRepository(firestoreClient *firestore.Client, middleware ...TaskRepositoryMiddleware) TaskRepository {
    79  	return &taskRepository{
    80  		collectionName:   "Task",
    81  		firestoreClient:  firestoreClient,
    82  		middleware:       middleware,
    83  		uniqueRepository: newUniqueRepository(firestoreClient, "Task"),
    84  	}
    85  }
    86  
    87  func (repo *taskRepository) beforeInsert(ctx context.Context, subject *Task) error {
    88  	repo.uniqueRepository.setMiddleware(ctx)
    89  	err := repo.uniqueRepository.CheckUnique(ctx, nil, subject)
    90  	if err != nil {
    91  		return xerrors.Errorf("unique.middleware error: %w", err)
    92  	}
    93  
    94  	for _, m := range repo.middleware {
    95  		c, err := m.BeforeInsert(ctx, subject)
    96  		if err != nil {
    97  			return xerrors.Errorf("beforeInsert.middleware error: %w", err)
    98  		}
    99  		if !c {
   100  			continue
   101  		}
   102  	}
   103  
   104  	return nil
   105  }
   106  
   107  func (repo *taskRepository) beforeUpdate(ctx context.Context, old, subject *Task) error {
   108  	if ctx.Value(transactionInProgressKey{}) != nil && old == nil {
   109  		var err error
   110  		doc := repo.GetDocRef(subject.Identity)
   111  		old, err = repo.get(context.Background(), doc)
   112  		if err != nil {
   113  			if status.Code(err) == codes.NotFound {
   114  				return ErrNotFound
   115  			}
   116  			return xerrors.Errorf("error in Get method: %w", err)
   117  		}
   118  	}
   119  	repo.uniqueRepository.setMiddleware(ctx)
   120  	err := repo.uniqueRepository.CheckUnique(ctx, old, subject)
   121  	if err != nil {
   122  		return xerrors.Errorf("unique.middleware error: %w", err)
   123  	}
   124  
   125  	for _, m := range repo.middleware {
   126  		c, err := m.BeforeUpdate(ctx, old, subject)
   127  		if err != nil {
   128  			return xerrors.Errorf("beforeUpdate.middleware error: %w", err)
   129  		}
   130  		if !c {
   131  			continue
   132  		}
   133  	}
   134  
   135  	return nil
   136  }
   137  
   138  func (repo *taskRepository) beforeDelete(ctx context.Context, subject *Task, opts ...DeleteOption) error {
   139  	repo.uniqueRepository.setMiddleware(ctx)
   140  	err := repo.uniqueRepository.DeleteUnique(ctx, subject)
   141  	if err != nil {
   142  		return xerrors.Errorf("unique.middleware error: %w", err)
   143  	}
   144  
   145  	for _, m := range repo.middleware {
   146  		c, err := m.BeforeDelete(ctx, subject, opts...)
   147  		if err != nil {
   148  			return xerrors.Errorf("beforeDelete.middleware error: %w", err)
   149  		}
   150  		if !c {
   151  			continue
   152  		}
   153  	}
   154  
   155  	return nil
   156  }
   157  
   158  // GetCollection - *firestore.CollectionRef getter
   159  func (repo *taskRepository) GetCollection() *firestore.CollectionRef {
   160  	return repo.firestoreClient.Collection(repo.collectionName)
   161  }
   162  
   163  // GetCollectionName - CollectionName getter
   164  func (repo *taskRepository) GetCollectionName() string {
   165  	return repo.collectionName
   166  }
   167  
   168  // GetDocRef - *firestore.DocumentRef getter
   169  func (repo *taskRepository) GetDocRef(identity string) *firestore.DocumentRef {
   170  	return repo.GetCollection().Doc(identity)
   171  }
   172  
   173  // RunInTransaction - (*firestore.Client).RunTransaction getter
   174  func (repo *taskRepository) RunInTransaction() func(ctx context.Context, f func(context.Context, *firestore.Transaction) error, opts ...firestore.TransactionOption) (err error) {
   175  	return repo.firestoreClient.RunTransaction
   176  }
   177  
   178  // TaskSearchParam - params for search
   179  type TaskSearchParam struct {
   180  	Identity     *QueryChainer
   181  	Desc         *QueryChainer
   182  	Desc2        *QueryChainer
   183  	Created      *QueryChainer
   184  	ReservedDate *QueryChainer
   185  	Done         *QueryChainer
   186  	Done2        *QueryChainer
   187  	Count        *QueryChainer
   188  	Count64      *QueryChainer
   189  	NameList     *QueryChainer
   190  	Proportion   *QueryChainer
   191  	Geo          *QueryChainer
   192  	Sub          *QueryChainer
   193  	Flag         *QueryChainer
   194  
   195  	CursorKey   string
   196  	CursorLimit int
   197  }
   198  
   199  // TaskUpdateParam - params for strict updates
   200  type TaskUpdateParam struct {
   201  	Desc2        interface{}
   202  	Created      interface{}
   203  	ReservedDate interface{}
   204  	Done         interface{}
   205  	Done2        interface{}
   206  	Count        interface{}
   207  	Count64      interface{}
   208  	NameList     interface{}
   209  	Proportion   interface{}
   210  	Geo          interface{}
   211  	Sub          interface{}
   212  	Flag         interface{}
   213  }
   214  
   215  // Search - search documents
   216  // The third argument is firestore.Query, basically you can pass nil
   217  func (repo *taskRepository) Search(ctx context.Context, param *TaskSearchParam, q *firestore.Query) ([]*Task, error) {
   218  	return repo.search(ctx, param, q)
   219  }
   220  
   221  // SearchByParam - search documents by search param
   222  func (repo *taskRepository) SearchByParam(ctx context.Context, param *TaskSearchParam) ([]*Task, *PagingResult, error) {
   223  	return repo.searchByParam(ctx, param)
   224  }
   225  
   226  // Get - get `Task` by `Task.Identity`
   227  func (repo *taskRepository) Get(ctx context.Context, identity string, opts ...GetOption) (*Task, error) {
   228  	doc := repo.GetDocRef(identity)
   229  	return repo.get(ctx, doc, opts...)
   230  }
   231  
   232  // GetWithDoc - get `Task` by *firestore.DocumentRef
   233  func (repo *taskRepository) GetWithDoc(ctx context.Context, doc *firestore.DocumentRef, opts ...GetOption) (*Task, error) {
   234  	return repo.get(ctx, doc, opts...)
   235  }
   236  
   237  // Insert - insert of `Task`
   238  func (repo *taskRepository) Insert(ctx context.Context, subject *Task) (_ string, err error) {
   239  	if err := repo.beforeInsert(ctx, subject); err != nil {
   240  		return "", xerrors.Errorf("before insert error: %w", err)
   241  	}
   242  
   243  	return repo.insert(ctx, subject)
   244  }
   245  
   246  // Update - update of `Task`
   247  func (repo *taskRepository) Update(ctx context.Context, subject *Task) (err error) {
   248  	doc := repo.GetDocRef(subject.Identity)
   249  
   250  	old, err := repo.get(ctx, doc)
   251  	if err != nil {
   252  		if status.Code(err) == codes.NotFound {
   253  			return ErrNotFound
   254  		}
   255  		return xerrors.Errorf("error in Get method: %w", err)
   256  	}
   257  
   258  	if err := repo.beforeUpdate(ctx, old, subject); err != nil {
   259  		return xerrors.Errorf("before update error: %w", err)
   260  	}
   261  
   262  	return repo.update(ctx, subject)
   263  }
   264  
   265  // StrictUpdate - strict update of `Task`
   266  func (repo *taskRepository) StrictUpdate(ctx context.Context, id string, param *TaskUpdateParam, opts ...firestore.Precondition) error {
   267  	return repo.strictUpdate(ctx, id, param, opts...)
   268  }
   269  
   270  // Delete - delete of `Task`
   271  func (repo *taskRepository) Delete(ctx context.Context, subject *Task, opts ...DeleteOption) (err error) {
   272  	if err := repo.beforeDelete(ctx, subject, opts...); err != nil {
   273  		return xerrors.Errorf("before delete error: %w", err)
   274  	}
   275  
   276  	return repo.deleteByIdentity(ctx, subject.Identity)
   277  }
   278  
   279  // DeleteByIdentity - delete `Task` by `Task.Identity`
   280  func (repo *taskRepository) DeleteByIdentity(ctx context.Context, identity string, opts ...DeleteOption) (err error) {
   281  	subject, err := repo.Get(ctx, identity)
   282  	if err != nil {
   283  		return xerrors.Errorf("error in Get method: %w", err)
   284  	}
   285  
   286  	if err := repo.beforeDelete(ctx, subject, opts...); err != nil {
   287  		return xerrors.Errorf("before delete error: %w", err)
   288  	}
   289  
   290  	return repo.Delete(ctx, subject, opts...)
   291  }
   292  
   293  // GetMulti - get `Task` in bulk by array of `Task.Identity`
   294  func (repo *taskRepository) GetMulti(ctx context.Context, identities []string, opts ...GetOption) ([]*Task, error) {
   295  	return repo.getMulti(ctx, identities, opts...)
   296  }
   297  
   298  // InsertMulti - bulk insert of `Task`
   299  func (repo *taskRepository) InsertMulti(ctx context.Context, subjects []*Task) (_ []string, er error) {
   300  
   301  	identities := make([]string, 0, len(subjects))
   302  	batches := make([]*firestore.WriteBatch, 0)
   303  	batch := repo.firestoreClient.Batch()
   304  	collect := repo.GetCollection()
   305  
   306  	for i, subject := range subjects {
   307  		var ref *firestore.DocumentRef
   308  		if subject.Identity == "" {
   309  			ref = collect.NewDoc()
   310  			subject.Identity = ref.ID
   311  		} else {
   312  			ref = collect.Doc(subject.Identity)
   313  			if s, err := ref.Get(ctx); err == nil {
   314  				return nil, xerrors.Errorf("already exists [%v]: %#v", subject.Identity, s)
   315  			}
   316  		}
   317  
   318  		if err := repo.beforeInsert(ctx, subject); err != nil {
   319  			return nil, xerrors.Errorf("before insert error(%d) [%v]: %w", i, subject.Identity, err)
   320  		}
   321  
   322  		batch.Set(ref, subject)
   323  		identities = append(identities, ref.ID)
   324  		i++
   325  		if (i%500) == 0 && len(subjects) != i {
   326  			batches = append(batches, batch)
   327  			batch = repo.firestoreClient.Batch()
   328  		}
   329  	}
   330  	batches = append(batches, batch)
   331  
   332  	for _, b := range batches {
   333  		if _, err := b.Commit(ctx); err != nil {
   334  			return nil, xerrors.Errorf("error in Commit method: %w", err)
   335  		}
   336  	}
   337  
   338  	return identities, nil
   339  }
   340  
   341  // UpdateMulti - bulk update of `Task`
   342  func (repo *taskRepository) UpdateMulti(ctx context.Context, subjects []*Task) (er error) {
   343  
   344  	batches := make([]*firestore.WriteBatch, 0)
   345  	batch := repo.firestoreClient.Batch()
   346  	collect := repo.GetCollection()
   347  
   348  	for i, subject := range subjects {
   349  		ref := collect.Doc(subject.Identity)
   350  		snapShot, err := ref.Get(ctx)
   351  		if err != nil {
   352  			if status.Code(err) == codes.NotFound {
   353  				return xerrors.Errorf("not found [%v]: %w", subject.Identity, err)
   354  			}
   355  			return xerrors.Errorf("error in Get method [%v]: %w", subject.Identity, err)
   356  		}
   357  
   358  		old := new(Task)
   359  		if err = snapShot.DataTo(&old); err != nil {
   360  			return xerrors.Errorf("error in DataTo method: %w", err)
   361  		}
   362  
   363  		if err := repo.beforeUpdate(ctx, old, subject); err != nil {
   364  			return xerrors.Errorf("before update error(%d) [%v]: %w", i, subject.Identity, err)
   365  		}
   366  
   367  		batch.Set(ref, subject)
   368  		i++
   369  		if (i%500) == 0 && len(subjects) != i {
   370  			batches = append(batches, batch)
   371  			batch = repo.firestoreClient.Batch()
   372  		}
   373  	}
   374  	batches = append(batches, batch)
   375  
   376  	for _, b := range batches {
   377  		if _, err := b.Commit(ctx); err != nil {
   378  			return xerrors.Errorf("error in Commit method: %w", err)
   379  		}
   380  	}
   381  
   382  	return nil
   383  }
   384  
   385  // DeleteMulti - bulk delete of `Task`
   386  func (repo *taskRepository) DeleteMulti(ctx context.Context, subjects []*Task, opts ...DeleteOption) (er error) {
   387  
   388  	batches := make([]*firestore.WriteBatch, 0)
   389  	batch := repo.firestoreClient.Batch()
   390  	collect := repo.GetCollection()
   391  
   392  	for i, subject := range subjects {
   393  		ref := collect.Doc(subject.Identity)
   394  		if _, err := ref.Get(ctx); err != nil {
   395  			if status.Code(err) == codes.NotFound {
   396  				return xerrors.Errorf("not found [%v]: %w", subject.Identity, err)
   397  			}
   398  			return xerrors.Errorf("error in Get method [%v]: %w", subject.Identity, err)
   399  		}
   400  
   401  		if err := repo.beforeDelete(ctx, subject, opts...); err != nil {
   402  			return xerrors.Errorf("before delete error(%d) [%v]: %w", i, subject.Identity, err)
   403  		}
   404  
   405  		batch.Delete(ref)
   406  
   407  		i++
   408  		if (i%500) == 0 && len(subjects) != i {
   409  			batches = append(batches, batch)
   410  			batch = repo.firestoreClient.Batch()
   411  		}
   412  	}
   413  	batches = append(batches, batch)
   414  
   415  	for _, b := range batches {
   416  		if _, err := b.Commit(ctx); err != nil {
   417  			return xerrors.Errorf("error in Commit method: %w", err)
   418  		}
   419  	}
   420  
   421  	return nil
   422  }
   423  
   424  // DeleteMultiByIdentities - delete `Task` in bulk by array of `Task.Identity`
   425  func (repo *taskRepository) DeleteMultiByIdentities(ctx context.Context, identities []string, opts ...DeleteOption) (er error) {
   426  	subjects := make([]*Task, len(identities))
   427  
   428  	opt := GetOption{}
   429  	if len(opts) > 0 {
   430  		opt.IncludeSoftDeleted = opts[0].Mode == DeleteModeHard
   431  	}
   432  	for i, identity := range identities {
   433  		subject, err := repo.Get(ctx, identity, opt)
   434  		if err != nil {
   435  			return xerrors.Errorf("error in Get method: %w", err)
   436  		}
   437  		subjects[i] = subject
   438  	}
   439  
   440  	return repo.DeleteMulti(ctx, subjects, opts...)
   441  }
   442  
   443  // SearchWithTx - search documents in transaction
   444  func (repo *taskRepository) SearchWithTx(tx *firestore.Transaction, param *TaskSearchParam, q *firestore.Query) ([]*Task, error) {
   445  	return repo.search(tx, param, q)
   446  }
   447  
   448  // SearchByParamWithTx - search documents by search param in transaction
   449  func (repo *taskRepository) SearchByParamWithTx(tx *firestore.Transaction, param *TaskSearchParam) ([]*Task, *PagingResult, error) {
   450  	return repo.searchByParam(tx, param)
   451  }
   452  
   453  // GetWithTx - get `Task` by `Task.Identity` in transaction
   454  func (repo *taskRepository) GetWithTx(tx *firestore.Transaction, identity string, opts ...GetOption) (*Task, error) {
   455  	doc := repo.GetDocRef(identity)
   456  	return repo.get(tx, doc, opts...)
   457  }
   458  
   459  // GetWithDocWithTx - get `Task` by *firestore.DocumentRef in transaction
   460  func (repo *taskRepository) GetWithDocWithTx(tx *firestore.Transaction, doc *firestore.DocumentRef, opts ...GetOption) (*Task, error) {
   461  	return repo.get(tx, doc, opts...)
   462  }
   463  
   464  // InsertWithTx - insert of `Task` in transaction
   465  func (repo *taskRepository) InsertWithTx(ctx context.Context, tx *firestore.Transaction, subject *Task) (_ string, err error) {
   466  	if err := repo.beforeInsert(context.WithValue(ctx, transactionInProgressKey{}, tx), subject); err != nil {
   467  		return "", xerrors.Errorf("before insert error: %w", err)
   468  	}
   469  
   470  	return repo.insert(tx, subject)
   471  }
   472  
   473  // UpdateWithTx - update of `Task` in transaction
   474  func (repo *taskRepository) UpdateWithTx(ctx context.Context, tx *firestore.Transaction, subject *Task) (err error) {
   475  	if err := repo.beforeUpdate(context.WithValue(ctx, transactionInProgressKey{}, tx), nil, subject); err != nil {
   476  		return xerrors.Errorf("before update error: %w", err)
   477  	}
   478  
   479  	return repo.update(tx, subject)
   480  }
   481  
   482  // StrictUpdateWithTx - strict update of `Task` in transaction
   483  func (repo *taskRepository) StrictUpdateWithTx(tx *firestore.Transaction, id string, param *TaskUpdateParam, opts ...firestore.Precondition) error {
   484  	return repo.strictUpdate(tx, id, param, opts...)
   485  }
   486  
   487  // DeleteWithTx - delete of `Task` in transaction
   488  func (repo *taskRepository) DeleteWithTx(ctx context.Context, tx *firestore.Transaction, subject *Task, opts ...DeleteOption) (err error) {
   489  	if err := repo.beforeDelete(context.WithValue(ctx, transactionInProgressKey{}, tx), subject, opts...); err != nil {
   490  		return xerrors.Errorf("before delete error: %w", err)
   491  	}
   492  
   493  	return repo.deleteByIdentity(tx, subject.Identity)
   494  }
   495  
   496  // DeleteByIdentityWithTx - delete `Task` by `Task.Identity` in transaction
   497  func (repo *taskRepository) DeleteByIdentityWithTx(ctx context.Context, tx *firestore.Transaction, identity string, opts ...DeleteOption) (err error) {
   498  	subject, err := repo.Get(context.Background(), identity)
   499  	if err != nil {
   500  		return xerrors.Errorf("error in Get method: %w", err)
   501  	}
   502  
   503  	if err := repo.beforeDelete(context.WithValue(ctx, transactionInProgressKey{}, tx), subject, opts...); err != nil {
   504  		return xerrors.Errorf("before delete error: %w", err)
   505  	}
   506  
   507  	return repo.deleteByIdentity(tx, identity)
   508  }
   509  
   510  // GetMultiWithTx - get `Task` in bulk by array of `Task.Identity` in transaction
   511  func (repo *taskRepository) GetMultiWithTx(tx *firestore.Transaction, identities []string, opts ...GetOption) ([]*Task, error) {
   512  	return repo.getMulti(tx, identities, opts...)
   513  }
   514  
   515  // InsertMultiWithTx - bulk insert of `Task` in transaction
   516  func (repo *taskRepository) InsertMultiWithTx(ctx context.Context, tx *firestore.Transaction, subjects []*Task) (_ []string, er error) {
   517  
   518  	for i := range subjects {
   519  		if _, err := tx.Get(new(firestore.DocumentRef)); err == nil {
   520  			return nil, xerrors.Errorf("already exists(%d) [%v]: %w", i, subjects[i].Identity, err)
   521  		}
   522  	}
   523  
   524  	identities := make([]string, len(subjects))
   525  
   526  	for i := range subjects {
   527  		if err := repo.beforeInsert(ctx, subjects[i]); err != nil {
   528  			return nil, xerrors.Errorf("before insert error(%d) [%v]: %w", i, subjects[i].Identity, err)
   529  		}
   530  
   531  		identity, err := repo.insert(tx, subjects[i])
   532  		if err != nil {
   533  			return nil, xerrors.Errorf("error in insert method(%d) [%v]: %w", i, subjects[i].Identity, err)
   534  		}
   535  		identities[i] = identity
   536  	}
   537  
   538  	return identities, nil
   539  }
   540  
   541  // UpdateMultiWithTx - bulk update of `Task` in transaction
   542  func (repo *taskRepository) UpdateMultiWithTx(ctx context.Context, tx *firestore.Transaction, subjects []*Task) (er error) {
   543  	ctx = context.WithValue(ctx, transactionInProgressKey{}, tx)
   544  
   545  	for i := range subjects {
   546  		if err := repo.beforeUpdate(ctx, nil, subjects[i]); err != nil {
   547  			return xerrors.Errorf("before update error(%d) [%v]: %w", i, subjects[i].Identity, err)
   548  		}
   549  	}
   550  
   551  	for i := range subjects {
   552  		if err := repo.update(tx, subjects[i]); err != nil {
   553  			return xerrors.Errorf("error in update method(%d) [%v]: %w", i, subjects[i].Identity, err)
   554  		}
   555  	}
   556  
   557  	return nil
   558  }
   559  
   560  // DeleteMultiWithTx - bulk delete of `Task` in transaction
   561  func (repo *taskRepository) DeleteMultiWithTx(ctx context.Context, tx *firestore.Transaction, subjects []*Task, opts ...DeleteOption) (er error) {
   562  
   563  	var isHardDeleteMode bool
   564  	if len(opts) > 0 {
   565  		isHardDeleteMode = opts[0].Mode == DeleteModeHard
   566  	}
   567  	opt := GetOption{
   568  		IncludeSoftDeleted: isHardDeleteMode,
   569  	}
   570  	for i := range subjects {
   571  		dr := repo.GetDocRef(subjects[i].Identity)
   572  		if _, err := repo.get(context.Background(), dr, opt); err != nil {
   573  			if status.Code(err) == codes.NotFound {
   574  				return xerrors.Errorf("not found(%d) [%v]", i, subjects[i].Identity)
   575  			}
   576  			return xerrors.Errorf("error in get method(%d) [%v]: %w", i, subjects[i].Identity, err)
   577  		}
   578  
   579  		if err := repo.beforeDelete(context.WithValue(ctx, transactionInProgressKey{}, tx), subjects[i], opts...); err != nil {
   580  			return xerrors.Errorf("before delete error(%d) [%v]: %w", i, subjects[i].Identity, err)
   581  		}
   582  	}
   583  
   584  	for i := range subjects {
   585  		if err := repo.deleteByIdentity(tx, subjects[i].Identity); err != nil {
   586  			return xerrors.Errorf("error in delete method(%d) [%v]: %w", i, subjects[i].Identity, err)
   587  		}
   588  	}
   589  
   590  	return nil
   591  }
   592  
   593  // DeleteMultiByIdentityWithTx - delete `Task` in bulk by array of `Task.Identity` in transaction
   594  func (repo *taskRepository) DeleteMultiByIdentitiesWithTx(ctx context.Context, tx *firestore.Transaction, identities []string, opts ...DeleteOption) (er error) {
   595  
   596  	for i := range identities {
   597  		dr := repo.GetDocRef(identities[i])
   598  		subject, err := repo.get(context.Background(), dr)
   599  		if err != nil {
   600  			if status.Code(err) == codes.NotFound {
   601  				return xerrors.Errorf("not found(%d) [%v]", i, identities[i])
   602  			}
   603  			return xerrors.Errorf("error in get method(%d) [%v]: %w", i, identities[i], err)
   604  		}
   605  
   606  		if err := repo.beforeDelete(context.WithValue(ctx, transactionInProgressKey{}, tx), subject, opts...); err != nil {
   607  			return xerrors.Errorf("before delete error(%d) [%v]: %w", i, subject.Identity, err)
   608  		}
   609  	}
   610  
   611  	for i := range identities {
   612  		if err := repo.deleteByIdentity(tx, identities[i]); err != nil {
   613  			return xerrors.Errorf("error in delete method(%d) [%v]: %w", i, identities[i], err)
   614  		}
   615  	}
   616  
   617  	return nil
   618  }
   619  
   620  func (repo *taskRepository) get(v interface{}, doc *firestore.DocumentRef, _ ...GetOption) (*Task, error) {
   621  	var (
   622  		snapShot *firestore.DocumentSnapshot
   623  		err      error
   624  	)
   625  
   626  	switch x := v.(type) {
   627  	case *firestore.Transaction:
   628  		snapShot, err = x.Get(doc)
   629  	case context.Context:
   630  		snapShot, err = doc.Get(x)
   631  	default:
   632  		return nil, xerrors.Errorf("invalid type: %v", x)
   633  	}
   634  
   635  	if err != nil {
   636  		if status.Code(err) == codes.NotFound {
   637  			return nil, ErrNotFound
   638  		}
   639  		return nil, xerrors.Errorf("error in Get method: %w", err)
   640  	}
   641  
   642  	subject := new(Task)
   643  	if err := snapShot.DataTo(&subject); err != nil {
   644  		return nil, xerrors.Errorf("error in DataTo method: %w", err)
   645  	}
   646  
   647  	subject.Identity = snapShot.Ref.ID
   648  
   649  	return subject, nil
   650  }
   651  
   652  func (repo *taskRepository) getMulti(v interface{}, identities []string, _ ...GetOption) ([]*Task, error) {
   653  	var (
   654  		snapShots []*firestore.DocumentSnapshot
   655  		err       error
   656  		collect   = repo.GetCollection()
   657  		drs       = make([]*firestore.DocumentRef, len(identities))
   658  	)
   659  
   660  	for i, identity := range identities {
   661  		ref := collect.Doc(identity)
   662  		drs[i] = ref
   663  	}
   664  
   665  	switch x := v.(type) {
   666  	case *firestore.Transaction:
   667  		snapShots, err = x.GetAll(drs)
   668  	case context.Context:
   669  		snapShots, err = repo.firestoreClient.GetAll(x, drs)
   670  	default:
   671  		return nil, xerrors.Errorf("invalid type: %v", v)
   672  	}
   673  
   674  	if err != nil {
   675  		return nil, xerrors.Errorf("error in GetAll method: %w", err)
   676  	}
   677  
   678  	subjects := make([]*Task, 0, len(identities))
   679  	mErr := NewMultiErrors()
   680  	for i, snapShot := range snapShots {
   681  		if !snapShot.Exists() {
   682  			mErr = append(mErr, NewMultiError(i, ErrNotFound))
   683  			continue
   684  		}
   685  
   686  		subject := new(Task)
   687  		if err = snapShot.DataTo(&subject); err != nil {
   688  			return nil, xerrors.Errorf("error in DataTo method: %w", err)
   689  		}
   690  
   691  		subject.Identity = snapShot.Ref.ID
   692  		subjects = append(subjects, subject)
   693  	}
   694  
   695  	if len(mErr) == 0 {
   696  		return subjects, nil
   697  	}
   698  
   699  	return subjects, mErr
   700  }
   701  
   702  func (repo *taskRepository) insert(v interface{}, subject *Task) (string, error) {
   703  	var (
   704  		dr  = repo.GetDocRef(subject.Identity)
   705  		err error
   706  	)
   707  
   708  	switch x := v.(type) {
   709  	case *firestore.Transaction:
   710  		err = x.Create(dr, subject)
   711  	case context.Context:
   712  		_, err = dr.Create(x, subject)
   713  	default:
   714  		return "", xerrors.Errorf("invalid type: %v", v)
   715  	}
   716  
   717  	if err != nil {
   718  		if status.Code(err) == codes.AlreadyExists {
   719  			return "", xerrors.Errorf("error in Create method: err=%+v: %w", err, ErrAlreadyExists)
   720  		}
   721  		return "", xerrors.Errorf("error in Create method: %w", err)
   722  	}
   723  
   724  	subject.Identity = dr.ID
   725  
   726  	return dr.ID, nil
   727  }
   728  
   729  func (repo *taskRepository) update(v interface{}, subject *Task) error {
   730  	var (
   731  		dr  = repo.GetDocRef(subject.Identity)
   732  		err error
   733  	)
   734  
   735  	switch x := v.(type) {
   736  	case *firestore.Transaction:
   737  		err = x.Set(dr, subject)
   738  	case context.Context:
   739  		_, err = dr.Set(x, subject)
   740  	default:
   741  		return xerrors.Errorf("invalid type: %v", v)
   742  	}
   743  
   744  	if err != nil {
   745  		return xerrors.Errorf("error in Set method: %w", err)
   746  	}
   747  
   748  	return nil
   749  }
   750  
   751  func (repo *taskRepository) strictUpdate(v interface{}, id string, param *TaskUpdateParam, opts ...firestore.Precondition) error {
   752  	var (
   753  		dr  = repo.GetDocRef(id)
   754  		err error
   755  	)
   756  
   757  	updates := updater(Task{}, param)
   758  
   759  	switch x := v.(type) {
   760  	case *firestore.Transaction:
   761  		err = x.Update(dr, updates, opts...)
   762  	case context.Context:
   763  		_, err = dr.Update(x, updates, opts...)
   764  	default:
   765  		return xerrors.Errorf("invalid type: %v", v)
   766  	}
   767  
   768  	if err != nil {
   769  		return xerrors.Errorf("error in Update method: %w", err)
   770  	}
   771  
   772  	return nil
   773  }
   774  
   775  func (repo *taskRepository) deleteByIdentity(v interface{}, identity string) error {
   776  	dr := repo.GetDocRef(identity)
   777  	var err error
   778  
   779  	switch x := v.(type) {
   780  	case *firestore.Transaction:
   781  		err = x.Delete(dr, firestore.Exists)
   782  	case context.Context:
   783  		_, err = dr.Delete(x, firestore.Exists)
   784  	default:
   785  		return xerrors.Errorf("invalid type: %v", v)
   786  	}
   787  
   788  	if err != nil {
   789  		return xerrors.Errorf("error in Delete method: %w", err)
   790  	}
   791  
   792  	return nil
   793  }
   794  
   795  func (repo *taskRepository) runQuery(v interface{}, query firestore.Query) ([]*Task, error) {
   796  	var iter *firestore.DocumentIterator
   797  
   798  	switch x := v.(type) {
   799  	case *firestore.Transaction:
   800  		iter = x.Documents(query)
   801  	case context.Context:
   802  		iter = query.Documents(x)
   803  	default:
   804  		return nil, xerrors.Errorf("invalid type: %v", v)
   805  	}
   806  
   807  	defer iter.Stop()
   808  
   809  	subjects := make([]*Task, 0)
   810  
   811  	for {
   812  		doc, err := iter.Next()
   813  		if err == iterator.Done {
   814  			break
   815  		}
   816  		if err != nil {
   817  			return nil, xerrors.Errorf("error in Next method: %w", err)
   818  		}
   819  
   820  		subject := new(Task)
   821  
   822  		if err = doc.DataTo(&subject); err != nil {
   823  			return nil, xerrors.Errorf("error in DataTo method: %w", err)
   824  		}
   825  
   826  		subject.Identity = doc.Ref.ID
   827  		subjects = append(subjects, subject)
   828  	}
   829  
   830  	return subjects, nil
   831  }
   832  
   833  // BUG(54m): there may be potential bugs
   834  func (repo *taskRepository) searchByParam(v interface{}, param *TaskSearchParam) ([]*Task, *PagingResult, error) {
   835  	query := func() firestore.Query {
   836  		return repo.GetCollection().Query
   837  	}()
   838  	if param.Identity != nil {
   839  		for _, chain := range param.Identity.QueryGroup {
   840  			var value interface{}
   841  			switch val := chain.Value.(type) {
   842  			case string:
   843  				value = repo.GetDocRef(val)
   844  			case []string:
   845  				docRefs := make([]*firestore.DocumentRef, len(val))
   846  				for i := range val {
   847  					docRefs[i] = repo.GetDocRef(val[i])
   848  				}
   849  				value = docRefs
   850  			default:
   851  				return nil, nil, xerrors.Errorf("document id can only be of type `string` and `[]string`. value: %#v", chain.Value)
   852  			}
   853  			query = query.Where(firestore.DocumentID, chain.Operator, value)
   854  		}
   855  		if direction := param.Identity.OrderByDirection; direction > 0 {
   856  			query = query.OrderBy(firestore.DocumentID, direction)
   857  			query = param.Identity.BuildCursorQuery(query)
   858  		}
   859  	}
   860  	if param.Desc != nil {
   861  		for _, chain := range param.Desc.QueryGroup {
   862  			query = query.Where("description", chain.Operator, chain.Value)
   863  		}
   864  		if direction := param.Desc.OrderByDirection; direction > 0 {
   865  			query = query.OrderBy("description", direction)
   866  			query = param.Desc.BuildCursorQuery(query)
   867  		}
   868  	}
   869  	if param.Desc2 != nil {
   870  		for _, chain := range param.Desc2.QueryGroup {
   871  			query = query.Where("desc2", chain.Operator, chain.Value)
   872  		}
   873  		if direction := param.Desc2.OrderByDirection; direction > 0 {
   874  			query = query.OrderBy("desc2", direction)
   875  			query = param.Desc2.BuildCursorQuery(query)
   876  		}
   877  	}
   878  	if param.Created != nil {
   879  		for _, chain := range param.Created.QueryGroup {
   880  			query = query.Where("created", chain.Operator, chain.Value)
   881  		}
   882  		if direction := param.Created.OrderByDirection; direction > 0 {
   883  			query = query.OrderBy("created", direction)
   884  			query = param.Created.BuildCursorQuery(query)
   885  		}
   886  	}
   887  	if param.ReservedDate != nil {
   888  		for _, chain := range param.ReservedDate.QueryGroup {
   889  			query = query.Where("reservedDate", chain.Operator, chain.Value)
   890  		}
   891  		if direction := param.ReservedDate.OrderByDirection; direction > 0 {
   892  			query = query.OrderBy("reservedDate", direction)
   893  			query = param.ReservedDate.BuildCursorQuery(query)
   894  		}
   895  	}
   896  	if param.Done != nil {
   897  		for _, chain := range param.Done.QueryGroup {
   898  			query = query.Where("done", chain.Operator, chain.Value)
   899  		}
   900  		if direction := param.Done.OrderByDirection; direction > 0 {
   901  			query = query.OrderBy("done", direction)
   902  			query = param.Done.BuildCursorQuery(query)
   903  		}
   904  	}
   905  	if param.Done2 != nil {
   906  		for _, chain := range param.Done2.QueryGroup {
   907  			query = query.Where("done2", chain.Operator, chain.Value)
   908  		}
   909  		if direction := param.Done2.OrderByDirection; direction > 0 {
   910  			query = query.OrderBy("done2", direction)
   911  			query = param.Done2.BuildCursorQuery(query)
   912  		}
   913  	}
   914  	if param.Count != nil {
   915  		for _, chain := range param.Count.QueryGroup {
   916  			query = query.Where("count", chain.Operator, chain.Value)
   917  		}
   918  		if direction := param.Count.OrderByDirection; direction > 0 {
   919  			query = query.OrderBy("count", direction)
   920  			query = param.Count.BuildCursorQuery(query)
   921  		}
   922  	}
   923  	if param.Count64 != nil {
   924  		for _, chain := range param.Count64.QueryGroup {
   925  			query = query.Where("count64", chain.Operator, chain.Value)
   926  		}
   927  		if direction := param.Count64.OrderByDirection; direction > 0 {
   928  			query = query.OrderBy("count64", direction)
   929  			query = param.Count64.BuildCursorQuery(query)
   930  		}
   931  	}
   932  	if param.NameList != nil {
   933  		for _, chain := range param.NameList.QueryGroup {
   934  			query = query.Where("nameList", chain.Operator, chain.Value)
   935  		}
   936  	}
   937  	if param.Proportion != nil {
   938  		for _, chain := range param.Proportion.QueryGroup {
   939  			query = query.Where("proportion", chain.Operator, chain.Value)
   940  		}
   941  		if direction := param.Proportion.OrderByDirection; direction > 0 {
   942  			query = query.OrderBy("proportion", direction)
   943  			query = param.Proportion.BuildCursorQuery(query)
   944  		}
   945  	}
   946  	if param.Geo != nil {
   947  		for _, chain := range param.Geo.QueryGroup {
   948  			query = query.Where("geo", chain.Operator, chain.Value)
   949  		}
   950  		if direction := param.Geo.OrderByDirection; direction > 0 {
   951  			query = query.OrderBy("geo", direction)
   952  			query = param.Geo.BuildCursorQuery(query)
   953  		}
   954  	}
   955  	if param.Sub != nil {
   956  		for _, chain := range param.Sub.QueryGroup {
   957  			query = query.Where("sub", chain.Operator, chain.Value)
   958  		}
   959  		if direction := param.Sub.OrderByDirection; direction > 0 {
   960  			query = query.OrderBy("sub", direction)
   961  			query = param.Sub.BuildCursorQuery(query)
   962  		}
   963  	}
   964  	if param.Flag != nil {
   965  		for _, chain := range param.Flag.QueryGroup {
   966  			query = query.Where("flag", chain.Operator, chain.Value)
   967  		}
   968  		if direction := param.Flag.OrderByDirection; direction > 0 {
   969  			query = query.OrderBy("flag", direction)
   970  			query = param.Flag.BuildCursorQuery(query)
   971  		}
   972  	}
   973  
   974  	limit := param.CursorLimit + 1
   975  
   976  	if param.CursorKey != "" {
   977  		var (
   978  			ds  *firestore.DocumentSnapshot
   979  			err error
   980  		)
   981  		switch x := v.(type) {
   982  		case *firestore.Transaction:
   983  			ds, err = x.Get(repo.GetDocRef(param.CursorKey))
   984  		case context.Context:
   985  			ds, err = repo.GetDocRef(param.CursorKey).Get(x)
   986  		default:
   987  			return nil, nil, xerrors.Errorf("invalid x type: %v", v)
   988  		}
   989  		if err != nil {
   990  			if status.Code(err) == codes.NotFound {
   991  				return nil, nil, ErrNotFound
   992  			}
   993  			return nil, nil, xerrors.Errorf("error in Get method: %w", err)
   994  		}
   995  		query = query.StartAt(ds)
   996  	}
   997  
   998  	if limit > 1 {
   999  		query = query.Limit(limit)
  1000  	}
  1001  
  1002  	subjects, err := repo.runQuery(v, query)
  1003  	if err != nil {
  1004  		return nil, nil, xerrors.Errorf("error in runQuery method: %w", err)
  1005  	}
  1006  
  1007  	pagingResult := &PagingResult{
  1008  		Length: len(subjects),
  1009  	}
  1010  	if limit > 1 && limit == pagingResult.Length {
  1011  		next := pagingResult.Length - 1
  1012  		pagingResult.NextCursorKey = subjects[next].Identity
  1013  		subjects = subjects[:next]
  1014  		pagingResult.Length--
  1015  	}
  1016  
  1017  	return subjects, pagingResult, nil
  1018  }
  1019  
  1020  func (repo *taskRepository) search(v interface{}, param *TaskSearchParam, q *firestore.Query) ([]*Task, error) {
  1021  	if (param == nil && q == nil) || (param != nil && q != nil) {
  1022  		return nil, xerrors.New("either one should be nil")
  1023  	}
  1024  
  1025  	query := func() firestore.Query {
  1026  		if q != nil {
  1027  			return *q
  1028  		}
  1029  		return repo.GetCollection().Query
  1030  	}()
  1031  
  1032  	if q == nil {
  1033  		subjects, _, err := repo.searchByParam(v, param)
  1034  		if err != nil {
  1035  			return nil, xerrors.Errorf("error in searchByParam method: %w", err)
  1036  		}
  1037  
  1038  		return subjects, nil
  1039  	}
  1040  
  1041  	return repo.runQuery(v, query)
  1042  }
  1043  
  1044  // GetByDesc - get by Desc
  1045  func (repo *taskRepository) GetByDesc(ctx context.Context, description string) (*Task, error) {
  1046  	return repo.getByXXX(ctx, "description", description)
  1047  }
  1048  
  1049  // GetByDescWithTx - get by Desc in transaction
  1050  func (repo *taskRepository) GetByDescWithTx(tx *firestore.Transaction, description string) (*Task, error) {
  1051  	return repo.getByXXX(tx, "description", description)
  1052  }
  1053  
  1054  func (repo *taskRepository) getByXXX(v interface{}, field, value string) (*Task, error) {
  1055  	query := repo.GetCollection().Query.Where(field, OpTypeEqual, value).Limit(1)
  1056  	results, err := repo.runQuery(v, query)
  1057  	if err != nil {
  1058  		return nil, xerrors.Errorf("failed to run query: %w", err)
  1059  	} else if len(results) == 0 {
  1060  		return nil, ErrNotFound
  1061  	}
  1062  	return results[0], nil
  1063  }