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

     1  // Code generated by volcago. DO NOT EDIT.
     2  // generated version: devel
     3  package model
     4  
     5  import (
     6  	"context"
     7  	"crypto/md5"
     8  	"fmt"
     9  	"reflect"
    10  	"strings"
    11  
    12  	"cloud.google.com/go/firestore"
    13  	"golang.org/x/xerrors"
    14  	"google.golang.org/grpc/codes"
    15  	"google.golang.org/grpc/status"
    16  )
    17  
    18  // Unique - Collections for unique constraints
    19  type Unique struct {
    20  	ID         string `firestore:"-"`
    21  	Collection string
    22  	Data       string
    23  	Value      string
    24  }
    25  
    26  // UniqueRepositoryMiddleware - middleware
    27  type UniqueRepositoryMiddleware interface {
    28  	WrapError(ctx context.Context, err error, uniques []*Unique) error
    29  }
    30  
    31  type uniqueRepository struct {
    32  	collectionName   string
    33  	targetCollection string
    34  	firestoreClient  *firestore.Client
    35  	middleware       []UniqueRepositoryMiddleware
    36  }
    37  
    38  func newUniqueRepository(firestoreClient *firestore.Client, collection string) *uniqueRepository {
    39  	return &uniqueRepository{
    40  		collectionName:   "Unique",
    41  		targetCollection: collection,
    42  		firestoreClient:  firestoreClient,
    43  	}
    44  }
    45  
    46  func (repo *uniqueRepository) setMiddleware(ctx context.Context) {
    47  	if len(repo.middleware) > 0 {
    48  		return
    49  	}
    50  	if m, ok := ctx.Value(UniqueMiddlewareKey{}).(UniqueRepositoryMiddleware); ok {
    51  		repo.middleware = append(repo.middleware, m)
    52  	}
    53  }
    54  
    55  func (repo *uniqueRepository) wrapError(ctx context.Context, err error, uniques []*Unique) error {
    56  	for _, m := range repo.middleware {
    57  		if err = m.WrapError(ctx, err, uniques); err != nil {
    58  			return xerrors.Errorf("wrap error middleware: %w", err)
    59  		}
    60  	}
    61  	return err
    62  }
    63  
    64  func (repo *uniqueRepository) getUniqueItems(value interface{}) ([]*Unique, bool) {
    65  	rv := reflect.Indirect(reflect.ValueOf(value))
    66  	rt := rv.Type()
    67  	uniques := make([]*Unique, 0)
    68  	for i := 0; i < rt.NumField(); i++ {
    69  		f := rt.Field(i)
    70  		fieldName := f.Name
    71  
    72  		field := rv.FieldByName(fieldName)
    73  		if !field.IsValid() {
    74  			continue
    75  		}
    76  
    77  		if f.Anonymous {
    78  			if items, ok := repo.getUniqueItems(field.Interface()); ok {
    79  				uniques = append(uniques, items...)
    80  			}
    81  			continue
    82  		}
    83  
    84  		if field.Kind() != reflect.String {
    85  			continue
    86  		}
    87  
    88  		fieldValue := field.String()
    89  		if len(fieldValue) == 0 {
    90  			continue
    91  		}
    92  
    93  		if _, hasUnique := f.Tag.Lookup("unique"); !hasUnique {
    94  			continue
    95  		}
    96  
    97  		keySet := []string{
    98  			repo.targetCollection,
    99  			fieldName,
   100  			fieldValue,
   101  		}
   102  		key := strings.Join(keySet, "#")
   103  
   104  		u := &Unique{
   105  			ID:         fmt.Sprintf("%x", md5.Sum([]byte(key))),
   106  			Collection: repo.targetCollection,
   107  			Data:       fieldName,
   108  			Value:      fieldValue,
   109  		}
   110  
   111  		uniques = append(uniques, u)
   112  	}
   113  
   114  	return uniques, len(uniques) > 0
   115  }
   116  
   117  // CheckUnique - unique constraint check(Insert/Update)
   118  func (repo *uniqueRepository) CheckUnique(ctx context.Context, old, subject interface{}) error {
   119  	if subject == nil {
   120  		return xerrors.New("nil is not allowed")
   121  	}
   122  
   123  	bg := context.Background()
   124  	cb := func(cx context.Context, tx *firestore.Transaction) (err error) {
   125  		sustain := make(map[Unique]struct{}, 0)
   126  		deleteTarget := make(map[Unique]struct{}, 0)
   127  
   128  		switch {
   129  		case old != nil:
   130  			if rt1, rt2 := reflect.TypeOf(old), reflect.TypeOf(subject); rt1 != rt2 {
   131  				return xerrors.Errorf("different type: %v != %v", rt1, rt2)
   132  			}
   133  
   134  			items, ok := repo.getUniqueItems(old)
   135  			if !ok {
   136  				break
   137  			}
   138  
   139  			for _, unique := range items {
   140  				u, err := repo.getWithoutTx(bg, unique.ID)
   141  				if err != nil {
   142  					if xerrors.Is(err, ErrNotFound) {
   143  						continue
   144  					}
   145  					return xerrors.Errorf("failed to get: %w", err)
   146  				}
   147  
   148  				if _, ok := sustain[*u]; !ok {
   149  					sustain[*u] = struct{}{}
   150  				}
   151  
   152  				if _, ok := deleteTarget[*u]; !ok {
   153  					deleteTarget[*u] = struct{}{}
   154  				}
   155  			}
   156  		}
   157  
   158  		items, ok := repo.getUniqueItems(subject)
   159  		if !ok {
   160  			// NOTE(54m): If the struct does not have a `unique` tag
   161  			return nil
   162  		}
   163  
   164  		surveyTarget := make([]*Unique, 0)
   165  		for _, u := range items {
   166  			if _, ok := deleteTarget[*u]; ok {
   167  				delete(deleteTarget, *u)
   168  				continue
   169  			}
   170  
   171  			// NOTE(54m): Extract those that violate unique constraints, excluding existing ones
   172  			if _, ok := sustain[*u]; !ok {
   173  				surveyTarget = append(surveyTarget, u)
   174  			}
   175  		}
   176  
   177  		// NOTE(54m): Check unique items
   178  		{
   179  			uniques := make([]*Unique, 0)
   180  			for _, unique := range surveyTarget {
   181  				u, err := repo.getWithoutTx(bg, unique.ID)
   182  				if err != nil {
   183  					if xerrors.Is(err, ErrNotFound) {
   184  						continue
   185  					}
   186  					return xerrors.Errorf("failed to get(Tx): %w", err)
   187  				}
   188  
   189  				uniques = append(uniques, u)
   190  			}
   191  
   192  			if len(uniques) > 0 {
   193  				return repo.wrapError(cx, ErrUniqueConstraint, uniques)
   194  			}
   195  		}
   196  
   197  		// NOTE(54m): Delete unique items
   198  		for u := range deleteTarget {
   199  			delete(sustain, u)
   200  			if err := repo.deleteByID(tx, u.ID); err != nil {
   201  				return xerrors.Errorf("failed to delete(Tx): %w", err)
   202  			}
   203  		}
   204  
   205  		// NOTE(54m): Insert unique item
   206  		for _, u := range surveyTarget {
   207  			if err := repo.insert(tx, u); err != nil {
   208  				return xerrors.Errorf("failed to insert(Tx): %w", err)
   209  			}
   210  		}
   211  
   212  		return nil
   213  	}
   214  
   215  	var err error
   216  	if tx := ctx.Value(transactionInProgressKey{}); tx != nil {
   217  		err = cb(ctx, tx.(*firestore.Transaction))
   218  	} else {
   219  		err = repo.firestoreClient.RunTransaction(ctx, cb)
   220  	}
   221  
   222  	if err != nil {
   223  		if status.Code(err) == codes.AlreadyExists {
   224  			return repo.wrapError(ctx, ErrUniqueConstraint, []*Unique{&Unique{}})
   225  		}
   226  		return xerrors.Errorf("could not run transaction: %w", err)
   227  	}
   228  
   229  	return nil
   230  }
   231  
   232  // DeleteUnique - delete the document for unique constraints
   233  func (repo *uniqueRepository) DeleteUnique(ctx context.Context, subject interface{}) error {
   234  	if subject == nil {
   235  		return xerrors.New("nil is not allowed")
   236  	}
   237  
   238  	bg := context.Background()
   239  	cb := func(cx context.Context, tx *firestore.Transaction) error {
   240  		deleteTarget := make(map[Unique]struct{}, 0)
   241  
   242  		items, ok := repo.getUniqueItems(subject)
   243  		if !ok {
   244  			return nil
   245  		}
   246  
   247  		for _, unique := range items {
   248  			u, err := repo.getWithoutTx(bg, unique.ID)
   249  			if err != nil {
   250  				if xerrors.Is(err, ErrNotFound) {
   251  					continue
   252  				}
   253  				return xerrors.Errorf("failed to get: %w", err)
   254  			}
   255  
   256  			if _, ok := deleteTarget[*u]; !ok {
   257  				deleteTarget[*u] = struct{}{}
   258  			}
   259  		}
   260  
   261  		// NOTE(54m): Delete unique items
   262  		for u := range deleteTarget {
   263  			if err := repo.deleteByID(tx, u.ID); err != nil {
   264  				return xerrors.Errorf("failed to delete(Tx): %w", err)
   265  			}
   266  		}
   267  
   268  		return nil
   269  	}
   270  
   271  	var err error
   272  	if tx := ctx.Value(transactionInProgressKey{}); tx != nil {
   273  		err = cb(ctx, tx.(*firestore.Transaction))
   274  	} else {
   275  		err = repo.firestoreClient.RunTransaction(ctx, cb)
   276  	}
   277  
   278  	if err != nil {
   279  		return xerrors.Errorf("could not run transaction: %w", err)
   280  	}
   281  
   282  	return nil
   283  }
   284  
   285  func (repo *uniqueRepository) getCollection() *firestore.CollectionRef {
   286  	return repo.firestoreClient.Collection(repo.collectionName)
   287  }
   288  
   289  func (repo *uniqueRepository) getDocRef(id string) *firestore.DocumentRef {
   290  	return repo.getCollection().Doc(id)
   291  }
   292  
   293  func (repo *uniqueRepository) get(tx *firestore.Transaction, id string) (*Unique, error) {
   294  	doc := repo.getDocRef(id)
   295  
   296  	snapShot, err := tx.Get(doc)
   297  	if err != nil {
   298  		if status.Code(err) == codes.NotFound {
   299  			return nil, ErrNotFound
   300  		}
   301  		return nil, xerrors.Errorf("error in Get method(Tx): %w", err)
   302  	}
   303  
   304  	subject := new(Unique)
   305  	if err := snapShot.DataTo(&subject); err != nil {
   306  		return nil, xerrors.Errorf("error in DataTo method: %w", err)
   307  	}
   308  	subject.ID = snapShot.Ref.ID
   309  
   310  	return subject, nil
   311  }
   312  
   313  func (repo *uniqueRepository) getWithoutTx(ctx context.Context, id string) (*Unique, error) {
   314  	doc := repo.getDocRef(id)
   315  
   316  	snapShot, err := doc.Get(ctx)
   317  	if err != nil {
   318  		if status.Code(err) == codes.NotFound {
   319  			return nil, ErrNotFound
   320  		}
   321  		return nil, xerrors.Errorf("error in Get method: %w", err)
   322  	}
   323  
   324  	subject := new(Unique)
   325  	if err := snapShot.DataTo(&subject); err != nil {
   326  		return nil, xerrors.Errorf("error in DataTo method: %w", err)
   327  	}
   328  	subject.ID = snapShot.Ref.ID
   329  
   330  	return subject, nil
   331  }
   332  
   333  func (repo *uniqueRepository) insert(tx *firestore.Transaction, subject *Unique) error {
   334  	dr := repo.getDocRef(subject.ID)
   335  
   336  	if err := tx.Create(dr, subject); err != nil {
   337  		return xerrors.Errorf("error in Create method(Tx): %w", err)
   338  	}
   339  	subject.ID = dr.ID
   340  
   341  	return nil
   342  }
   343  
   344  func (repo *uniqueRepository) deleteByID(tx *firestore.Transaction, id string) error {
   345  	dr := repo.getDocRef(id)
   346  
   347  	if err := tx.Delete(dr, firestore.Exists); err != nil {
   348  		return xerrors.Errorf("error in Delete method(Tx): %w", err)
   349  	}
   350  
   351  	return nil
   352  }