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 }