package app import ( "context" "fmt" "gitea.dwysokinski.me/twhelp/corev3/internal/domain" ) type ServerRepository interface { CreateOrUpdate(ctx context.Context, params ...domain.CreateServerParams) error List(ctx context.Context, params domain.ListServersParams) (domain.Servers, error) } type ServerService struct { repo ServerRepository twSvc TWService publisher ServerPublisher } func NewServerService(repo ServerRepository, twSvc TWService, publisher ServerPublisher) *ServerService { return &ServerService{repo: repo, twSvc: twSvc, publisher: publisher} } func (svc *ServerService) Sync(ctx context.Context, payload domain.SyncServersCmdPayload) error { versionCode := payload.VersionCode() openServers, err := svc.twSvc.GetOpenServers(ctx, payload.URL().String()) if err != nil { return fmt.Errorf("couldn't get open servers for version code %s: %w", versionCode, err) } specialServers, err := svc.listAllSpecial(ctx, versionCode) if err != nil { return fmt.Errorf("couldn't list special servers with version code %s: %w", versionCode, err) } currentlyStoredOpenServers, err := svc.listAllOpen(ctx, versionCode) if err != nil { return fmt.Errorf("couldn't list open servers with version code %s: %w", versionCode, err) } openServersWithoutSpecial := openServers.FilterOutSpecial(specialServers) serversToBeClosed, err := currentlyStoredOpenServers.Close(openServersWithoutSpecial) if err != nil { return fmt.Errorf("couldn't close servers: %w", err) } params, err := domain.NewCreateServerParams(append(openServersWithoutSpecial, serversToBeClosed...), versionCode) if err != nil { return err } if err = svc.repo.CreateOrUpdate(ctx, params...); err != nil { return err } payloads, err := domain.NewServerSyncedEventPayloads(openServersWithoutSpecial, versionCode) if err != nil { return fmt.Errorf("couldn't construct server synced event payloads: %w", err) } return svc.publisher.EventSynced(ctx, payloads...) } func (svc *ServerService) listAllSpecial(ctx context.Context, versionCode string) (domain.Servers, error) { params := domain.NewListServersParams() if err := params.SetVersionCodes([]string{versionCode}); err != nil { return nil, err } if err := params.SetSpecial(domain.NullBool{ Value: true, Valid: true, }); err != nil { return nil, err } return svc.ListAll(ctx, params) } func (svc *ServerService) listAllOpen(ctx context.Context, versionCode string) (domain.Servers, error) { params := domain.NewListServersParams() if err := params.SetVersionCodes([]string{versionCode}); err != nil { return nil, err } if err := params.SetOpen(domain.NullBool{ Value: true, Valid: true, }); err != nil { return nil, err } return svc.ListAll(ctx, params) } // ListAll retrieves all servers from the database based on the given params in an optimal way. // You can't specify a custom limit/offset/sort/keyGT for this operation. func (svc *ServerService) ListAll(ctx context.Context, params domain.ListServersParams) (domain.Servers, error) { if err := params.SetOffset(0); err != nil { return nil, err } if err := params.SetLimit(domain.ServerListMaxLimit); err != nil { return nil, err } if err := params.SetSort([]domain.ServerSort{domain.ServerSortKeyASC}); err != nil { return nil, err } var servers domain.Servers for { ss, err := svc.repo.List(ctx, params) if err != nil { return nil, err } if len(ss) == 0 { return servers, nil } servers = append(servers, ss...) if err = params.SetKeyGT(domain.NullString{ Value: ss[len(ss)-1].Key(), Valid: true, }); err != nil { return nil, err } } }