package app import ( "context" "errors" "fmt" "time" "gitea.dwysokinski.me/twhelp/corev3/internal/domain" ) type EnnoblementRepository interface { Create(ctx context.Context, params ...domain.CreateEnnoblementParams) error List(ctx context.Context, params domain.ListEnnoblementsParams) (domain.ListEnnoblementsResult, error) } type EnnoblementService struct { repo EnnoblementRepository twSvc TWService pub EnnoblementPublisher } func NewEnnoblementService(repo EnnoblementRepository, twSvc TWService, pub EnnoblementPublisher) *EnnoblementService { return &EnnoblementService{repo: repo, twSvc: twSvc, pub: pub} } func (svc *EnnoblementService) Sync( ctx context.Context, syncEnnoblementsCmdPayload domain.SyncEnnoblementsCmdPayload, ) error { serverURL := syncEnnoblementsCmdPayload.ServerURL() serverKey := syncEnnoblementsCmdPayload.ServerKey() latestEnnoblement, err := svc.getLatest(ctx, serverKey) if err != nil && !errors.Is(err, errEnnoblementNotFound) { return fmt.Errorf("%s: %w", serverKey, err) } var since time.Time if err == nil { since = latestEnnoblement.CreatedAt() } ennoblements, err := svc.twSvc.GetEnnoblements(ctx, serverURL, since) if err != nil { return fmt.Errorf("%s: couldn't get ennoblements: %w", serverKey, err) } if err = svc.create(ctx, serverKey, ennoblements); err != nil { return fmt.Errorf("%s: couldn't create ennoblements: %w", serverKey, err) } payload, err := domain.NewEnnoblementsSyncedEventPayload( serverKey, serverURL, syncEnnoblementsCmdPayload.VersionCode(), ) if err != nil { return fmt.Errorf("%s: couldn't construct domain.EnnoblementsSyncedEventPayload %w", serverKey, err) } if err = svc.pub.EventSynced(ctx, payload); err != nil { return fmt.Errorf("%s: %w", serverKey, err) } return nil } var errEnnoblementNotFound = errors.New("ennoblement not found") func (svc *EnnoblementService) getLatest(ctx context.Context, serverKey string) (domain.Ennoblement, error) { params := domain.NewListEnnoblementsParams() if err := params.SetLimit(1); err != nil { return domain.Ennoblement{}, err } if err := params.SetServerKeys([]string{serverKey}); err != nil { return domain.Ennoblement{}, err } if err := params.SetSort([]domain.EnnoblementSort{domain.EnnoblementSortCreatedAtDESC}); err != nil { return domain.Ennoblement{}, err } res, err := svc.repo.List(ctx, params) if err != nil { return domain.Ennoblement{}, err } ennoblements := res.Ennoblements() if len(ennoblements) == 0 { return domain.Ennoblement{}, errEnnoblementNotFound } return ennoblements[0], nil } const ennoblementCreateChunkSize = 500 func (svc *EnnoblementService) create( ctx context.Context, serverKey string, ennoblements domain.BaseEnnoblements, ) error { for i := 0; i < len(ennoblements); i += ennoblementCreateChunkSize { end := i + ennoblementCreateChunkSize if end > len(ennoblements) { end = len(ennoblements) } createParams, err := domain.NewCreateEnnoblementParams(serverKey, ennoblements[i:end]) if err != nil { return err } if err = svc.repo.Create(ctx, createParams...); err != nil { return err } } return nil } func (svc *EnnoblementService) List( ctx context.Context, params domain.ListEnnoblementsParams, ) (domain.ListEnnoblementsResult, error) { return svc.repo.List(ctx, params) }