fix: blank page loading filters (#107)

* fix(filters): load indexers separate

* feat: add ctx to filter related db methods
This commit is contained in:
Ludvig Lundgren 2022-02-03 21:58:41 +01:00 committed by GitHub
parent 10a2ab2c96
commit 26f558859a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 90 additions and 115 deletions

View file

@ -16,7 +16,7 @@ func NewActionRepo(db *SqliteDB) domain.ActionRepo {
return &ActionRepo{db: db} return &ActionRepo{db: db}
} }
func (r *ActionRepo) FindByFilterID(filterID int) ([]domain.Action, error) { func (r *ActionRepo) FindByFilterID(ctx context.Context, filterID int) ([]domain.Action, error) {
//r.db.lock.RLock() //r.db.lock.RLock()
//defer r.db.lock.RUnlock() //defer r.db.lock.RUnlock()

View file

@ -19,11 +19,11 @@ func NewFilterRepo(db *SqliteDB) domain.FilterRepo {
return &FilterRepo{db: db} return &FilterRepo{db: db}
} }
func (r *FilterRepo) ListFilters() ([]domain.Filter, error) { func (r *FilterRepo) ListFilters(ctx context.Context) ([]domain.Filter, error) {
//r.db.lock.RLock() //r.db.lock.RLock()
//defer r.db.lock.RUnlock() //defer r.db.lock.RUnlock()
rows, err := r.db.handler.Query("SELECT id, enabled, name, match_releases, except_releases, created_at, updated_at FROM filter ORDER BY name ASC") rows, err := r.db.handler.QueryContext(ctx, "SELECT id, enabled, name, match_releases, except_releases, created_at, updated_at FROM filter ORDER BY name ASC")
if err != nil { if err != nil {
log.Error().Stack().Err(err).Msg("filters_list: error query data") log.Error().Stack().Err(err).Msg("filters_list: error query data")
return nil, err return nil, err
@ -54,18 +54,16 @@ func (r *FilterRepo) ListFilters() ([]domain.Filter, error) {
return filters, nil return filters, nil
} }
func (r *FilterRepo) FindByID(filterID int) (*domain.Filter, error) { func (r *FilterRepo) FindByID(ctx context.Context, filterID int) (*domain.Filter, error) {
//r.db.lock.RLock() //r.db.lock.RLock()
//defer r.db.lock.RUnlock() //defer r.db.lock.RUnlock()
row := r.db.handler.QueryRow("SELECT id, enabled, name, min_size, max_size, delay, match_releases, except_releases, use_regex, match_release_groups, except_release_groups, scene, freeleech, freeleech_percent, shows, seasons, episodes, resolutions, codecs, sources, containers, match_hdr, except_hdr, years, artists, albums, release_types_match, formats, quality, media, log_score, has_log, has_cue, perfect_flac, match_categories, except_categories, match_uploaders, except_uploaders, tags, except_tags, created_at, updated_at FROM filter WHERE id = ?", filterID) row := r.db.handler.QueryRowContext(ctx, "SELECT id, enabled, name, min_size, max_size, delay, match_releases, except_releases, use_regex, match_release_groups, except_release_groups, scene, freeleech, freeleech_percent, shows, seasons, episodes, resolutions, codecs, sources, containers, match_hdr, except_hdr, years, artists, albums, release_types_match, formats, quality, media, log_score, has_log, has_cue, perfect_flac, match_categories, except_categories, match_uploaders, except_uploaders, tags, except_tags, created_at, updated_at FROM filter WHERE id = ?", filterID)
var f domain.Filter
if err := row.Err(); err != nil { if err := row.Err(); err != nil {
return nil, err return nil, err
} }
var f domain.Filter
var minSize, maxSize, matchReleases, exceptReleases, matchReleaseGroups, exceptReleaseGroups, freeleechPercent, shows, seasons, episodes, years, artists, albums, matchCategories, exceptCategories, matchUploaders, exceptUploaders, tags, exceptTags sql.NullString var minSize, maxSize, matchReleases, exceptReleases, matchReleaseGroups, exceptReleaseGroups, freeleechPercent, shows, seasons, episodes, years, artists, albums, matchCategories, exceptCategories, matchUploaders, exceptUploaders, tags, exceptTags sql.NullString
var useRegex, scene, freeleech, hasLog, hasCue, perfectFlac sql.NullBool var useRegex, scene, freeleech, hasLog, hasCue, perfectFlac sql.NullBool
var delay, logScore sql.NullInt32 var delay, logScore sql.NullInt32

View file

@ -64,7 +64,8 @@ func (r *IndexerRepo) List() ([]domain.Indexer, error) {
rows, err := r.db.handler.Query("SELECT id, enabled, name, identifier, settings FROM indexer ORDER BY name ASC") rows, err := r.db.handler.Query("SELECT id, enabled, name, identifier, settings FROM indexer ORDER BY name ASC")
if err != nil { if err != nil {
log.Fatal().Err(err) log.Error().Stack().Err(err).Msg("indexer.list: error query indexer")
return nil, err
} }
defer rows.Close() defer rows.Close()
@ -78,8 +79,6 @@ func (r *IndexerRepo) List() ([]domain.Indexer, error) {
if err := rows.Scan(&f.ID, &f.Enabled, &f.Name, &f.Identifier, &settings); err != nil { if err := rows.Scan(&f.ID, &f.Enabled, &f.Name, &f.Identifier, &settings); err != nil {
log.Error().Stack().Err(err).Msg("indexer.list: error scanning data to struct") log.Error().Stack().Err(err).Msg("indexer.list: error scanning data to struct")
}
if err != nil {
return nil, err return nil, err
} }
@ -100,17 +99,18 @@ func (r *IndexerRepo) List() ([]domain.Indexer, error) {
return indexers, nil return indexers, nil
} }
func (r *IndexerRepo) FindByFilterID(id int) ([]domain.Indexer, error) { func (r *IndexerRepo) FindByFilterID(ctx context.Context, id int) ([]domain.Indexer, error) {
//r.db.lock.RLock() //r.db.lock.RLock()
//defer r.db.lock.RUnlock() //defer r.db.lock.RUnlock()
rows, err := r.db.handler.Query(` rows, err := r.db.handler.QueryContext(ctx, `
SELECT i.id, i.enabled, i.name, i.identifier SELECT i.id, i.enabled, i.name, i.identifier
FROM indexer i FROM indexer i
JOIN filter_indexer fi on i.id = fi.indexer_id JOIN filter_indexer fi on i.id = fi.indexer_id
WHERE fi.filter_id = ?`, id) WHERE fi.filter_id = ?`, id)
if err != nil { if err != nil {
log.Fatal().Err(err) log.Error().Stack().Err(err).Msg("indexer.find_by_filter_id: error query indexer")
return nil, err
} }
defer rows.Close() defer rows.Close()
@ -123,9 +123,7 @@ func (r *IndexerRepo) FindByFilterID(id int) ([]domain.Indexer, error) {
//var settingsMap map[string]string //var settingsMap map[string]string
if err := rows.Scan(&f.ID, &f.Enabled, &f.Name, &f.Identifier); err != nil { if err := rows.Scan(&f.ID, &f.Enabled, &f.Name, &f.Identifier); err != nil {
log.Error().Stack().Err(err).Msg("indexer.list: error scanning data to struct") log.Error().Stack().Err(err).Msg("indexer.find_by_filter_id: error scanning data to struct")
}
if err != nil {
return nil, err return nil, err
} }

View file

@ -6,7 +6,7 @@ type ActionRepo interface {
Store(ctx context.Context, action Action) (*Action, error) Store(ctx context.Context, action Action) (*Action, error)
StoreFilterActions(ctx context.Context, actions []Action, filterID int64) ([]Action, error) StoreFilterActions(ctx context.Context, actions []Action, filterID int64) ([]Action, error)
DeleteByFilterID(ctx context.Context, filterID int) error DeleteByFilterID(ctx context.Context, filterID int) error
FindByFilterID(filterID int) ([]Action, error) FindByFilterID(ctx context.Context, filterID int) ([]Action, error)
List() ([]Action, error) List() ([]Action, error)
Delete(actionID int) error Delete(actionID int) error
ToggleEnabled(actionID int) error ToggleEnabled(actionID int) error

View file

@ -11,9 +11,9 @@ https://autodl-community.github.io/autodl-irssi/configuration/filter/
*/ */
type FilterRepo interface { type FilterRepo interface {
FindByID(filterID int) (*Filter, error) FindByID(ctx context.Context, filterID int) (*Filter, error)
FindByIndexerIdentifier(indexer string) ([]Filter, error) FindByIndexerIdentifier(indexer string) ([]Filter, error)
ListFilters() ([]Filter, error) ListFilters(ctx context.Context) ([]Filter, error)
Store(filter Filter) (*Filter, error) Store(filter Filter) (*Filter, error)
Update(ctx context.Context, filter Filter) (*Filter, error) Update(ctx context.Context, filter Filter) (*Filter, error)
ToggleEnabled(ctx context.Context, filterID int, enabled bool) error ToggleEnabled(ctx context.Context, filterID int, enabled bool) error

View file

@ -11,7 +11,7 @@ type IndexerRepo interface {
Update(indexer Indexer) (*Indexer, error) Update(indexer Indexer) (*Indexer, error)
List() ([]Indexer, error) List() ([]Indexer, error)
Delete(ctx context.Context, id int) error Delete(ctx context.Context, id int) error
FindByFilterID(id int) ([]Indexer, error) FindByFilterID(ctx context.Context, id int) ([]Indexer, error)
} }
type Indexer struct { type Indexer struct {

View file

@ -13,10 +13,10 @@ import (
) )
type Service interface { type Service interface {
FindByID(filterID int) (*domain.Filter, error) FindByID(ctx context.Context, filterID int) (*domain.Filter, error)
FindByIndexerIdentifier(indexer string) ([]domain.Filter, error) FindByIndexerIdentifier(indexer string) ([]domain.Filter, error)
FindAndCheckFilters(release *domain.Release) (bool, *domain.Filter, error) FindAndCheckFilters(release *domain.Release) (bool, *domain.Filter, error)
ListFilters() ([]domain.Filter, error) ListFilters(ctx context.Context) ([]domain.Filter, error)
Store(filter domain.Filter) (*domain.Filter, error) Store(filter domain.Filter) (*domain.Filter, error)
Update(ctx context.Context, filter domain.Filter) (*domain.Filter, error) Update(ctx context.Context, filter domain.Filter) (*domain.Filter, error)
ToggleEnabled(ctx context.Context, filterID int, enabled bool) error ToggleEnabled(ctx context.Context, filterID int, enabled bool) error
@ -39,9 +39,9 @@ func NewService(repo domain.FilterRepo, actionRepo domain.ActionRepo, apiService
} }
} }
func (s *service) ListFilters() ([]domain.Filter, error) { func (s *service) ListFilters(ctx context.Context) ([]domain.Filter, error) {
// get filters // get filters
filters, err := s.repo.ListFilters() filters, err := s.repo.ListFilters(ctx)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -49,7 +49,7 @@ func (s *service) ListFilters() ([]domain.Filter, error) {
var ret []domain.Filter var ret []domain.Filter
for _, filter := range filters { for _, filter := range filters {
indexers, err := s.indexerSvc.FindByFilterID(filter.ID) indexers, err := s.indexerSvc.FindByFilterID(ctx, filter.ID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -61,22 +61,22 @@ func (s *service) ListFilters() ([]domain.Filter, error) {
return ret, nil return ret, nil
} }
func (s *service) FindByID(filterID int) (*domain.Filter, error) { func (s *service) FindByID(ctx context.Context, filterID int) (*domain.Filter, error) {
// find filter // find filter
filter, err := s.repo.FindByID(filterID) filter, err := s.repo.FindByID(ctx, filterID)
if err != nil { if err != nil {
return nil, err return nil, err
} }
// find actions and attach // find actions and attach
actions, err := s.actionRepo.FindByFilterID(filter.ID) actions, err := s.actionRepo.FindByFilterID(ctx, filter.ID)
if err != nil { if err != nil {
log.Error().Msgf("could not find filter actions: %+v", &filter.ID) log.Error().Msgf("could not find filter actions: %+v", &filter.ID)
} }
filter.Actions = actions filter.Actions = actions
// find indexers and attach // find indexers and attach
indexers, err := s.indexerSvc.FindByFilterID(filter.ID) indexers, err := s.indexerSvc.FindByFilterID(ctx, filter.ID)
if err != nil { if err != nil {
log.Error().Err(err).Msgf("could not find indexers for filter: %+v", &filter.Name) log.Error().Err(err).Msgf("could not find indexers for filter: %+v", &filter.Name)
return nil, err return nil, err
@ -276,7 +276,7 @@ func (s *service) FindAndCheckFilters(release *domain.Release) (bool, *domain.Fi
} }
// found matching filter, lets find the filter actions and attach // found matching filter, lets find the filter actions and attach
actions, err := s.actionRepo.FindByFilterID(f.ID) actions, err := s.actionRepo.FindByFilterID(context.TODO(), f.ID)
if err != nil { if err != nil {
log.Error().Err(err).Msgf("could not find actions for filter: %+v", f.Name) log.Error().Err(err).Msgf("could not find actions for filter: %+v", f.Name)
} }

View file

@ -12,8 +12,8 @@ import (
) )
type filterService interface { type filterService interface {
ListFilters() ([]domain.Filter, error) ListFilters(ctx context.Context) ([]domain.Filter, error)
FindByID(filterID int) (*domain.Filter, error) FindByID(ctx context.Context, filterID int) (*domain.Filter, error)
Store(filter domain.Filter) (*domain.Filter, error) Store(filter domain.Filter) (*domain.Filter, error)
Delete(ctx context.Context, filterID int) error Delete(ctx context.Context, filterID int) error
Update(ctx context.Context, filter domain.Filter) (*domain.Filter, error) Update(ctx context.Context, filter domain.Filter) (*domain.Filter, error)
@ -44,7 +44,7 @@ func (h filterHandler) Routes(r chi.Router) {
func (h filterHandler) getFilters(w http.ResponseWriter, r *http.Request) { func (h filterHandler) getFilters(w http.ResponseWriter, r *http.Request) {
ctx := r.Context() ctx := r.Context()
trackers, err := h.service.ListFilters() trackers, err := h.service.ListFilters(ctx)
if err != nil { if err != nil {
// //
} }
@ -60,7 +60,7 @@ func (h filterHandler) getByID(w http.ResponseWriter, r *http.Request) {
id, _ := strconv.Atoi(filterID) id, _ := strconv.Atoi(filterID)
filter, err := h.service.FindByID(id) filter, err := h.service.FindByID(ctx, id)
if err != nil { if err != nil {
h.encoder.StatusNotFound(ctx, w) h.encoder.StatusNotFound(ctx, w)
return return
@ -69,22 +69,6 @@ func (h filterHandler) getByID(w http.ResponseWriter, r *http.Request) {
h.encoder.StatusResponse(ctx, w, filter, http.StatusOK) h.encoder.StatusResponse(ctx, w, filter, http.StatusOK)
} }
func (h filterHandler) storeFilterAction(w http.ResponseWriter, r *http.Request) {
var (
ctx = r.Context()
filterID = chi.URLParam(r, "filterID")
)
id, _ := strconv.Atoi(filterID)
filter, err := h.service.FindByID(id)
if err != nil {
//
}
h.encoder.StatusResponse(ctx, w, filter, http.StatusCreated)
}
func (h filterHandler) store(w http.ResponseWriter, r *http.Request) { func (h filterHandler) store(w http.ResponseWriter, r *http.Request) {
var ( var (
ctx = r.Context() ctx = r.Context()

View file

@ -16,7 +16,7 @@ type Service interface {
Store(indexer domain.Indexer) (*domain.Indexer, error) Store(indexer domain.Indexer) (*domain.Indexer, error)
Update(indexer domain.Indexer) (*domain.Indexer, error) Update(indexer domain.Indexer) (*domain.Indexer, error)
Delete(ctx context.Context, id int) error Delete(ctx context.Context, id int) error
FindByFilterID(id int) ([]domain.Indexer, error) FindByFilterID(ctx context.Context, id int) ([]domain.Indexer, error)
List() ([]domain.Indexer, error) List() ([]domain.Indexer, error)
GetAll() ([]*domain.IndexerDefinition, error) GetAll() ([]*domain.IndexerDefinition, error)
GetTemplates() ([]domain.IndexerDefinition, error) GetTemplates() ([]domain.IndexerDefinition, error)
@ -92,8 +92,8 @@ func (s *service) Delete(ctx context.Context, id int) error {
return nil return nil
} }
func (s *service) FindByFilterID(id int) ([]domain.Indexer, error) { func (s *service) FindByFilterID(ctx context.Context, id int) ([]domain.Indexer, error) {
filters, err := s.repo.FindByFilterID(id) filters, err := s.repo.FindByFilterID(ctx, id)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -115,7 +115,7 @@ export default function FilterDetails() {
let history = useHistory(); let history = useHistory();
let { filterId }: any = useParams(); let { filterId }: any = useParams();
const { isLoading, data } = useQuery<Filter, Error>(['filter', parseInt(filterId)], () => APIClient.filters.getByID(parseInt(filterId)), const { isLoading, data: filter } = useQuery<Filter, Error>(['filter', parseInt(filterId)], () => APIClient.filters.getByID(parseInt(filterId)),
{ {
retry: false, retry: false,
refetchOnWindowFocus: false, refetchOnWindowFocus: false,
@ -125,12 +125,6 @@ export default function FilterDetails() {
}, },
) )
const { data: indexers } = useQuery<Indexer[], Error>(["filter", "indexer_list"], APIClient.indexers.getOptions,
{
refetchOnWindowFocus: false
}
)
const updateMutation = useMutation((filter: Filter) => APIClient.filters.update(filter), { const updateMutation = useMutation((filter: Filter) => APIClient.filters.update(filter), {
onSuccess: (filter) => { onSuccess: (filter) => {
// queryClient.setQueryData(['filter', filter.id], data) // queryClient.setQueryData(['filter', filter.id], data)
@ -142,7 +136,7 @@ export default function FilterDetails() {
const deleteMutation = useMutation((id: number) => APIClient.filters.delete(id), { const deleteMutation = useMutation((id: number) => APIClient.filters.delete(id), {
onSuccess: () => { onSuccess: () => {
toast.custom((t) => <Toast type="success" body={`${data?.name} was deleted`} t={t} />) toast.custom((t) => <Toast type="success" body={`${filter?.name} was deleted`} t={t} />)
// redirect // redirect
history.push("/filters") history.push("/filters")
@ -153,7 +147,7 @@ export default function FilterDetails() {
return null return null
} }
if (!data) { if (!filter) {
return null return null
} }
@ -162,7 +156,7 @@ export default function FilterDetails() {
} }
const deleteAction = () => { const deleteAction = () => {
deleteMutation.mutate(data.id) deleteMutation.mutate(filter.id)
} }
const handleMobileNav = (e: any, href: string) => { const handleMobileNav = (e: any, href: string) => {
@ -183,7 +177,7 @@ export default function FilterDetails() {
</NavLink> </NavLink>
</h1> </h1>
<ChevronRightIcon className="h-6 w-6 text-gray-500" aria-hidden="true" /> <ChevronRightIcon className="h-6 w-6 text-gray-500" aria-hidden="true" />
<h1 className="text-3xl font-bold text-white capitalize">{data.name}</h1> <h1 className="text-3xl font-bold text-white capitalize">{filter.name}</h1>
</div> </div>
</header> </header>
<div className="max-w-7xl mx-auto pb-12 px-4 sm:px-6 lg:px-8"> <div className="max-w-7xl mx-auto pb-12 px-4 sm:px-6 lg:px-8">
@ -220,46 +214,46 @@ export default function FilterDetails() {
<Formik <Formik
initialValues={{ initialValues={{
id: data.id, id: filter.id,
name: data.name, name: filter.name,
enabled: data.enabled || false, enabled: filter.enabled || false,
min_size: data.min_size, min_size: filter.min_size,
max_size: data.max_size, max_size: filter.max_size,
delay: data.delay, delay: filter.delay,
shows: data.shows, shows: filter.shows,
years: data.years, years: filter.years,
resolutions: data.resolutions || [], resolutions: filter.resolutions || [],
sources: data.sources || [], sources: filter.sources || [],
codecs: data.codecs || [], codecs: filter.codecs || [],
containers: data.containers || [], containers: filter.containers || [],
match_hdr: data.match_hdr || [], match_hdr: filter.match_hdr || [],
except_hdr: data.except_hdr || [], except_hdr: filter.except_hdr || [],
seasons: data.seasons, seasons: filter.seasons,
episodes: data.episodes, episodes: filter.episodes,
match_releases: data.match_releases, match_releases: filter.match_releases,
except_releases: data.except_releases, except_releases: filter.except_releases,
match_release_groups: data.match_release_groups, match_release_groups: filter.match_release_groups,
except_release_groups: data.except_release_groups, except_release_groups: filter.except_release_groups,
match_categories: data.match_categories, match_categories: filter.match_categories,
except_categories: data.except_categories, except_categories: filter.except_categories,
tags: data.tags, tags: filter.tags,
except_tags: data.except_tags, except_tags: filter.except_tags,
match_uploaders: data.match_uploaders, match_uploaders: filter.match_uploaders,
except_uploaders: data.except_uploaders, except_uploaders: filter.except_uploaders,
freeleech: data.freeleech, freeleech: filter.freeleech,
freeleech_percent: data.freeleech_percent, freeleech_percent: filter.freeleech_percent,
indexers: data.indexers || [], indexers: filter.indexers || [],
actions: data.actions || [], actions: filter.actions || [],
formats: data.formats || [], formats: filter.formats || [],
quality: data.quality || [], quality: filter.quality || [],
media: data.media || [], media: filter.media || [],
match_release_types: data.match_release_types || [], match_release_types: filter.match_release_types || [],
log_score: data.log_score, log_score: filter.log_score,
log: data.log, log: filter.log,
cue: data.cue, cue: filter.cue,
perfect_flac: data.perfect_flac, perfect_flac: filter.perfect_flac,
artists: data.artists, artists: filter.artists,
albums: data.albums, albums: filter.albums,
} as Filter} } as Filter}
onSubmit={handleSubmit} onSubmit={handleSubmit}
> >
@ -267,7 +261,7 @@ export default function FilterDetails() {
<Form> <Form>
<RouteSwitch> <RouteSwitch>
<Route exact path={url}> <Route exact path={url}>
<General indexers={indexers as any} /> <General />
</Route> </Route>
<Route path={`${url}/movies-tv`}> <Route path={`${url}/movies-tv`}>
@ -283,7 +277,7 @@ export default function FilterDetails() {
</Route> </Route>
<Route path={`${url}/actions`}> <Route path={`${url}/actions`}>
<FilterActions filter={data} values={values} /> <FilterActions filter={filter} values={values} />
</Route> </Route>
</RouteSwitch> </RouteSwitch>
@ -303,13 +297,14 @@ export default function FilterDetails() {
) )
} }
interface GeneralProps { function General() {
indexers: Indexer[]; const { isLoading, data: indexers } = useQuery<Indexer[], Error>(["filter", "indexer_list"], APIClient.indexers.getOptions,
{
refetchOnWindowFocus: false
} }
)
function General({ indexers }: GeneralProps) { let opts = indexers && indexers.length > 0 ? indexers.map(v => ({
let opts = indexers ? indexers.map(v => ({
label: v.name, label: v.name,
value: { value: {
id: v.id, id: v.id,
@ -327,7 +322,7 @@ function General({ indexers }: GeneralProps) {
<TextField name="name" label="Filter name" columns={6} placeholder="eg. Filter 1" /> <TextField name="name" label="Filter name" columns={6} placeholder="eg. Filter 1" />
<div className="col-span-6"> <div className="col-span-6">
<IndexerMultiSelect name="indexers" options={opts} label="Indexers" columns={6} /> {!isLoading && <IndexerMultiSelect name="indexers" options={opts} label="Indexers" columns={6} />}
</div> </div>
</div> </div>
</div> </div>