From 4625e1f95576d104196d8f00e4875fe8d5d02948 Mon Sep 17 00:00:00 2001 From: WithoutPants <53250216+WithoutPants@users.noreply.github.com> Date: Tue, 7 Sep 2021 11:54:22 +1000 Subject: [PATCH 01/86] Unify scrape refactor (#1630) * Unify scraped types * Make name fields optional * Unify single scrape queries * Change UI to use new interfaces * Add multi scrape interfaces * Use images instead of image --- gqlgen.yml | 16 -- graphql/documents/data/scrapers.graphql | 22 +- .../queries/scrapers/scrapers.graphql | 34 ++- graphql/schema/schema.graphql | 43 +++- graphql/schema/types/scraped-movie.graphql | 10 +- .../schema/types/scraped-performer.graphql | 10 +- graphql/schema/types/scraper.graphql | 135 ++++++---- pkg/api/resolver.go | 20 -- pkg/api/resolver_model_scraper.go | 23 -- pkg/api/resolver_query_scraper.go | 195 ++++++++++++++- pkg/manager/task_stash_box_tag.go | 18 +- pkg/models/model_scraped_item.go | 168 ------------- pkg/scraper/action.go | 6 +- pkg/scraper/config.go | 24 +- pkg/scraper/image.go | 2 + pkg/scraper/json.go | 32 +-- pkg/scraper/mapped.go | 20 +- pkg/scraper/matchers.go | 36 +-- pkg/scraper/scrapers.go | 110 ++++++--- pkg/scraper/script.go | 36 ++- pkg/scraper/stash.go | 232 ++++++++++-------- pkg/scraper/stashbox/stash_box.go | 161 ++++++++++-- pkg/scraper/xpath.go | 32 +-- pkg/scraper/xpath_test.go | 10 +- .../GalleryDetails/GalleryEditPanel.tsx | 11 +- .../GalleryDetails/GalleryScrapeDialog.tsx | 37 +-- .../Movies/MovieDetails/MovieEditPanel.tsx | 4 +- .../Movies/MovieDetails/MovieScrapeDialog.tsx | 9 +- .../PerformerDetails/PerformerEditPanel.tsx | 26 +- .../PerformerScrapeDialog.tsx | 20 +- .../PerformerDetails/PerformerScrapeModal.tsx | 2 +- .../PerformerStashBoxModal.tsx | 12 +- .../Scenes/SceneDetails/SceneEditPanel.tsx | 16 +- .../Scenes/SceneDetails/SceneScrapeDialog.tsx | 52 ++-- .../components/Tagger/StashSearchResult.tsx | 2 +- ui/v2.5/src/components/Tagger/TaggerList.tsx | 66 +---- ui/v2.5/src/components/Tagger/TaggerScene.tsx | 2 +- .../Tagger/performers/PerformerTagger.tsx | 6 +- ui/v2.5/src/components/Tagger/utils.ts | 6 +- ui/v2.5/src/core/StashService.ts | 150 ++++++----- 40 files changed, 1035 insertions(+), 781 deletions(-) delete mode 100644 pkg/api/resolver_model_scraper.go diff --git a/gqlgen.yml b/gqlgen.yml index eab0a4db9..8a21df01b 100644 --- a/gqlgen.yml +++ b/gqlgen.yml @@ -34,24 +34,8 @@ models: model: github.com/stashapp/stash/pkg/models.Movie Tag: model: github.com/stashapp/stash/pkg/models.Tag - ScrapedPerformer: - model: github.com/stashapp/stash/pkg/models.ScrapedPerformer - ScrapedScene: - model: github.com/stashapp/stash/pkg/models.ScrapedScene - ScrapedScenePerformer: - model: github.com/stashapp/stash/pkg/models.ScrapedScenePerformer - ScrapedSceneStudio: - model: github.com/stashapp/stash/pkg/models.ScrapedSceneStudio - ScrapedSceneMovie: - model: github.com/stashapp/stash/pkg/models.ScrapedSceneMovie - ScrapedSceneTag: - model: github.com/stashapp/stash/pkg/models.ScrapedSceneTag SceneFileType: model: github.com/stashapp/stash/pkg/models.SceneFileType - ScrapedMovie: - model: github.com/stashapp/stash/pkg/models.ScrapedMovie - ScrapedMovieStudio: - model: github.com/stashapp/stash/pkg/models.ScrapedMovieStudio SavedFilter: model: github.com/stashapp/stash/pkg/models.SavedFilter StashID: diff --git a/graphql/documents/data/scrapers.graphql b/graphql/documents/data/scrapers.graphql index b84fabfc9..7c4632b95 100644 --- a/graphql/documents/data/scrapers.graphql +++ b/graphql/documents/data/scrapers.graphql @@ -1,4 +1,5 @@ fragment ScrapedPerformerData on ScrapedPerformer { + stored_id name gender url @@ -18,7 +19,7 @@ fragment ScrapedPerformerData on ScrapedPerformer { tags { ...ScrapedSceneTagData } - image + images details death_date hair_color @@ -26,7 +27,7 @@ fragment ScrapedPerformerData on ScrapedPerformer { remote_site_id } -fragment ScrapedScenePerformerData on ScrapedScenePerformer { +fragment ScrapedScenePerformerData on ScrapedPerformer { stored_id name gender @@ -55,8 +56,8 @@ fragment ScrapedScenePerformerData on ScrapedScenePerformer { weight } -fragment ScrapedMovieStudioData on ScrapedMovieStudio { - id +fragment ScrapedMovieStudioData on ScrapedStudio { + stored_id name url } @@ -78,7 +79,7 @@ fragment ScrapedMovieData on ScrapedMovie { } } -fragment ScrapedSceneMovieData on ScrapedSceneMovie { +fragment ScrapedSceneMovieData on ScrapedMovie { stored_id name aliases @@ -90,14 +91,14 @@ fragment ScrapedSceneMovieData on ScrapedSceneMovie { synopsis } -fragment ScrapedSceneStudioData on ScrapedSceneStudio { +fragment ScrapedSceneStudioData on ScrapedStudio { stored_id name url remote_site_id } -fragment ScrapedSceneTagData on ScrapedSceneTag { +fragment ScrapedSceneTagData on ScrapedTag { stored_id name } @@ -108,6 +109,7 @@ fragment ScrapedSceneData on ScrapedScene { url date image + remote_site_id file { size @@ -135,6 +137,12 @@ fragment ScrapedSceneData on ScrapedScene { movies { ...ScrapedSceneMovieData } + + fingerprints { + hash + algorithm + duration + } } fragment ScrapedGalleryData on ScrapedGallery { diff --git a/graphql/documents/queries/scrapers/scrapers.graphql b/graphql/documents/queries/scrapers/scrapers.graphql index d5c54bac1..92c0bfd82 100644 --- a/graphql/documents/queries/scrapers/scrapers.graphql +++ b/graphql/documents/queries/scrapers/scrapers.graphql @@ -42,14 +42,14 @@ query ListMovieScrapers { } } -query ScrapePerformerList($scraper_id: ID!, $query: String!) { - scrapePerformerList(scraper_id: $scraper_id, query: $query) { +query ScrapeSinglePerformer($source: ScraperSourceInput!, $input: ScrapeSinglePerformerInput!) { + scrapeSinglePerformer(source: $source, input: $input) { ...ScrapedPerformerData } } -query ScrapePerformer($scraper_id: ID!, $scraped_performer: ScrapedPerformerInput!) { - scrapePerformer(scraper_id: $scraper_id, scraped_performer: $scraped_performer) { +query ScrapeMultiPerformers($source: ScraperSourceInput!, $input: ScrapeMultiPerformersInput!) { + scrapeMultiPerformers(source: $source, input: $input) { ...ScrapedPerformerData } } @@ -60,8 +60,14 @@ query ScrapePerformerURL($url: String!) { } } -query ScrapeScene($scraper_id: ID!, $scene: SceneUpdateInput!) { - scrapeScene(scraper_id: $scraper_id, scene: $scene) { +query ScrapeSingleScene($source: ScraperSourceInput!, $input: ScrapeSingleSceneInput!) { + scrapeSingleScene(source: $source, input: $input) { + ...ScrapedSceneData + } +} + +query ScrapeMultiScenes($source: ScraperSourceInput!, $input: ScrapeMultiScenesInput!) { + scrapeMultiScenes(source: $source, input: $input) { ...ScrapedSceneData } } @@ -72,8 +78,8 @@ query ScrapeSceneURL($url: String!) { } } -query ScrapeGallery($scraper_id: ID!, $gallery: GalleryUpdateInput!) { - scrapeGallery(scraper_id: $scraper_id, gallery: $gallery) { +query ScrapeSingleGallery($source: ScraperSourceInput!, $input: ScrapeSingleGalleryInput!) { + scrapeSingleGallery(source: $source, input: $input) { ...ScrapedGalleryData } } @@ -89,15 +95,3 @@ query ScrapeMovieURL($url: String!) { ...ScrapedMovieData } } - -query QueryStashBoxScene($input: StashBoxSceneQueryInput!) { - queryStashBoxScene(input: $input) { - ...ScrapedStashBoxSceneData - } -} - -query QueryStashBoxPerformer($input: StashBoxPerformerQueryInput!) { - queryStashBoxPerformer(input: $input) { - ...ScrapedStashBoxPerformerData - } -} diff --git a/graphql/schema/schema.graphql b/graphql/schema/schema.graphql index c0ba269ef..64a55c7eb 100644 --- a/graphql/schema/schema.graphql +++ b/graphql/schema/schema.graphql @@ -72,31 +72,50 @@ type Query { listGalleryScrapers: [Scraper!]! listMovieScrapers: [Scraper!]! - """Scrape a list of performers based on name""" - scrapePerformerList(scraper_id: ID!, query: String!): [ScrapedPerformer!]! - """Scrapes a complete performer record based on a scrapePerformerList result""" - scrapePerformer(scraper_id: ID!, scraped_performer: ScrapedPerformerInput!): ScrapedPerformer + """Scrape for a single scene""" + scrapeSingleScene(source: ScraperSourceInput!, input: ScrapeSingleSceneInput!): [ScrapedScene!]! + """Scrape for multiple scenes""" + scrapeMultiScenes(source: ScraperSourceInput!, input: ScrapeMultiScenesInput!): [[ScrapedScene!]!]! + + """Scrape for a single performer""" + scrapeSinglePerformer(source: ScraperSourceInput!, input: ScrapeSinglePerformerInput!): [ScrapedPerformer!]! + """Scrape for multiple performers""" + scrapeMultiPerformers(source: ScraperSourceInput!, input: ScrapeMultiPerformersInput!): [[ScrapedPerformer!]!]! + + """Scrape for a single gallery""" + scrapeSingleGallery(source: ScraperSourceInput!, input: ScrapeSingleGalleryInput!): [ScrapedGallery!]! + + """Scrape for a single movie""" + scrapeSingleMovie(source: ScraperSourceInput!, input: ScrapeSingleMovieInput!): [ScrapedMovie!]! + """Scrapes a complete performer record based on a URL""" scrapePerformerURL(url: String!): ScrapedPerformer - """Scrapes a complete scene record based on an existing scene""" - scrapeScene(scraper_id: ID!, scene: SceneUpdateInput!): ScrapedScene """Scrapes a complete performer record based on a URL""" scrapeSceneURL(url: String!): ScrapedScene - """Scrapes a complete gallery record based on an existing gallery""" - scrapeGallery(scraper_id: ID!, gallery: GalleryUpdateInput!): ScrapedGallery """Scrapes a complete gallery record based on a URL""" scrapeGalleryURL(url: String!): ScrapedGallery """Scrapes a complete movie record based on a URL""" scrapeMovieURL(url: String!): ScrapedMovie + """Scrape a list of performers based on name""" + scrapePerformerList(scraper_id: ID!, query: String!): [ScrapedPerformer!]! @deprecated(reason: "use scrapeSinglePerformer") + """Scrapes a complete performer record based on a scrapePerformerList result""" + scrapePerformer(scraper_id: ID!, scraped_performer: ScrapedPerformerInput!): ScrapedPerformer @deprecated(reason: "use scrapeSinglePerformer") + """Scrapes a complete scene record based on an existing scene""" + scrapeScene(scraper_id: ID!, scene: SceneUpdateInput!): ScrapedScene @deprecated(reason: "use scrapeSingleScene") + """Scrapes a complete gallery record based on an existing gallery""" + scrapeGallery(scraper_id: ID!, gallery: GalleryUpdateInput!): ScrapedGallery @deprecated(reason: "use scrapeSingleGallery") + """Scrape a performer using Freeones""" - scrapeFreeones(performer_name: String!): ScrapedPerformer + scrapeFreeones(performer_name: String!): ScrapedPerformer @deprecated(reason: "use scrapeSinglePerformer with scraper_id = builtin_freeones") """Scrape a list of performers from a query""" - scrapeFreeonesPerformerList(query: String!): [String!]! + scrapeFreeonesPerformerList(query: String!): [String!]! @deprecated(reason: "use scrapeSinglePerformer with scraper_id = builtin_freeones") """Query StashBox for scenes""" - queryStashBoxScene(input: StashBoxSceneQueryInput!): [ScrapedScene!]! - queryStashBoxPerformer(input: StashBoxPerformerQueryInput!): [StashBoxPerformerQueryResult!]! + queryStashBoxScene(input: StashBoxSceneQueryInput!): [ScrapedScene!]! @deprecated(reason: "use scrapeSingleScene or scrapeMultiScenes") + """Query StashBox for performers""" + queryStashBoxPerformer(input: StashBoxPerformerQueryInput!): [StashBoxPerformerQueryResult!]! @deprecated(reason: "use scrapeSinglePerformer or scrapeMultiPerformers") + # === end deprecated methods === # Plugins """List loaded plugins""" diff --git a/graphql/schema/types/scraped-movie.graphql b/graphql/schema/types/scraped-movie.graphql index d1546dfb9..55efb693d 100644 --- a/graphql/schema/types/scraped-movie.graphql +++ b/graphql/schema/types/scraped-movie.graphql @@ -1,12 +1,6 @@ -type ScrapedMovieStudio { - """Set if studio matched""" - id: ID - name: String! - url: String -} - """A movie from a scraping operation...""" type ScrapedMovie { + stored_id: ID name: String aliases: String duration: String @@ -15,7 +9,7 @@ type ScrapedMovie { director: String url: String synopsis: String - studio: ScrapedMovieStudio + studio: ScrapedStudio """This should be a base64 encoded data URL""" front_image: String diff --git a/graphql/schema/types/scraped-performer.graphql b/graphql/schema/types/scraped-performer.graphql index 2ae1b5a8a..b11b9b1b5 100644 --- a/graphql/schema/types/scraped-performer.graphql +++ b/graphql/schema/types/scraped-performer.graphql @@ -1,5 +1,7 @@ """A performer from a scraping operation...""" type ScrapedPerformer { + """Set if performer matched""" + stored_id: ID name: String gender: String url: String @@ -16,11 +18,11 @@ type ScrapedPerformer { tattoos: String piercings: String aliases: String - # Should be ScrapedPerformerTag - but would be identical types - tags: [ScrapedSceneTag!] + tags: [ScrapedTag!] """This should be a base64 encoded data URL""" - image: String + image: String @deprecated(reason: "use images instead") + images: [String!] details: String death_date: String hair_color: String @@ -29,6 +31,8 @@ type ScrapedPerformer { } input ScrapedPerformerInput { + """Set if performer matched""" + stored_id: ID name: String gender: String url: String diff --git a/graphql/schema/types/scraper.graphql b/graphql/schema/types/scraper.graphql index 860457bb0..9e35346f4 100644 --- a/graphql/schema/types/scraper.graphql +++ b/graphql/schema/types/scraper.graphql @@ -26,49 +26,7 @@ type Scraper { movie: ScraperSpec } -type ScrapedScenePerformer { - """Set if performer matched""" - stored_id: ID - name: String! - gender: String - url: String - twitter: String - instagram: String - birthdate: String - ethnicity: String - country: String - eye_color: String - height: String - measurements: String - fake_tits: String - career_length: String - tattoos: String - piercings: String - aliases: String - tags: [ScrapedSceneTag!] - - remote_site_id: String - images: [String!] - details: String - death_date: String - hair_color: String - weight: String -} - -type ScrapedSceneMovie { - """Set if movie matched""" - stored_id: ID - name: String! - aliases: String - duration: String - date: String - rating: String - director: String - synopsis: String - url: String -} - -type ScrapedSceneStudio { +type ScrapedStudio { """Set if studio matched""" stored_id: ID name: String! @@ -77,7 +35,7 @@ type ScrapedSceneStudio { remote_site_id: String } -type ScrapedSceneTag { +type ScrapedTag { """Set if tag matched""" stored_id: ID name: String! @@ -94,25 +52,98 @@ type ScrapedScene { file: SceneFileType # Resolver - studio: ScrapedSceneStudio - tags: [ScrapedSceneTag!] - performers: [ScrapedScenePerformer!] - movies: [ScrapedSceneMovie!] + studio: ScrapedStudio + tags: [ScrapedTag!] + performers: [ScrapedPerformer!] + movies: [ScrapedMovie!] remote_site_id: String duration: Int fingerprints: [StashBoxFingerprint!] } +input ScrapedSceneInput { + title: String + details: String + url: String + date: String + + # no image, file, duration or relationships + + remote_site_id: String +} + type ScrapedGallery { title: String details: String url: String date: String - studio: ScrapedSceneStudio - tags: [ScrapedSceneTag!] - performers: [ScrapedScenePerformer!] + studio: ScrapedStudio + tags: [ScrapedTag!] + performers: [ScrapedPerformer!] +} + +input ScrapedGalleryInput { + title: String + details: String + url: String + date: String + + # no studio, tags or performers +} + +input ScraperSourceInput { + """Index of the configured stash-box instance to use. Should be unset if scraper_id is set""" + stash_box_index: Int + """Scraper ID to scrape with. Should be unset if stash_box_index is set""" + scraper_id: ID +} + +input ScrapeSingleSceneInput { + """Instructs to query by string""" + query: String + """Instructs to query by scene fingerprints""" + scene_id: ID + """Instructs to query by scene fragment""" + scene_input: ScrapedSceneInput +} + +input ScrapeMultiScenesInput { + """Instructs to query by scene fingerprints""" + scene_ids: [ID!] +} + +input ScrapeSinglePerformerInput { + """Instructs to query by string""" + query: String + """Instructs to query by performer id""" + performer_id: ID + """Instructs to query by performer fragment""" + performer_input: ScrapedPerformerInput +} + +input ScrapeMultiPerformersInput { + """Instructs to query by scene fingerprints""" + performer_ids: [ID!] +} + +input ScrapeSingleGalleryInput { + """Instructs to query by string""" + query: String + """Instructs to query by gallery id""" + gallery_id: ID + """Instructs to query by gallery fragment""" + gallery_input: ScrapedGalleryInput +} + +input ScrapeSingleMovieInput { + """Instructs to query by string""" + query: String + """Instructs to query by movie id""" + movie_id: ID + """Instructs to query by gallery fragment""" + movie_input: ScrapedMovieInput } input StashBoxSceneQueryInput { @@ -135,7 +166,7 @@ input StashBoxPerformerQueryInput { type StashBoxPerformerQueryResult { query: String! - results: [ScrapedScenePerformer!]! + results: [ScrapedPerformer!]! } type StashBoxFingerprint { diff --git a/pkg/api/resolver.go b/pkg/api/resolver.go index ed15a1d21..7d3f6aa3f 100644 --- a/pkg/api/resolver.go +++ b/pkg/api/resolver.go @@ -53,22 +53,6 @@ func (r *Resolver) Tag() models.TagResolver { return &tagResolver{r} } -func (r *Resolver) ScrapedSceneTag() models.ScrapedSceneTagResolver { - return &scrapedSceneTagResolver{r} -} - -func (r *Resolver) ScrapedSceneMovie() models.ScrapedSceneMovieResolver { - return &scrapedSceneMovieResolver{r} -} - -func (r *Resolver) ScrapedScenePerformer() models.ScrapedScenePerformerResolver { - return &scrapedScenePerformerResolver{r} -} - -func (r *Resolver) ScrapedSceneStudio() models.ScrapedSceneStudioResolver { - return &scrapedSceneStudioResolver{r} -} - type mutationResolver struct{ *Resolver } type queryResolver struct{ *Resolver } type subscriptionResolver struct{ *Resolver } @@ -81,10 +65,6 @@ type imageResolver struct{ *Resolver } type studioResolver struct{ *Resolver } type movieResolver struct{ *Resolver } type tagResolver struct{ *Resolver } -type scrapedSceneTagResolver struct{ *Resolver } -type scrapedSceneMovieResolver struct{ *Resolver } -type scrapedScenePerformerResolver struct{ *Resolver } -type scrapedSceneStudioResolver struct{ *Resolver } func (r *Resolver) withTxn(ctx context.Context, fn func(r models.Repository) error) error { return r.txnManager.WithTxn(ctx, fn) diff --git a/pkg/api/resolver_model_scraper.go b/pkg/api/resolver_model_scraper.go deleted file mode 100644 index 583194496..000000000 --- a/pkg/api/resolver_model_scraper.go +++ /dev/null @@ -1,23 +0,0 @@ -package api - -import ( - "context" - - "github.com/stashapp/stash/pkg/models" -) - -func (r *scrapedSceneTagResolver) StoredID(ctx context.Context, obj *models.ScrapedSceneTag) (*string, error) { - return obj.ID, nil -} - -func (r *scrapedSceneMovieResolver) StoredID(ctx context.Context, obj *models.ScrapedSceneMovie) (*string, error) { - return obj.ID, nil -} - -func (r *scrapedScenePerformerResolver) StoredID(ctx context.Context, obj *models.ScrapedScenePerformer) (*string, error) { - return obj.ID, nil -} - -func (r *scrapedSceneStudioResolver) StoredID(ctx context.Context, obj *models.ScrapedSceneStudio) (*string, error) { - return obj.ID, nil -} diff --git a/pkg/api/resolver_query_scraper.go b/pkg/api/resolver_query_scraper.go index 301870351..55b17c09f 100644 --- a/pkg/api/resolver_query_scraper.go +++ b/pkg/api/resolver_query_scraper.go @@ -2,7 +2,9 @@ package api import ( "context" + "errors" "fmt" + "strconv" "github.com/stashapp/stash/pkg/manager" "github.com/stashapp/stash/pkg/manager/config" @@ -29,8 +31,9 @@ func (r *queryResolver) ScrapeFreeonesPerformerList(ctx context.Context, query s var ret []string for _, v := range scrapedPerformers { - name := v.Name - ret = append(ret, *name) + if v.Name != nil { + ret = append(ret, *v.Name) + } } return ret, nil @@ -69,7 +72,12 @@ func (r *queryResolver) ScrapePerformerURL(ctx context.Context, url string) (*mo } func (r *queryResolver) ScrapeScene(ctx context.Context, scraperID string, scene models.SceneUpdateInput) (*models.ScrapedScene, error) { - return manager.GetInstance().ScraperCache.ScrapeScene(scraperID, scene) + id, err := strconv.Atoi(scene.ID) + if err != nil { + return nil, err + } + + return manager.GetInstance().ScraperCache.ScrapeScene(scraperID, id) } func (r *queryResolver) ScrapeSceneURL(ctx context.Context, url string) (*models.ScrapedScene, error) { @@ -77,7 +85,12 @@ func (r *queryResolver) ScrapeSceneURL(ctx context.Context, url string) (*models } func (r *queryResolver) ScrapeGallery(ctx context.Context, scraperID string, gallery models.GalleryUpdateInput) (*models.ScrapedGallery, error) { - return manager.GetInstance().ScraperCache.ScrapeGallery(scraperID, gallery) + id, err := strconv.Atoi(gallery.ID) + if err != nil { + return nil, err + } + + return manager.GetInstance().ScraperCache.ScrapeGallery(scraperID, id) } func (r *queryResolver) ScrapeGalleryURL(ctx context.Context, url string) (*models.ScrapedGallery, error) { @@ -98,7 +111,7 @@ func (r *queryResolver) QueryStashBoxScene(ctx context.Context, input models.Sta client := stashbox.NewClient(*boxes[input.StashBoxIndex], r.txnManager) if len(input.SceneIds) > 0 { - return client.FindStashBoxScenesByFingerprints(input.SceneIds) + return client.FindStashBoxScenesByFingerprintsFlat(input.SceneIds) } if input.Q != nil { @@ -127,3 +140,175 @@ func (r *queryResolver) QueryStashBoxPerformer(ctx context.Context, input models return nil, nil } + +func (r *queryResolver) getStashBoxClient(index int) (*stashbox.Client, error) { + boxes := config.GetInstance().GetStashBoxes() + + if index < 0 || index >= len(boxes) { + return nil, fmt.Errorf("invalid stash_box_index %d", index) + } + + return stashbox.NewClient(*boxes[index], r.txnManager), nil +} + +func (r *queryResolver) ScrapeSingleScene(ctx context.Context, source models.ScraperSourceInput, input models.ScrapeSingleSceneInput) ([]*models.ScrapedScene, error) { + if source.ScraperID != nil { + var singleScene *models.ScrapedScene + var err error + + if input.SceneID != nil { + var sceneID int + sceneID, err = strconv.Atoi(*input.SceneID) + if err != nil { + return nil, err + } + singleScene, err = manager.GetInstance().ScraperCache.ScrapeScene(*source.ScraperID, sceneID) + } else if input.SceneInput != nil { + singleScene, err = manager.GetInstance().ScraperCache.ScrapeSceneFragment(*source.ScraperID, *input.SceneInput) + } else { + return nil, errors.New("not implemented") + } + + if err != nil { + return nil, err + } + + if singleScene != nil { + return []*models.ScrapedScene{singleScene}, nil + } + + return nil, nil + } else if source.StashBoxIndex != nil { + client, err := r.getStashBoxClient(*source.StashBoxIndex) + if err != nil { + return nil, err + } + + if input.SceneID != nil { + return client.FindStashBoxScenesByFingerprintsFlat([]string{*input.SceneID}) + } else if input.Query != nil { + return client.QueryStashBoxScene(*input.Query) + } + + return nil, errors.New("scene_id or query must be set") + } + + return nil, errors.New("scraper_id or stash_box_index must be set") +} + +func (r *queryResolver) ScrapeMultiScenes(ctx context.Context, source models.ScraperSourceInput, input models.ScrapeMultiScenesInput) ([][]*models.ScrapedScene, error) { + if source.ScraperID != nil { + return nil, errors.New("not implemented") + } else if source.StashBoxIndex != nil { + client, err := r.getStashBoxClient(*source.StashBoxIndex) + if err != nil { + return nil, err + } + + return client.FindStashBoxScenesByFingerprints(input.SceneIds) + } + + return nil, errors.New("scraper_id or stash_box_index must be set") +} + +func (r *queryResolver) ScrapeSinglePerformer(ctx context.Context, source models.ScraperSourceInput, input models.ScrapeSinglePerformerInput) ([]*models.ScrapedPerformer, error) { + if source.ScraperID != nil { + if input.PerformerInput != nil { + singlePerformer, err := manager.GetInstance().ScraperCache.ScrapePerformer(*source.ScraperID, *input.PerformerInput) + if err != nil { + return nil, err + } + + if singlePerformer != nil { + return []*models.ScrapedPerformer{singlePerformer}, nil + } + + return nil, nil + } + + if input.Query != nil { + return manager.GetInstance().ScraperCache.ScrapePerformerList(*source.ScraperID, *input.Query) + } + + return nil, errors.New("not implemented") + } else if source.StashBoxIndex != nil { + client, err := r.getStashBoxClient(*source.StashBoxIndex) + if err != nil { + return nil, err + } + + var ret []*models.StashBoxPerformerQueryResult + if input.PerformerID != nil { + ret, err = client.FindStashBoxPerformersByNames([]string{*input.PerformerID}) + } else if input.Query != nil { + ret, err = client.QueryStashBoxPerformer(*input.Query) + } else { + return nil, errors.New("not implemented") + } + + if err != nil { + return nil, err + } + + if len(ret) > 0 { + return ret[0].Results, nil + } + + return nil, nil + } + + return nil, errors.New("scraper_id or stash_box_index must be set") +} + +func (r *queryResolver) ScrapeMultiPerformers(ctx context.Context, source models.ScraperSourceInput, input models.ScrapeMultiPerformersInput) ([][]*models.ScrapedPerformer, error) { + if source.ScraperID != nil { + return nil, errors.New("not implemented") + } else if source.StashBoxIndex != nil { + client, err := r.getStashBoxClient(*source.StashBoxIndex) + if err != nil { + return nil, err + } + + return client.FindStashBoxPerformersByPerformerNames(input.PerformerIds) + } + + return nil, errors.New("scraper_id or stash_box_index must be set") +} + +func (r *queryResolver) ScrapeSingleGallery(ctx context.Context, source models.ScraperSourceInput, input models.ScrapeSingleGalleryInput) ([]*models.ScrapedGallery, error) { + if source.ScraperID != nil { + var singleGallery *models.ScrapedGallery + var err error + + if input.GalleryID != nil { + var galleryID int + galleryID, err = strconv.Atoi(*input.GalleryID) + if err != nil { + return nil, err + } + singleGallery, err = manager.GetInstance().ScraperCache.ScrapeGallery(*source.ScraperID, galleryID) + } else if input.GalleryInput != nil { + singleGallery, err = manager.GetInstance().ScraperCache.ScrapeGalleryFragment(*source.ScraperID, *input.GalleryInput) + } else { + return nil, errors.New("not implemented") + } + + if err != nil { + return nil, err + } + + if singleGallery != nil { + return []*models.ScrapedGallery{singleGallery}, nil + } + + return nil, nil + } else if source.StashBoxIndex != nil { + return nil, errors.New("not supported") + } + + return nil, errors.New("scraper_id must be set") +} + +func (r *queryResolver) ScrapeSingleMovie(ctx context.Context, source models.ScraperSourceInput, input models.ScrapeSingleMovieInput) ([]*models.ScrapedMovie, error) { + return nil, errors.New("not supported") +} diff --git a/pkg/manager/task_stash_box_tag.go b/pkg/manager/task_stash_box_tag.go index 597655e9f..5bd5c2252 100644 --- a/pkg/manager/task_stash_box_tag.go +++ b/pkg/manager/task_stash_box_tag.go @@ -40,7 +40,7 @@ func (t *StashBoxPerformerTagTask) Description() string { } func (t *StashBoxPerformerTagTask) stashBoxPerformerTag() { - var performer *models.ScrapedScenePerformer + var performer *models.ScrapedPerformer var err error client := stashbox.NewClient(*t.box, t.txnManager) @@ -132,8 +132,8 @@ func (t *StashBoxPerformerTagTask) stashBoxPerformerTag() { value := getNullString(performer.Measurements) partial.Measurements = &value } - if excluded["name"] { - value := sql.NullString{String: performer.Name, Valid: true} + if excluded["name"] && performer.Name != nil { + value := sql.NullString{String: *performer.Name, Valid: true} partial.Name = &value } if performer.Piercings != nil && !excluded["piercings"] { @@ -180,17 +180,21 @@ func (t *StashBoxPerformerTagTask) stashBoxPerformerTag() { } if err == nil { - logger.Infof("Updated performer %s", performer.Name) + var name string + if performer.Name != nil { + name = *performer.Name + } + logger.Infof("Updated performer %s", name) } return err }) - } else if t.name != nil { + } else if t.name != nil && performer.Name != nil { currentTime := time.Now() newPerformer := models.Performer{ Aliases: getNullString(performer.Aliases), Birthdate: getDate(performer.Birthdate), CareerLength: getNullString(performer.CareerLength), - Checksum: utils.MD5FromString(performer.Name), + Checksum: utils.MD5FromString(*performer.Name), Country: getNullString(performer.Country), CreatedAt: models.SQLiteTimestamp{Timestamp: currentTime}, Ethnicity: getNullString(performer.Ethnicity), @@ -201,7 +205,7 @@ func (t *StashBoxPerformerTagTask) stashBoxPerformerTag() { Height: getNullString(performer.Height), Instagram: getNullString(performer.Instagram), Measurements: getNullString(performer.Measurements), - Name: sql.NullString{String: performer.Name, Valid: true}, + Name: sql.NullString{String: *performer.Name, Valid: true}, Piercings: getNullString(performer.Piercings), Tattoos: getNullString(performer.Tattoos), Twitter: getNullString(performer.Twitter), diff --git a/pkg/models/model_scraped_item.go b/pkg/models/model_scraped_item.go index 230fd0ba0..4035163b7 100644 --- a/pkg/models/model_scraped_item.go +++ b/pkg/models/model_scraped_item.go @@ -23,174 +23,6 @@ type ScrapedItem struct { UpdatedAt SQLiteTimestamp `db:"updated_at" json:"updated_at"` } -type ScrapedPerformer struct { - Name *string `graphql:"name" json:"name"` - Gender *string `graphql:"gender" json:"gender"` - URL *string `graphql:"url" json:"url"` - Twitter *string `graphql:"twitter" json:"twitter"` - Instagram *string `graphql:"instagram" json:"instagram"` - Birthdate *string `graphql:"birthdate" json:"birthdate"` - Ethnicity *string `graphql:"ethnicity" json:"ethnicity"` - Country *string `graphql:"country" json:"country"` - EyeColor *string `graphql:"eye_color" json:"eye_color"` - Height *string `graphql:"height" json:"height"` - Measurements *string `graphql:"measurements" json:"measurements"` - FakeTits *string `graphql:"fake_tits" json:"fake_tits"` - CareerLength *string `graphql:"career_length" json:"career_length"` - Tattoos *string `graphql:"tattoos" json:"tattoos"` - Piercings *string `graphql:"piercings" json:"piercings"` - Aliases *string `graphql:"aliases" json:"aliases"` - Tags []*ScrapedSceneTag `graphql:"tags" json:"tags"` - Image *string `graphql:"image" json:"image"` - Details *string `graphql:"details" json:"details"` - DeathDate *string `graphql:"death_date" json:"death_date"` - HairColor *string `graphql:"hair_color" json:"hair_color"` - Weight *string `graphql:"weight" json:"weight"` - RemoteSiteID *string `graphql:"remote_site_id" json:"remote_site_id"` -} - -// this type has no Image field -type ScrapedPerformerStash struct { - Name *string `graphql:"name" json:"name"` - Gender *string `graphql:"gender" json:"gender"` - URL *string `graphql:"url" json:"url"` - Twitter *string `graphql:"twitter" json:"twitter"` - Instagram *string `graphql:"instagram" json:"instagram"` - Birthdate *string `graphql:"birthdate" json:"birthdate"` - Ethnicity *string `graphql:"ethnicity" json:"ethnicity"` - Country *string `graphql:"country" json:"country"` - EyeColor *string `graphql:"eye_color" json:"eye_color"` - Height *string `graphql:"height" json:"height"` - Measurements *string `graphql:"measurements" json:"measurements"` - FakeTits *string `graphql:"fake_tits" json:"fake_tits"` - CareerLength *string `graphql:"career_length" json:"career_length"` - Tattoos *string `graphql:"tattoos" json:"tattoos"` - Piercings *string `graphql:"piercings" json:"piercings"` - Aliases *string `graphql:"aliases" json:"aliases"` - Tags []*ScrapedSceneTag `graphql:"tags" json:"tags"` - Details *string `graphql:"details" json:"details"` - DeathDate *string `graphql:"death_date" json:"death_date"` - HairColor *string `graphql:"hair_color" json:"hair_color"` - Weight *string `graphql:"weight" json:"weight"` -} - -type ScrapedScene struct { - Title *string `graphql:"title" json:"title"` - Details *string `graphql:"details" json:"details"` - URL *string `graphql:"url" json:"url"` - Date *string `graphql:"date" json:"date"` - Image *string `graphql:"image" json:"image"` - RemoteSiteID *string `graphql:"remote_site_id" json:"remote_site_id"` - Duration *int `graphql:"duration" json:"duration"` - File *SceneFileType `graphql:"file" json:"file"` - Fingerprints []*StashBoxFingerprint `graphql:"fingerprints" json:"fingerprints"` - Studio *ScrapedSceneStudio `graphql:"studio" json:"studio"` - Movies []*ScrapedSceneMovie `graphql:"movies" json:"movies"` - Tags []*ScrapedSceneTag `graphql:"tags" json:"tags"` - Performers []*ScrapedScenePerformer `graphql:"performers" json:"performers"` -} - -// stash doesn't return image, and we need id -type ScrapedSceneStash struct { - ID string `graphql:"id" json:"id"` - Title *string `graphql:"title" json:"title"` - Details *string `graphql:"details" json:"details"` - URL *string `graphql:"url" json:"url"` - Date *string `graphql:"date" json:"date"` - File *SceneFileType `graphql:"file" json:"file"` - Studio *ScrapedSceneStudio `graphql:"studio" json:"studio"` - Tags []*ScrapedSceneTag `graphql:"tags" json:"tags"` - Performers []*ScrapedScenePerformer `graphql:"performers" json:"performers"` -} - -type ScrapedGalleryStash struct { - ID string `graphql:"id" json:"id"` - Title *string `graphql:"title" json:"title"` - Details *string `graphql:"details" json:"details"` - URL *string `graphql:"url" json:"url"` - Date *string `graphql:"date" json:"date"` - File *SceneFileType `graphql:"file" json:"file"` - Studio *ScrapedSceneStudio `graphql:"studio" json:"studio"` - Tags []*ScrapedSceneTag `graphql:"tags" json:"tags"` - Performers []*ScrapedScenePerformer `graphql:"performers" json:"performers"` -} - -type ScrapedScenePerformer struct { - // Set if performer matched - ID *string `graphql:"id" json:"id"` - Name string `graphql:"name" json:"name"` - Gender *string `graphql:"gender" json:"gender"` - URL *string `graphql:"url" json:"url"` - Twitter *string `graphql:"twitter" json:"twitter"` - Instagram *string `graphql:"instagram" json:"instagram"` - Birthdate *string `graphql:"birthdate" json:"birthdate"` - Ethnicity *string `graphql:"ethnicity" json:"ethnicity"` - Country *string `graphql:"country" json:"country"` - EyeColor *string `graphql:"eye_color" json:"eye_color"` - Height *string `graphql:"height" json:"height"` - Measurements *string `graphql:"measurements" json:"measurements"` - FakeTits *string `graphql:"fake_tits" json:"fake_tits"` - CareerLength *string `graphql:"career_length" json:"career_length"` - Tattoos *string `graphql:"tattoos" json:"tattoos"` - Piercings *string `graphql:"piercings" json:"piercings"` - Aliases *string `graphql:"aliases" json:"aliases"` - Tags []*ScrapedSceneTag `graphql:"tags" json:"tags"` - RemoteSiteID *string `graphql:"remote_site_id" json:"remote_site_id"` - Images []string `graphql:"images" json:"images"` - Details *string `graphql:"details" json:"details"` - DeathDate *string `graphql:"death_date" json:"death_date"` - HairColor *string `graphql:"hair_color" json:"hair_color"` - Weight *string `graphql:"weight" json:"weight"` -} - -type ScrapedSceneStudio struct { - // Set if studio matched - ID *string `graphql:"id" json:"id"` - Name string `graphql:"name" json:"name"` - URL *string `graphql:"url" json:"url"` - RemoteSiteID *string `graphql:"remote_site_id" json:"remote_site_id"` -} - -type ScrapedSceneMovie struct { - // Set if movie matched - ID *string `graphql:"id" json:"id"` - Name string `graphql:"name" json:"name"` - Aliases string `graphql:"aliases" json:"aliases"` - Duration string `graphql:"duration" json:"duration"` - Date string `graphql:"date" json:"date"` - Rating string `graphql:"rating" json:"rating"` - Director string `graphql:"director" json:"director"` - Synopsis string `graphql:"synopsis" json:"synopsis"` - URL *string `graphql:"url" json:"url"` -} - -type ScrapedSceneTag struct { - // Set if tag matched - ID *string `graphql:"stored_id" json:"stored_id"` - Name string `graphql:"name" json:"name"` -} - -type ScrapedMovie struct { - Name *string `graphql:"name" json:"name"` - Aliases *string `graphql:"aliases" json:"aliases"` - Duration *string `graphql:"duration" json:"duration"` - Date *string `graphql:"date" json:"date"` - Rating *string `graphql:"rating" json:"rating"` - Director *string `graphql:"director" json:"director"` - Studio *ScrapedMovieStudio `graphql:"studio" json:"studio"` - Synopsis *string `graphql:"synopsis" json:"synopsis"` - URL *string `graphql:"url" json:"url"` - FrontImage *string `graphql:"front_image" json:"front_image"` - BackImage *string `graphql:"back_image" json:"back_image"` -} - -type ScrapedMovieStudio struct { - // Set if studio matched - ID *string `graphql:"id" json:"id"` - Name string `graphql:"name" json:"name"` - URL *string `graphql:"url" json:"url"` -} - type ScrapedItems []*ScrapedItem func (s *ScrapedItems) Append(o interface{}) { diff --git a/pkg/scraper/action.go b/pkg/scraper/action.go index ca7e82b2c..fdfa15afa 100644 --- a/pkg/scraper/action.go +++ b/pkg/scraper/action.go @@ -37,10 +37,12 @@ type scraper interface { scrapePerformerByFragment(scrapedPerformer models.ScrapedPerformerInput) (*models.ScrapedPerformer, error) scrapePerformerByURL(url string) (*models.ScrapedPerformer, error) - scrapeSceneByFragment(scene models.SceneUpdateInput) (*models.ScrapedScene, error) + scrapeSceneByScene(scene *models.Scene) (*models.ScrapedScene, error) + scrapeSceneByFragment(scene models.ScrapedSceneInput) (*models.ScrapedScene, error) scrapeSceneByURL(url string) (*models.ScrapedScene, error) - scrapeGalleryByFragment(scene models.GalleryUpdateInput) (*models.ScrapedGallery, error) + scrapeGalleryByGallery(gallery *models.Gallery) (*models.ScrapedGallery, error) + scrapeGalleryByFragment(gallery models.ScrapedGalleryInput) (*models.ScrapedGallery, error) scrapeGalleryByURL(url string) (*models.ScrapedGallery, error) scrapeMovieByURL(url string) (*models.ScrapedMovie, error) diff --git a/pkg/scraper/config.go b/pkg/scraper/config.go index c16d55a7f..d71d2f954 100644 --- a/pkg/scraper/config.go +++ b/pkg/scraper/config.go @@ -393,8 +393,18 @@ func (c config) matchesMovieURL(url string) bool { return false } -func (c config) ScrapeScene(scene models.SceneUpdateInput, txnManager models.TransactionManager, globalConfig GlobalConfig) (*models.ScrapedScene, error) { +func (c config) ScrapeSceneByScene(scene *models.Scene, txnManager models.TransactionManager, globalConfig GlobalConfig) (*models.ScrapedScene, error) { if c.SceneByFragment != nil { + s := getScraper(*c.SceneByFragment, txnManager, c, globalConfig) + return s.scrapeSceneByScene(scene) + } + + return nil, nil +} + +func (c config) ScrapeSceneByFragment(scene models.ScrapedSceneInput, txnManager models.TransactionManager, globalConfig GlobalConfig) (*models.ScrapedScene, error) { + if c.SceneByFragment != nil { + // TODO - this should be sceneByQueryFragment s := getScraper(*c.SceneByFragment, txnManager, c, globalConfig) return s.scrapeSceneByFragment(scene) } @@ -420,8 +430,18 @@ func (c config) ScrapeSceneURL(url string, txnManager models.TransactionManager, return nil, nil } -func (c config) ScrapeGallery(gallery models.GalleryUpdateInput, txnManager models.TransactionManager, globalConfig GlobalConfig) (*models.ScrapedGallery, error) { +func (c config) ScrapeGalleryByGallery(gallery *models.Gallery, txnManager models.TransactionManager, globalConfig GlobalConfig) (*models.ScrapedGallery, error) { + if c.SceneByFragment != nil { + s := getScraper(*c.GalleryByFragment, txnManager, c, globalConfig) + return s.scrapeGalleryByGallery(gallery) + } + + return nil, nil +} + +func (c config) ScrapeGalleryByFragment(gallery models.ScrapedGalleryInput, txnManager models.TransactionManager, globalConfig GlobalConfig) (*models.ScrapedGallery, error) { if c.GalleryByFragment != nil { + // TODO - this should be galleryByQueryFragment s := getScraper(*c.GalleryByFragment, txnManager, c, globalConfig) return s.scrapeGalleryByFragment(gallery) } diff --git a/pkg/scraper/image.go b/pkg/scraper/image.go index ab09f28da..66261537f 100644 --- a/pkg/scraper/image.go +++ b/pkg/scraper/image.go @@ -28,6 +28,8 @@ func setPerformerImage(p *models.ScrapedPerformer, globalConfig GlobalConfig) er } p.Image = img + // Image is deprecated. Use images instead + p.Images = []string{*img} return nil } diff --git a/pkg/scraper/json.go b/pkg/scraper/json.go index b7c68e86e..e5786761a 100644 --- a/pkg/scraper/json.go +++ b/pkg/scraper/json.go @@ -143,18 +143,9 @@ func (s *jsonScraper) scrapePerformerByFragment(scrapedPerformer models.ScrapedP return nil, errors.New("scrapePerformerByFragment not supported for json scraper") } -func (s *jsonScraper) scrapeSceneByFragment(scene models.SceneUpdateInput) (*models.ScrapedScene, error) { - storedScene, err := sceneFromUpdateFragment(scene, s.txnManager) - if err != nil { - return nil, err - } - - if storedScene == nil { - return nil, errors.New("no scene found") - } - +func (s *jsonScraper) scrapeSceneByScene(scene *models.Scene) (*models.ScrapedScene, error) { // construct the URL - queryURL := queryURLParametersFromScene(storedScene) + queryURL := queryURLParametersFromScene(scene) if s.scraper.QueryURLReplacements != nil { queryURL.applyReplacements(s.scraper.QueryURLReplacements) } @@ -176,18 +167,13 @@ func (s *jsonScraper) scrapeSceneByFragment(scene models.SceneUpdateInput) (*mod return scraper.scrapeScene(q) } -func (s *jsonScraper) scrapeGalleryByFragment(gallery models.GalleryUpdateInput) (*models.ScrapedGallery, error) { - storedGallery, err := galleryFromUpdateFragment(gallery, s.txnManager) - if err != nil { - return nil, err - } - - if storedGallery == nil { - return nil, errors.New("no scene found") - } +func (s *jsonScraper) scrapeSceneByFragment(scene models.ScrapedSceneInput) (*models.ScrapedScene, error) { + return nil, errors.New("scrapeSceneByFragment not supported for json scraper") +} +func (s *jsonScraper) scrapeGalleryByGallery(gallery *models.Gallery) (*models.ScrapedGallery, error) { // construct the URL - queryURL := queryURLParametersFromGallery(storedGallery) + queryURL := queryURLParametersFromGallery(gallery) if s.scraper.QueryURLReplacements != nil { queryURL.applyReplacements(s.scraper.QueryURLReplacements) } @@ -209,6 +195,10 @@ func (s *jsonScraper) scrapeGalleryByFragment(gallery models.GalleryUpdateInput) return scraper.scrapeGallery(q) } +func (s *jsonScraper) scrapeGalleryByFragment(gallery models.ScrapedGalleryInput) (*models.ScrapedGallery, error) { + return nil, errors.New("scrapeGalleryByFragment not supported for json scraper") +} + func (s *jsonScraper) getJsonQuery(doc string) *jsonQuery { return &jsonQuery{ doc: doc, diff --git a/pkg/scraper/mapped.go b/pkg/scraper/mapped.go index 5cbdead74..52af8556c 100644 --- a/pkg/scraper/mapped.go +++ b/pkg/scraper/mapped.go @@ -763,7 +763,7 @@ func (s mappedScraper) scrapePerformer(q mappedQuery) (*models.ScrapedPerformer, tagResults := performerTagsMap.process(q, s.Common) for _, p := range tagResults { - tag := &models.ScrapedSceneTag{} + tag := &models.ScrapedTag{} p.apply(tag) ret.Tags = append(ret.Tags, tag) } @@ -824,11 +824,11 @@ func (s mappedScraper) scrapeScene(q mappedQuery) (*models.ScrapedScene, error) performerResults := scenePerformersMap.process(q, s.Common) for _, p := range performerResults { - performer := &models.ScrapedScenePerformer{} + performer := &models.ScrapedPerformer{} p.apply(performer) for _, p := range performerTagResults { - tag := &models.ScrapedSceneTag{} + tag := &models.ScrapedTag{} p.apply(tag) ret.Tags = append(ret.Tags, tag) } @@ -842,7 +842,7 @@ func (s mappedScraper) scrapeScene(q mappedQuery) (*models.ScrapedScene, error) tagResults := sceneTagsMap.process(q, s.Common) for _, p := range tagResults { - tag := &models.ScrapedSceneTag{} + tag := &models.ScrapedTag{} p.apply(tag) ret.Tags = append(ret.Tags, tag) } @@ -853,7 +853,7 @@ func (s mappedScraper) scrapeScene(q mappedQuery) (*models.ScrapedScene, error) studioResults := sceneStudioMap.process(q, s.Common) if len(studioResults) > 0 { - studio := &models.ScrapedSceneStudio{} + studio := &models.ScrapedStudio{} studioResults[0].apply(studio) ret.Studio = studio } @@ -864,7 +864,7 @@ func (s mappedScraper) scrapeScene(q mappedQuery) (*models.ScrapedScene, error) movieResults := sceneMoviesMap.process(q, s.Common) for _, p := range movieResults { - movie := &models.ScrapedSceneMovie{} + movie := &models.ScrapedMovie{} p.apply(movie) ret.Movies = append(ret.Movies, movie) } @@ -899,7 +899,7 @@ func (s mappedScraper) scrapeGallery(q mappedQuery) (*models.ScrapedGallery, err performerResults := galleryPerformersMap.process(q, s.Common) for _, p := range performerResults { - performer := &models.ScrapedScenePerformer{} + performer := &models.ScrapedPerformer{} p.apply(performer) ret.Performers = append(ret.Performers, performer) } @@ -910,7 +910,7 @@ func (s mappedScraper) scrapeGallery(q mappedQuery) (*models.ScrapedGallery, err tagResults := galleryTagsMap.process(q, s.Common) for _, p := range tagResults { - tag := &models.ScrapedSceneTag{} + tag := &models.ScrapedTag{} p.apply(tag) ret.Tags = append(ret.Tags, tag) } @@ -921,7 +921,7 @@ func (s mappedScraper) scrapeGallery(q mappedQuery) (*models.ScrapedGallery, err studioResults := galleryStudioMap.process(q, s.Common) if len(studioResults) > 0 { - studio := &models.ScrapedSceneStudio{} + studio := &models.ScrapedStudio{} studioResults[0].apply(studio) ret.Studio = studio } @@ -951,7 +951,7 @@ func (s mappedScraper) scrapeMovie(q mappedQuery) (*models.ScrapedMovie, error) studioResults := movieStudioMap.process(q, s.Common) if len(studioResults) > 0 { - studio := &models.ScrapedMovieStudio{} + studio := &models.ScrapedStudio{} studioResults[0].apply(studio) ret.Studio = studio } diff --git a/pkg/scraper/matchers.go b/pkg/scraper/matchers.go index cc1d6f99c..fc9bf29e2 100644 --- a/pkg/scraper/matchers.go +++ b/pkg/scraper/matchers.go @@ -7,10 +7,14 @@ import ( "github.com/stashapp/stash/pkg/tag" ) -// MatchScrapedScenePerformer matches the provided performer with the +// MatchScrapedPerformer matches the provided performer with the // performers in the database and sets the ID field if one is found. -func MatchScrapedScenePerformer(qb models.PerformerReader, p *models.ScrapedScenePerformer) error { - performers, err := qb.FindByNames([]string{p.Name}, true) +func MatchScrapedPerformer(qb models.PerformerReader, p *models.ScrapedPerformer) error { + if p.Name == nil { + return nil + } + + performers, err := qb.FindByNames([]string{*p.Name}, true) if err != nil { return err @@ -22,13 +26,13 @@ func MatchScrapedScenePerformer(qb models.PerformerReader, p *models.ScrapedScen } id := strconv.Itoa(performers[0].ID) - p.ID = &id + p.StoredID = &id return nil } -// MatchScrapedSceneStudio matches the provided studio with the studios +// MatchScrapedStudio matches the provided studio with the studios // in the database and sets the ID field if one is found. -func MatchScrapedSceneStudio(qb models.StudioReader, s *models.ScrapedSceneStudio) error { +func MatchScrapedStudio(qb models.StudioReader, s *models.ScrapedStudio) error { studio, err := qb.FindByName(s.Name, true) if err != nil { @@ -41,14 +45,18 @@ func MatchScrapedSceneStudio(qb models.StudioReader, s *models.ScrapedSceneStudi } id := strconv.Itoa(studio.ID) - s.ID = &id + s.StoredID = &id return nil } -// MatchScrapedSceneMovie matches the provided movie with the movies +// MatchScrapedMovie matches the provided movie with the movies // in the database and sets the ID field if one is found. -func MatchScrapedSceneMovie(qb models.MovieReader, m *models.ScrapedSceneMovie) error { - movies, err := qb.FindByNames([]string{m.Name}, true) +func MatchScrapedMovie(qb models.MovieReader, m *models.ScrapedMovie) error { + if m.Name == nil { + return nil + } + + movies, err := qb.FindByNames([]string{*m.Name}, true) if err != nil { return err @@ -60,13 +68,13 @@ func MatchScrapedSceneMovie(qb models.MovieReader, m *models.ScrapedSceneMovie) } id := strconv.Itoa(movies[0].ID) - m.ID = &id + m.StoredID = &id return nil } -// MatchScrapedSceneTag matches the provided tag with the tags +// MatchScrapedTag matches the provided tag with the tags // in the database and sets the ID field if one is found. -func MatchScrapedSceneTag(qb models.TagReader, s *models.ScrapedSceneTag) error { +func MatchScrapedTag(qb models.TagReader, s *models.ScrapedTag) error { t, err := tag.ByName(qb, s.Name) if err != nil { @@ -87,6 +95,6 @@ func MatchScrapedSceneTag(qb models.TagReader, s *models.ScrapedSceneTag) error } id := strconv.Itoa(t.ID) - s.ID = &id + s.StoredID = &id return nil } diff --git a/pkg/scraper/scrapers.go b/pkg/scraper/scrapers.go index 6c8d6e09d..41b57d2cf 100644 --- a/pkg/scraper/scrapers.go +++ b/pkg/scraper/scrapers.go @@ -3,10 +3,10 @@ package scraper import ( "context" "errors" + "fmt" "os" "path/filepath" "regexp" - "strconv" "strings" "github.com/stashapp/stash/pkg/logger" @@ -260,7 +260,7 @@ func (c Cache) postScrapePerformer(ret *models.ScrapedPerformer) error { return nil } -func (c Cache) postScrapeScenePerformer(ret *models.ScrapedScenePerformer) error { +func (c Cache) postScrapeScenePerformer(ret *models.ScrapedPerformer) error { if err := c.txnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error { tqb := r.Tag() @@ -290,13 +290,13 @@ func (c Cache) postScrapeScene(ret *models.ScrapedScene) error { return err } - if err := MatchScrapedScenePerformer(pqb, p); err != nil { + if err := MatchScrapedPerformer(pqb, p); err != nil { return err } } for _, p := range ret.Movies { - err := MatchScrapedSceneMovie(mqb, p) + err := MatchScrapedMovie(mqb, p) if err != nil { return err } @@ -309,7 +309,7 @@ func (c Cache) postScrapeScene(ret *models.ScrapedScene) error { ret.Tags = tags if ret.Studio != nil { - err := MatchScrapedSceneStudio(sqb, ret.Studio) + err := MatchScrapedStudio(sqb, ret.Studio) if err != nil { return err } @@ -335,7 +335,7 @@ func (c Cache) postScrapeGallery(ret *models.ScrapedGallery) error { sqb := r.Studio() for _, p := range ret.Performers { - err := MatchScrapedScenePerformer(pqb, p) + err := MatchScrapedPerformer(pqb, p) if err != nil { return err } @@ -348,7 +348,7 @@ func (c Cache) postScrapeGallery(ret *models.ScrapedGallery) error { ret.Tags = tags if ret.Studio != nil { - err := MatchScrapedSceneStudio(sqb, ret.Studio) + err := MatchScrapedStudio(sqb, ret.Studio) if err != nil { return err } @@ -362,12 +362,42 @@ func (c Cache) postScrapeGallery(ret *models.ScrapedGallery) error { return nil } -// ScrapeScene uses the scraper with the provided ID to scrape a scene. -func (c Cache) ScrapeScene(scraperID string, scene models.SceneUpdateInput) (*models.ScrapedScene, error) { +// ScrapeScene uses the scraper with the provided ID to scrape a scene using existing data. +func (c Cache) ScrapeScene(scraperID string, sceneID int) (*models.ScrapedScene, error) { + // find scraper with the provided id + s := c.findScraper(scraperID) + if s == nil { + return nil, fmt.Errorf("scraper with ID %s not found", scraperID) + } + + // get scene from id + scene, err := getScene(sceneID, c.txnManager) + if err != nil { + return nil, err + } + + ret, err := s.ScrapeSceneByScene(scene, c.txnManager, c.globalConfig) + + if err != nil { + return nil, err + } + + if ret != nil { + err = c.postScrapeScene(ret) + if err != nil { + return nil, err + } + } + + return ret, nil +} + +// ScrapeSceneFragment uses the scraper with the provided ID to scrape a scene. +func (c Cache) ScrapeSceneFragment(scraperID string, scene models.ScrapedSceneInput) (*models.ScrapedScene, error) { // find scraper with the provided id s := c.findScraper(scraperID) if s != nil { - ret, err := s.ScrapeScene(scene, c.txnManager, c.globalConfig) + ret, err := s.ScrapeSceneByFragment(scene, c.txnManager, c.globalConfig) if err != nil { return nil, err @@ -410,11 +440,40 @@ func (c Cache) ScrapeSceneURL(url string) (*models.ScrapedScene, error) { return nil, nil } -// ScrapeGallery uses the scraper with the provided ID to scrape a scene. -func (c Cache) ScrapeGallery(scraperID string, gallery models.GalleryUpdateInput) (*models.ScrapedGallery, error) { +// ScrapeGallery uses the scraper with the provided ID to scrape a gallery using existing data. +func (c Cache) ScrapeGallery(scraperID string, galleryID int) (*models.ScrapedGallery, error) { s := c.findScraper(scraperID) if s != nil { - ret, err := s.ScrapeGallery(gallery, c.txnManager, c.globalConfig) + // get gallery from id + gallery, err := getGallery(galleryID, c.txnManager) + if err != nil { + return nil, err + } + + ret, err := s.ScrapeGalleryByGallery(gallery, c.txnManager, c.globalConfig) + + if err != nil { + return nil, err + } + + if ret != nil { + err = c.postScrapeGallery(ret) + if err != nil { + return nil, err + } + } + + return ret, nil + } + + return nil, errors.New("Scraped with ID " + scraperID + " not found") +} + +// ScrapeGalleryFragment uses the scraper with the provided ID to scrape a gallery. +func (c Cache) ScrapeGalleryFragment(scraperID string, gallery models.ScrapedGalleryInput) (*models.ScrapedGallery, error) { + s := c.findScraper(scraperID) + if s != nil { + ret, err := s.ScrapeGalleryByFragment(gallery, c.txnManager, c.globalConfig) if err != nil { return nil, err @@ -457,23 +516,6 @@ func (c Cache) ScrapeGalleryURL(url string) (*models.ScrapedGallery, error) { return nil, nil } -func matchMovieStudio(qb models.StudioReader, s *models.ScrapedMovieStudio) error { - studio, err := qb.FindByName(s.Name, true) - - if err != nil { - return err - } - - if studio == nil { - // ignore - cannot match - return nil - } - - id := strconv.Itoa(studio.ID) - s.ID = &id - return nil -} - // ScrapeMovieURL uses the first scraper it finds that matches the URL // provided to scrape a movie. If no scrapers are found that matches // the URL, then nil is returned. @@ -487,7 +529,7 @@ func (c Cache) ScrapeMovieURL(url string) (*models.ScrapedMovie, error) { if ret.Studio != nil { if err := c.txnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error { - return matchMovieStudio(r.Studio(), ret.Studio) + return MatchScrapedStudio(r.Studio(), ret.Studio) }); err != nil { return nil, err } @@ -508,8 +550,8 @@ func (c Cache) ScrapeMovieURL(url string) (*models.ScrapedMovie, error) { return nil, nil } -func postProcessTags(tqb models.TagReader, scrapedTags []*models.ScrapedSceneTag) ([]*models.ScrapedSceneTag, error) { - var ret []*models.ScrapedSceneTag +func postProcessTags(tqb models.TagReader, scrapedTags []*models.ScrapedTag) ([]*models.ScrapedTag, error) { + var ret []*models.ScrapedTag excludePatterns := stash_config.GetInstance().GetScraperExcludeTagPatterns() var excludeRegexps []*regexp.Regexp @@ -533,7 +575,7 @@ ScrapeTag: } } - err := MatchScrapedSceneTag(tqb, t) + err := MatchScrapedTag(tqb, t) if err != nil { return nil, err } diff --git a/pkg/scraper/script.go b/pkg/scraper/script.go index 32f768d45..6b47dd6ef 100644 --- a/pkg/scraper/script.go +++ b/pkg/scraper/script.go @@ -63,7 +63,7 @@ func (s *scriptScraper) runScraperScript(inString string, out interface{}) error if err = cmd.Start(); err != nil { logger.Error("Error running scraper script: " + err.Error()) - return errors.New("Error running scraper script") + return errors.New("error running scraper script") } scanner := bufio.NewScanner(stderr) @@ -86,7 +86,7 @@ func (s *scriptScraper) runScraperScript(inString string, out interface{}) error logger.Debugf("Scraper script finished") if err != nil { - return errors.New("Error running scraper script") + return errors.New("error running scraper script") } return nil @@ -134,7 +134,21 @@ func (s *scriptScraper) scrapePerformerByURL(url string) (*models.ScrapedPerform return &ret, err } -func (s *scriptScraper) scrapeSceneByFragment(scene models.SceneUpdateInput) (*models.ScrapedScene, error) { +func (s *scriptScraper) scrapeSceneByScene(scene *models.Scene) (*models.ScrapedScene, error) { + inString, err := json.Marshal(sceneToUpdateInput(scene)) + + if err != nil { + return nil, err + } + + var ret models.ScrapedScene + + err = s.runScraperScript(string(inString), &ret) + + return &ret, err +} + +func (s *scriptScraper) scrapeSceneByFragment(scene models.ScrapedSceneInput) (*models.ScrapedScene, error) { inString, err := json.Marshal(scene) if err != nil { @@ -148,7 +162,21 @@ func (s *scriptScraper) scrapeSceneByFragment(scene models.SceneUpdateInput) (*m return &ret, err } -func (s *scriptScraper) scrapeGalleryByFragment(gallery models.GalleryUpdateInput) (*models.ScrapedGallery, error) { +func (s *scriptScraper) scrapeGalleryByGallery(gallery *models.Gallery) (*models.ScrapedGallery, error) { + inString, err := json.Marshal(galleryToUpdateInput(gallery)) + + if err != nil { + return nil, err + } + + var ret models.ScrapedGallery + + err = s.runScraperScript(string(inString), &ret) + + return &ret, err +} + +func (s *scriptScraper) scrapeGalleryByFragment(gallery models.ScrapedGalleryInput) (*models.ScrapedGallery, error) { inString, err := json.Marshal(gallery) if err != nil { diff --git a/pkg/scraper/stash.go b/pkg/scraper/stash.go index d37b82847..539057196 100644 --- a/pkg/scraper/stash.go +++ b/pkg/scraper/stash.go @@ -2,6 +2,7 @@ package scraper import ( "context" + "database/sql" "errors" "strconv" @@ -81,11 +82,40 @@ func (s *stashScraper) scrapePerformersByName(name string) ([]*models.ScrapedPer return ret, nil } +// need a separate for scraped stash performers - does not include remote_site_id or image +type scrapedTagStash struct { + Name string `graphql:"name" json:"name"` +} + +type scrapedPerformerStash struct { + Name *string `graphql:"name" json:"name"` + Gender *string `graphql:"gender" json:"gender"` + URL *string `graphql:"url" json:"url"` + Twitter *string `graphql:"twitter" json:"twitter"` + Instagram *string `graphql:"instagram" json:"instagram"` + Birthdate *string `graphql:"birthdate" json:"birthdate"` + Ethnicity *string `graphql:"ethnicity" json:"ethnicity"` + Country *string `graphql:"country" json:"country"` + EyeColor *string `graphql:"eye_color" json:"eye_color"` + Height *string `graphql:"height" json:"height"` + Measurements *string `graphql:"measurements" json:"measurements"` + FakeTits *string `graphql:"fake_tits" json:"fake_tits"` + CareerLength *string `graphql:"career_length" json:"career_length"` + Tattoos *string `graphql:"tattoos" json:"tattoos"` + Piercings *string `graphql:"piercings" json:"piercings"` + Aliases *string `graphql:"aliases" json:"aliases"` + Tags []*scrapedTagStash `graphql:"tags" json:"tags"` + Details *string `graphql:"details" json:"details"` + DeathDate *string `graphql:"death_date" json:"death_date"` + HairColor *string `graphql:"hair_color" json:"hair_color"` + Weight *string `graphql:"weight" json:"weight"` +} + func (s *stashScraper) scrapePerformerByFragment(scrapedPerformer models.ScrapedPerformerInput) (*models.ScrapedPerformer, error) { client := s.getStashClient() var q struct { - FindPerformer *models.ScrapedPerformerStash `graphql:"findPerformer(id: $f)"` + FindPerformer *scrapedPerformerStash `graphql:"findPerformer(id: $f)"` } performerID := *scrapedPerformer.URL @@ -100,13 +130,6 @@ func (s *stashScraper) scrapePerformerByFragment(scrapedPerformer models.Scraped return nil, err } - if q.FindPerformer != nil { - // the ids of the tags must be nilled - for _, t := range q.FindPerformer.Tags { - t.ID = nil - } - } - // need to copy back to a scraped performer ret := models.ScrapedPerformer{} err = copier.Copy(&ret, q.FindPerformer) @@ -123,25 +146,27 @@ func (s *stashScraper) scrapePerformerByFragment(scrapedPerformer models.Scraped return &ret, nil } -func (s *stashScraper) scrapeSceneByFragment(scene models.SceneUpdateInput) (*models.ScrapedScene, error) { +type scrapedStudioStash struct { + Name string `graphql:"name" json:"name"` + URL *string `graphql:"url" json:"url"` +} + +type scrapedSceneStash struct { + ID string `graphql:"id" json:"id"` + Title *string `graphql:"title" json:"title"` + Details *string `graphql:"details" json:"details"` + URL *string `graphql:"url" json:"url"` + Date *string `graphql:"date" json:"date"` + File *models.SceneFileType `graphql:"file" json:"file"` + Studio *scrapedStudioStash `graphql:"studio" json:"studio"` + Tags []*scrapedTagStash `graphql:"tags" json:"tags"` + Performers []*scrapedPerformerStash `graphql:"performers" json:"performers"` +} + +func (s *stashScraper) scrapeSceneByScene(scene *models.Scene) (*models.ScrapedScene, error) { // query by MD5 - // assumes that the scene exists in the database - id, err := strconv.Atoi(scene.ID) - if err != nil { - return nil, err - } - - var storedScene *models.Scene - if err := s.txnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error { - var err error - storedScene, err = r.Scene().Find(id) - return err - }); err != nil { - return nil, err - } - var q struct { - FindScene *models.ScrapedSceneStash `graphql:"findSceneByHash(input: $c)"` + FindScene *scrapedSceneStash `graphql:"findSceneByHash(input: $c)"` } type SceneHashInput struct { @@ -150,8 +175,8 @@ func (s *stashScraper) scrapeSceneByFragment(scene models.SceneUpdateInput) (*mo } input := SceneHashInput{ - Checksum: &storedScene.Checksum.String, - Oshash: &storedScene.OSHash.String, + Checksum: &scene.Checksum.String, + Oshash: &scene.OSHash.String, } vars := map[string]interface{}{ @@ -159,34 +184,18 @@ func (s *stashScraper) scrapeSceneByFragment(scene models.SceneUpdateInput) (*mo } client := s.getStashClient() - err = client.Query(context.Background(), &q, vars) - if err != nil { + if err := client.Query(context.Background(), &q, vars); err != nil { return nil, err } - if q.FindScene != nil { - // the ids of the studio, performers and tags must be nilled - if q.FindScene.Studio != nil { - q.FindScene.Studio.ID = nil - } - - for _, p := range q.FindScene.Performers { - p.ID = nil - } - - for _, t := range q.FindScene.Tags { - t.ID = nil - } - } - // need to copy back to a scraped scene ret := models.ScrapedScene{} - err = copier.Copy(&ret, q.FindScene) - if err != nil { + if err := copier.Copy(&ret, q.FindScene); err != nil { return nil, err } // get the performer image directly + var err error ret.Image, err = getStashSceneImage(s.config.StashServer.URL, q.FindScene.ID, s.globalConfig) if err != nil { return nil, err @@ -195,27 +204,25 @@ func (s *stashScraper) scrapeSceneByFragment(scene models.SceneUpdateInput) (*mo return &ret, nil } -func (s *stashScraper) scrapeGalleryByFragment(scene models.GalleryUpdateInput) (*models.ScrapedGallery, error) { - id, err := strconv.Atoi(scene.ID) - if err != nil { - return nil, err - } +func (s *stashScraper) scrapeSceneByFragment(scene models.ScrapedSceneInput) (*models.ScrapedScene, error) { + return nil, errors.New("scrapeSceneByFragment not supported for stash scraper") +} - // query by MD5 - // assumes that the gallery exists in the database - var storedGallery *models.Gallery - if err := s.txnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error { - qb := r.Gallery() - - var err error - storedGallery, err = qb.Find(id) - return err - }); err != nil { - return nil, err - } +type scrapedGalleryStash struct { + ID string `graphql:"id" json:"id"` + Title *string `graphql:"title" json:"title"` + Details *string `graphql:"details" json:"details"` + URL *string `graphql:"url" json:"url"` + Date *string `graphql:"date" json:"date"` + File *models.SceneFileType `graphql:"file" json:"file"` + Studio *scrapedStudioStash `graphql:"studio" json:"studio"` + Tags []*scrapedTagStash `graphql:"tags" json:"tags"` + Performers []*scrapedPerformerStash `graphql:"performers" json:"performers"` +} +func (s *stashScraper) scrapeGalleryByGallery(gallery *models.Gallery) (*models.ScrapedGallery, error) { var q struct { - FindGallery *models.ScrapedGalleryStash `graphql:"findGalleryByHash(input: $c)"` + FindGallery *scrapedGalleryStash `graphql:"findGalleryByHash(input: $c)"` } type GalleryHashInput struct { @@ -223,7 +230,7 @@ func (s *stashScraper) scrapeGalleryByFragment(scene models.GalleryUpdateInput) } input := GalleryHashInput{ - Checksum: &storedGallery.Checksum, + Checksum: &gallery.Checksum, } vars := map[string]interface{}{ @@ -231,36 +238,23 @@ func (s *stashScraper) scrapeGalleryByFragment(scene models.GalleryUpdateInput) } client := s.getStashClient() - err = client.Query(context.Background(), &q, vars) - if err != nil { + if err := client.Query(context.Background(), &q, vars); err != nil { return nil, err } - if q.FindGallery != nil { - // the ids of the studio, performers and tags must be nilled - if q.FindGallery.Studio != nil { - q.FindGallery.Studio.ID = nil - } - - for _, p := range q.FindGallery.Performers { - p.ID = nil - } - - for _, t := range q.FindGallery.Tags { - t.ID = nil - } - } - // need to copy back to a scraped scene ret := models.ScrapedGallery{} - err = copier.Copy(&ret, q.FindGallery) - if err != nil { + if err := copier.Copy(&ret, q.FindGallery); err != nil { return nil, err } return &ret, nil } +func (s *stashScraper) scrapeGalleryByFragment(scene models.ScrapedGalleryInput) (*models.ScrapedGallery, error) { + return nil, errors.New("scrapeGalleryByFragment not supported for stash scraper") +} + func (s *stashScraper) scrapePerformerByURL(url string) (*models.ScrapedPerformer, error) { return nil, errors.New("scrapePerformerByURL not supported for stash scraper") } @@ -277,17 +271,11 @@ func (s *stashScraper) scrapeMovieByURL(url string) (*models.ScrapedMovie, error return nil, errors.New("scrapeMovieByURL not supported for stash scraper") } -func sceneFromUpdateFragment(scene models.SceneUpdateInput, txnManager models.TransactionManager) (*models.Scene, error) { - id, err := strconv.Atoi(scene.ID) - if err != nil { - return nil, err - } - - // TODO - should we modify it with the input? +func getScene(sceneID int, txnManager models.TransactionManager) (*models.Scene, error) { var ret *models.Scene if err := txnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error { var err error - ret, err = r.Scene().Find(id) + ret, err = r.Scene().Find(sceneID) return err }); err != nil { return nil, err @@ -295,18 +283,66 @@ func sceneFromUpdateFragment(scene models.SceneUpdateInput, txnManager models.Tr return ret, nil } -func galleryFromUpdateFragment(gallery models.GalleryUpdateInput, txnManager models.TransactionManager) (ret *models.Gallery, err error) { - id, err := strconv.Atoi(gallery.ID) - if err != nil { - return nil, err +func sceneToUpdateInput(scene *models.Scene) models.SceneUpdateInput { + toStringPtr := func(s sql.NullString) *string { + if s.Valid { + return &s.String + } + + return nil } + dateToStringPtr := func(s models.SQLiteDate) *string { + if s.Valid { + return &s.String + } + + return nil + } + + return models.SceneUpdateInput{ + ID: strconv.Itoa(scene.ID), + Title: toStringPtr(scene.Title), + Details: toStringPtr(scene.Details), + URL: toStringPtr(scene.URL), + Date: dateToStringPtr(scene.Date), + } +} + +func getGallery(galleryID int, txnManager models.TransactionManager) (*models.Gallery, error) { + var ret *models.Gallery if err := txnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error { - ret, err = r.Gallery().Find(id) + var err error + ret, err = r.Gallery().Find(galleryID) return err }); err != nil { return nil, err } - return ret, nil } + +func galleryToUpdateInput(gallery *models.Gallery) models.GalleryUpdateInput { + toStringPtr := func(s sql.NullString) *string { + if s.Valid { + return &s.String + } + + return nil + } + + dateToStringPtr := func(s models.SQLiteDate) *string { + if s.Valid { + return &s.String + } + + return nil + } + + return models.GalleryUpdateInput{ + ID: strconv.Itoa(gallery.ID), + Title: toStringPtr(gallery.Title), + Details: toStringPtr(gallery.Details), + URL: toStringPtr(gallery.URL), + Date: dateToStringPtr(gallery.Date), + } +} diff --git a/pkg/scraper/stashbox/stash_box.go b/pkg/scraper/stashbox/stash_box.go index 4ab848fbf..f7bd53a99 100644 --- a/pkg/scraper/stashbox/stash_box.go +++ b/pkg/scraper/stashbox/stash_box.go @@ -66,8 +66,79 @@ func (c Client) QueryStashBoxScene(queryStr string) ([]*models.ScrapedScene, err } // FindStashBoxScenesByFingerprints queries stash-box for scenes using every -// scene's MD5/OSHASH checksum, or PHash -func (c Client) FindStashBoxScenesByFingerprints(sceneIDs []string) ([]*models.ScrapedScene, error) { +// scene's MD5/OSHASH checksum, or PHash, and returns results in the same order +// as the input slice. +func (c Client) FindStashBoxScenesByFingerprints(sceneIDs []string) ([][]*models.ScrapedScene, error) { + ids, err := utils.StringSliceToIntSlice(sceneIDs) + if err != nil { + return nil, err + } + + var fingerprints []string + // map fingerprints to their scene index + fpToScene := make(map[string][]int) + + if err := c.txnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error { + qb := r.Scene() + + for index, sceneID := range ids { + scene, err := qb.Find(sceneID) + if err != nil { + return err + } + + if scene == nil { + return fmt.Errorf("scene with id %d not found", sceneID) + } + + if scene.Checksum.Valid { + fingerprints = append(fingerprints, scene.Checksum.String) + fpToScene[scene.Checksum.String] = append(fpToScene[scene.Checksum.String], index) + } + + if scene.OSHash.Valid { + fingerprints = append(fingerprints, scene.OSHash.String) + fpToScene[scene.OSHash.String] = append(fpToScene[scene.OSHash.String], index) + } + + if scene.Phash.Valid { + phashStr := utils.PhashToString(scene.Phash.Int64) + fingerprints = append(fingerprints, phashStr) + fpToScene[phashStr] = append(fpToScene[phashStr], index) + } + } + + return nil + }); err != nil { + return nil, err + } + + allScenes, err := c.findStashBoxScenesByFingerprints(fingerprints) + if err != nil { + return nil, err + } + + // set the matched scenes back in their original order + ret := make([][]*models.ScrapedScene, len(sceneIDs)) + for _, s := range allScenes { + var addedTo []int + for _, fp := range s.Fingerprints { + sceneIndexes := fpToScene[fp.Hash] + for _, index := range sceneIndexes { + if !utils.IntInclude(addedTo, index) { + addedTo = append(addedTo, index) + ret[index] = append(ret[index], s) + } + } + } + } + + return ret, nil +} + +// FindStashBoxScenesByFingerprintsFlat queries stash-box for scenes using every +// scene's MD5/OSHASH checksum, or PHash, and returns results a flat slice. +func (c Client) FindStashBoxScenesByFingerprintsFlat(sceneIDs []string) ([]*models.ScrapedScene, error) { ids, err := utils.StringSliceToIntSlice(sceneIDs) if err != nil { return nil, err @@ -97,7 +168,8 @@ func (c Client) FindStashBoxScenesByFingerprints(sceneIDs []string) ([]*models.S } if scene.Phash.Valid { - fingerprints = append(fingerprints, utils.PhashToString(scene.Phash.Int64)) + phashStr := utils.PhashToString(scene.Phash.Int64) + fingerprints = append(fingerprints, phashStr) } } @@ -237,10 +309,18 @@ func (c Client) QueryStashBoxPerformer(queryStr string) ([]*models.StashBoxPerfo Results: performers, }, } + + // set the deprecated image field + for _, p := range res[0].Results { + if len(p.Images) > 0 { + p.Image = &p.Images[0] + } + } + return res, err } -func (c Client) queryStashBoxPerformer(queryStr string) ([]*models.ScrapedScenePerformer, error) { +func (c Client) queryStashBoxPerformer(queryStr string) ([]*models.ScrapedPerformer, error) { performers, err := c.client.SearchPerformer(context.TODO(), queryStr) if err != nil { return nil, err @@ -248,7 +328,7 @@ func (c Client) queryStashBoxPerformer(queryStr string) ([]*models.ScrapedSceneP performerFragments := performers.SearchPerformer - var ret []*models.ScrapedScenePerformer + var ret []*models.ScrapedPerformer for _, fragment := range performerFragments { performer := performerFragmentToScrapedScenePerformer(*fragment) ret = append(ret, performer) @@ -292,6 +372,50 @@ func (c Client) FindStashBoxPerformersByNames(performerIDs []string) ([]*models. return c.findStashBoxPerformersByNames(performers) } +func (c Client) FindStashBoxPerformersByPerformerNames(performerIDs []string) ([][]*models.ScrapedPerformer, error) { + ids, err := utils.StringSliceToIntSlice(performerIDs) + if err != nil { + return nil, err + } + + var performers []*models.Performer + + if err := c.txnManager.WithReadTxn(context.TODO(), func(r models.ReaderRepository) error { + qb := r.Performer() + + for _, performerID := range ids { + performer, err := qb.Find(performerID) + if err != nil { + return err + } + + if performer == nil { + return fmt.Errorf("performer with id %d not found", performerID) + } + + if performer.Name.Valid { + performers = append(performers, performer) + } + } + + return nil + }); err != nil { + return nil, err + } + + results, err := c.findStashBoxPerformersByNames(performers) + if err != nil { + return nil, err + } + + var ret [][]*models.ScrapedPerformer + for _, r := range results { + ret = append(ret, r.Results) + } + + return ret, nil +} + func (c Client) findStashBoxPerformersByNames(performers []*models.Performer) ([]*models.StashBoxPerformerQueryResult, error) { var ret []*models.StashBoxPerformerQueryResult for _, performer := range performers { @@ -413,14 +537,14 @@ func fetchImage(url string) (*string, error) { return &img, nil } -func performerFragmentToScrapedScenePerformer(p graphql.PerformerFragment) *models.ScrapedScenePerformer { +func performerFragmentToScrapedScenePerformer(p graphql.PerformerFragment) *models.ScrapedPerformer { id := p.ID images := []string{} for _, image := range p.Images { images = append(images, image.URL) } - sp := &models.ScrapedScenePerformer{ - Name: p.Name, + sp := &models.ScrapedPerformer{ + Name: &p.Name, Country: p.Country, Measurements: formatMeasurements(p.Measurements), CareerLength: formatCareerLength(p.CareerStartYear, p.CareerEndYear), @@ -430,10 +554,13 @@ func performerFragmentToScrapedScenePerformer(p graphql.PerformerFragment) *mode RemoteSiteID: &id, Images: images, // TODO - tags not currently supported - // TODO - Image - should be returned as a set of URLs. Will need a // graphql schema change to accommodate this. Leave off for now. } + if len(sp.Images) > 0 { + sp.Image = &sp.Images[0] + } + if p.Height != nil && *p.Height > 0 { hs := strconv.Itoa(*p.Height) sp.Height = &hs @@ -511,13 +638,13 @@ func sceneFragmentToScrapedScene(txnManager models.TransactionManager, s *graphq if s.Studio != nil { studioID := s.Studio.ID - ss.Studio = &models.ScrapedSceneStudio{ + ss.Studio = &models.ScrapedStudio{ Name: s.Studio.Name, URL: findURL(s.Studio.Urls, "HOME"), RemoteSiteID: &studioID, } - err := scraper.MatchScrapedSceneStudio(r.Studio(), ss.Studio) + err := scraper.MatchScrapedStudio(r.Studio(), ss.Studio) if err != nil { return err } @@ -526,7 +653,7 @@ func sceneFragmentToScrapedScene(txnManager models.TransactionManager, s *graphq for _, p := range s.Performers { sp := performerFragmentToScrapedScenePerformer(p.Performer) - err := scraper.MatchScrapedScenePerformer(pqb, sp) + err := scraper.MatchScrapedPerformer(pqb, sp) if err != nil { return err } @@ -535,11 +662,11 @@ func sceneFragmentToScrapedScene(txnManager models.TransactionManager, s *graphq } for _, t := range s.Tags { - st := &models.ScrapedSceneTag{ + st := &models.ScrapedTag{ Name: t.Name, } - err := scraper.MatchScrapedSceneTag(tqb, st) + err := scraper.MatchScrapedTag(tqb, st) if err != nil { return err } @@ -555,7 +682,7 @@ func sceneFragmentToScrapedScene(txnManager models.TransactionManager, s *graphq return ss, nil } -func (c Client) FindStashBoxPerformerByID(id string) (*models.ScrapedScenePerformer, error) { +func (c Client) FindStashBoxPerformerByID(id string) (*models.ScrapedPerformer, error) { performer, err := c.client.FindPerformerByID(context.TODO(), id) if err != nil { return nil, err @@ -565,13 +692,13 @@ func (c Client) FindStashBoxPerformerByID(id string) (*models.ScrapedScenePerfor return ret, nil } -func (c Client) FindStashBoxPerformerByName(name string) (*models.ScrapedScenePerformer, error) { +func (c Client) FindStashBoxPerformerByName(name string) (*models.ScrapedPerformer, error) { performers, err := c.client.SearchPerformer(context.TODO(), name) if err != nil { return nil, err } - var ret *models.ScrapedScenePerformer + var ret *models.ScrapedPerformer for _, performer := range performers.SearchPerformer { if strings.EqualFold(performer.Name, name) { ret = performerFragmentToScrapedScenePerformer(*performer) diff --git a/pkg/scraper/xpath.go b/pkg/scraper/xpath.go index e612b5f4d..71ab74a8d 100644 --- a/pkg/scraper/xpath.go +++ b/pkg/scraper/xpath.go @@ -124,18 +124,9 @@ func (s *xpathScraper) scrapePerformerByFragment(scrapedPerformer models.Scraped return nil, errors.New("scrapePerformerByFragment not supported for xpath scraper") } -func (s *xpathScraper) scrapeSceneByFragment(scene models.SceneUpdateInput) (*models.ScrapedScene, error) { - storedScene, err := sceneFromUpdateFragment(scene, s.txnManager) - if err != nil { - return nil, err - } - - if storedScene == nil { - return nil, errors.New("no scene found") - } - +func (s *xpathScraper) scrapeSceneByScene(scene *models.Scene) (*models.ScrapedScene, error) { // construct the URL - queryURL := queryURLParametersFromScene(storedScene) + queryURL := queryURLParametersFromScene(scene) if s.scraper.QueryURLReplacements != nil { queryURL.applyReplacements(s.scraper.QueryURLReplacements) } @@ -157,18 +148,13 @@ func (s *xpathScraper) scrapeSceneByFragment(scene models.SceneUpdateInput) (*mo return scraper.scrapeScene(q) } -func (s *xpathScraper) scrapeGalleryByFragment(gallery models.GalleryUpdateInput) (*models.ScrapedGallery, error) { - storedGallery, err := galleryFromUpdateFragment(gallery, s.txnManager) - if err != nil { - return nil, err - } - - if storedGallery == nil { - return nil, errors.New("no scene found") - } +func (s *xpathScraper) scrapeSceneByFragment(scene models.ScrapedSceneInput) (*models.ScrapedScene, error) { + return nil, errors.New("scrapeSceneByFragment not supported for xpath scraper") +} +func (s *xpathScraper) scrapeGalleryByGallery(gallery *models.Gallery) (*models.ScrapedGallery, error) { // construct the URL - queryURL := queryURLParametersFromGallery(storedGallery) + queryURL := queryURLParametersFromGallery(gallery) if s.scraper.QueryURLReplacements != nil { queryURL.applyReplacements(s.scraper.QueryURLReplacements) } @@ -190,6 +176,10 @@ func (s *xpathScraper) scrapeGalleryByFragment(gallery models.GalleryUpdateInput return scraper.scrapeGallery(q) } +func (s *xpathScraper) scrapeGalleryByFragment(gallery models.ScrapedGalleryInput) (*models.ScrapedGallery, error) { + return nil, errors.New("scrapeGalleryByFragment not supported for xpath scraper") +} + func (s *xpathScraper) loadURL(url string) (*html.Node, error) { r, err := loadURL(url, s.config, s.globalConfig) if err != nil { diff --git a/pkg/scraper/xpath_test.go b/pkg/scraper/xpath_test.go index 5983bd7a0..60fb5749f 100644 --- a/pkg/scraper/xpath_test.go +++ b/pkg/scraper/xpath_test.go @@ -593,7 +593,7 @@ func makeSceneXPathConfig() mappedScraper { return scraper } -func verifyTags(t *testing.T, expectedTagNames []string, actualTags []*models.ScrapedSceneTag) { +func verifyTags(t *testing.T, expectedTagNames []string, actualTags []*models.ScrapedTag) { t.Helper() i := 0 @@ -614,7 +614,7 @@ func verifyTags(t *testing.T, expectedTagNames []string, actualTags []*models.Sc } } -func verifyMovies(t *testing.T, expectedMovieNames []string, actualMovies []*models.ScrapedSceneMovie) { +func verifyMovies(t *testing.T, expectedMovieNames []string, actualMovies []*models.ScrapedMovie) { t.Helper() i := 0 @@ -625,7 +625,7 @@ func verifyMovies(t *testing.T, expectedMovieNames []string, actualMovies []*mod expectedMovie = expectedMovieNames[i] } if i < len(actualMovies) { - actualMovie = actualMovies[i].Name + actualMovie = *actualMovies[i].Name } if expectedMovie != actualMovie { @@ -635,7 +635,7 @@ func verifyMovies(t *testing.T, expectedMovieNames []string, actualMovies []*mod } } -func verifyPerformers(t *testing.T, expectedNames []string, expectedURLs []string, actualPerformers []*models.ScrapedScenePerformer) { +func verifyPerformers(t *testing.T, expectedNames []string, expectedURLs []string, actualPerformers []*models.ScrapedPerformer) { t.Helper() i := 0 @@ -651,7 +651,7 @@ func verifyPerformers(t *testing.T, expectedNames []string, expectedURLs []strin expectedURL = expectedURLs[i] } if i < len(actualPerformers) { - actualName = actualPerformers[i].Name + actualName = *actualPerformers[i].Name if actualPerformers[i].URL != nil { actualURL = *actualPerformers[i].URL } diff --git a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx index c83b94bc7..cd13c59df 100644 --- a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryEditPanel.tsx @@ -229,19 +229,18 @@ export const GalleryEditPanel: React.FC< } async function onScrapeClicked(scraper: GQL.Scraper) { + if (!gallery) return; + setIsLoading(true); try { - const galleryInput = getGalleryInput( - formik.values - ) as GQL.GalleryUpdateInput; - const result = await queryScrapeGallery(scraper.id, galleryInput); - if (!result.data || !result.data.scrapeGallery) { + const result = await queryScrapeGallery(scraper.id, gallery.id); + if (!result.data || !result.data.scrapeSingleGallery?.length) { Toast.success({ content: "No galleries found", }); return; } - setScrapedGallery(result.data.scrapeGallery); + setScrapedGallery(result.data.scrapeSingleGallery[0]); } catch (e) { Toast.error(e); } finally { diff --git a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryScrapeDialog.tsx b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryScrapeDialog.tsx index 701bbe019..2e34fc7af 100644 --- a/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryScrapeDialog.tsx +++ b/ui/v2.5/src/components/Galleries/GalleryDetails/GalleryScrapeDialog.tsx @@ -45,8 +45,8 @@ function renderScrapedStudioRow( title: string, result: ScrapeResult, onChange: (value: ScrapeResult) => void, - newStudio?: GQL.ScrapedSceneStudio, - onCreateNew?: (value: GQL.ScrapedSceneStudio) => void + newStudio?: GQL.ScrapedStudio, + onCreateNew?: (value: GQL.ScrapedStudio) => void ) { return ( , onChange: (value: ScrapeResult) => void, - newPerformers: GQL.ScrapedScenePerformer[], - onCreateNew?: (value: GQL.ScrapedScenePerformer) => void + newPerformers: GQL.ScrapedPerformer[], + onCreateNew?: (value: GQL.ScrapedPerformer) => void ) { + const performersCopy = newPerformers.map((p) => { + const name: string = p.name ?? ""; + return { ...p, name }; + }); + return ( ); @@ -139,8 +144,8 @@ function renderScrapedTagsRow( title: string, result: ScrapeResult, onChange: (value: ScrapeResult) => void, - newTags: GQL.ScrapedSceneTag[], - onCreateNew?: (value: GQL.ScrapedSceneTag) => void + newTags: GQL.ScrapedTag[], + onCreateNew?: (value: GQL.ScrapedTag) => void ) { return ( = ( props.scraped.studio?.stored_id ) ); - const [newStudio, setNewStudio] = useState< - GQL.ScrapedSceneStudio | undefined - >( + const [newStudio, setNewStudio] = useState( props.scraped.studio && !props.scraped.studio.stored_id ? props.scraped.studio : undefined @@ -241,9 +244,9 @@ export const GalleryScrapeDialog: React.FC = ( mapStoredIdObjects(props.scraped.performers ?? undefined) ) ); - const [newPerformers, setNewPerformers] = useState< - GQL.ScrapedScenePerformer[] - >(props.scraped.performers?.filter((t) => !t.stored_id) ?? []); + const [newPerformers, setNewPerformers] = useState( + props.scraped.performers?.filter((t) => !t.stored_id) ?? [] + ); const [tags, setTags] = useState>( new ScrapeResult( @@ -251,7 +254,7 @@ export const GalleryScrapeDialog: React.FC = ( mapStoredIdObjects(props.scraped.tags ?? undefined) ) ); - const [newTags, setNewTags] = useState( + const [newTags, setNewTags] = useState( props.scraped.tags?.filter((t) => !t.stored_id) ?? [] ); @@ -275,7 +278,7 @@ export const GalleryScrapeDialog: React.FC = ( return <>; } - async function createNewStudio(toCreate: GQL.ScrapedSceneStudio) { + async function createNewStudio(toCreate: GQL.ScrapedStudio) { try { const result = await createStudio({ variables: { @@ -308,7 +311,7 @@ export const GalleryScrapeDialog: React.FC = ( } } - async function createNewPerformer(toCreate: GQL.ScrapedScenePerformer) { + async function createNewPerformer(toCreate: GQL.ScrapedPerformer) { const input = makePerformerCreateInput(toCreate); try { @@ -349,7 +352,7 @@ export const GalleryScrapeDialog: React.FC = ( } } - async function createNewTag(toCreate: GQL.ScrapedSceneTag) { + async function createNewTag(toCreate: GQL.ScrapedTag) { const tagInput: GQL.TagCreateInput = { name: toCreate.name ?? "" }; try { const result = await createTag({ diff --git a/ui/v2.5/src/components/Movies/MovieDetails/MovieEditPanel.tsx b/ui/v2.5/src/components/Movies/MovieDetails/MovieEditPanel.tsx index 24d7f3af1..981d52751 100644 --- a/ui/v2.5/src/components/Movies/MovieDetails/MovieEditPanel.tsx +++ b/ui/v2.5/src/components/Movies/MovieDetails/MovieEditPanel.tsx @@ -203,8 +203,8 @@ export const MovieEditPanel: React.FC = ({ formik.setFieldValue("date", state.date ?? undefined); } - if (state.studio && state.studio.id) { - formik.setFieldValue("studio_id", state.studio.id ?? undefined); + if (state.studio && state.studio.stored_id) { + formik.setFieldValue("studio_id", state.studio.stored_id ?? undefined); } if (state.director) { diff --git a/ui/v2.5/src/components/Movies/MovieDetails/MovieScrapeDialog.tsx b/ui/v2.5/src/components/Movies/MovieDetails/MovieScrapeDialog.tsx index aad6735b6..467fa96a7 100644 --- a/ui/v2.5/src/components/Movies/MovieDetails/MovieScrapeDialog.tsx +++ b/ui/v2.5/src/components/Movies/MovieDetails/MovieScrapeDialog.tsx @@ -87,7 +87,10 @@ export const MovieScrapeDialog: React.FC = ( new ScrapeResult(props.movie.synopsis, props.scraped.synopsis) ); const [studio, setStudio] = useState>( - new ScrapeResult(props.movie.studio_id, props.scraped.studio?.id) + new ScrapeResult( + props.movie.studio_id, + props.scraped.studio?.stored_id + ) ); const [url, setURL] = useState>( new ScrapeResult(props.movie.url, props.scraped.url) @@ -123,7 +126,7 @@ export const MovieScrapeDialog: React.FC = ( const durationString = duration.getNewValue(); return { - name: name.getNewValue(), + name: name.getNewValue() ?? "", aliases: aliases.getNewValue(), duration: durationString, date: date.getNewValue(), @@ -131,7 +134,7 @@ export const MovieScrapeDialog: React.FC = ( synopsis: synopsis.getNewValue(), studio: newStudio ? { - id: newStudio, + stored_id: newStudio, name: "", } : undefined, diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx index d62743307..948ad7c86 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerEditPanel.tsx @@ -72,7 +72,7 @@ export const PerformerEditPanel: React.FC = ({ // Editing state const [scraper, setScraper] = useState(); - const [newTags, setNewTags] = useState(); + const [newTags, setNewTags] = useState(); const [isScraperModalOpen, setIsScraperModalOpen] = useState(false); const [isDeleteAlertOpen, setIsDeleteAlertOpen] = useState(false); @@ -224,7 +224,7 @@ export const PerformerEditPanel: React.FC = ({ return ret; } - async function createNewTag(toCreate: GQL.ScrapedSceneTag) { + async function createNewTag(toCreate: GQL.ScrapedTag) { const tagInput: GQL.TagCreateInput = { name: toCreate.name ?? "" }; try { const result = await createTag({ @@ -334,9 +334,10 @@ export const PerformerEditPanel: React.FC = ({ // otherwise follow existing behaviour (`undefined`) if ( (!isNew || [null, undefined].includes(formik.values.image)) && - state.image !== undefined + state.images && + state.images.length > 0 ) { - const imageStr = state.image; + const imageStr = state.images[0]; formik.setFieldValue("image", imageStr ?? undefined); } if (state.details) { @@ -524,20 +525,23 @@ export const PerformerEditPanel: React.FC = ({ const { __typename, - image: _image, + images: _image, tags: _tags, ...ret } = selectedPerformer; const result = await queryScrapePerformer(selectedScraper.id, ret); - if (!result?.data?.scrapePerformer) return; + if (!result?.data?.scrapeSinglePerformer?.length) return; + // assume one result // if this is a new performer, just dump the data if (isNew) { - updatePerformerEditStateFromScraper(result.data.scrapePerformer); + updatePerformerEditStateFromScraper( + result.data.scrapeSinglePerformer[0] + ); setScraper(undefined); } else { - setScrapedPerformer(result.data.scrapePerformer); + setScrapedPerformer(result.data.scrapeSinglePerformer[0]); } } catch (e) { Toast.error(e); @@ -569,12 +573,12 @@ export const PerformerEditPanel: React.FC = ({ } } - async function onScrapeStashBox(performerResult: GQL.ScrapedScenePerformer) { + async function onScrapeStashBox(performerResult: GQL.ScrapedPerformer) { setIsScraperModalOpen(false); - const result: Partial = { + const result: GQL.ScrapedPerformerDataFragment = { ...performerResult, - image: performerResult.images?.[0] ?? undefined, + images: performerResult.images ?? undefined, country: getCountryByISO(performerResult.country), __typename: "ScrapedPerformer", }; diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx index 2a48fca8c..d337fc5c7 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeDialog.tsx @@ -97,8 +97,8 @@ function renderScrapedTagsRow( title: string, result: ScrapeResult, onChange: (value: ScrapeResult) => void, - newTags: GQL.ScrapedSceneTag[], - onCreateNew?: (value: GQL.ScrapedSceneTag) => void + newTags: GQL.ScrapedTag[], + onCreateNew?: (value: GQL.ScrapedTag) => void ) { return ( = ( ) ); - const [newTags, setNewTags] = useState( + const [newTags, setNewTags] = useState( props.scraped.tags?.filter((t) => !t.stored_id) ?? [] ); const [image, setImage] = useState>( - new ScrapeResult(props.performer.image, props.scraped.image) + new ScrapeResult( + props.performer.image, + props.scraped.images && props.scraped.images.length > 0 + ? props.scraped.images[0] + : undefined + ) ); const allFields = [ @@ -338,7 +343,7 @@ export const PerformerScrapeDialog: React.FC = ( return <>; } - async function createNewTag(toCreate: GQL.ScrapedSceneTag) { + async function createNewTag(toCreate: GQL.ScrapedTag) { const tagInput: GQL.TagCreateInput = { name: toCreate.name ?? "" }; try { const result = await createTag({ @@ -375,8 +380,9 @@ export const PerformerScrapeDialog: React.FC = ( } function makeNewScrapedItem(): GQL.ScrapedPerformer { + const newImage = image.getNewValue(); return { - name: name.getNewValue(), + name: name.getNewValue() ?? "", aliases: aliases.getNewValue(), birthdate: birthdate.getNewValue(), ethnicity: ethnicity.getNewValue(), @@ -398,7 +404,7 @@ export const PerformerScrapeDialog: React.FC = ( name: "", }; }), - image: image.getNewValue(), + images: newImage ? [newImage] : undefined, details: details.getNewValue(), death_date: deathDate.getNewValue(), hair_color: hairColor.getNewValue(), diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeModal.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeModal.tsx index 823fa4d25..5c02f1f42 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeModal.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerScrapeModal.tsx @@ -30,7 +30,7 @@ const PerformerScrapeModal: React.FC = ({ const [query, setQuery] = useState(name ?? ""); const { data, loading } = useScrapePerformerList(scraper.id, query); - const performers = data?.scrapePerformerList ?? []; + const performers = data?.scrapeSinglePerformer ?? []; const onInputChange = debounce((input: string) => { setQuery(input); diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerStashBoxModal.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerStashBoxModal.tsx index 319bb73f6..734c2be18 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/PerformerStashBoxModal.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/PerformerStashBoxModal.tsx @@ -16,7 +16,7 @@ export interface IStashBox extends GQL.StashBox { interface IProps { instance: IStashBox; onHide: () => void; - onSelectPerformer: (performer: GQL.ScrapedScenePerformer) => void; + onSelectPerformer: (performer: GQL.ScrapedPerformer) => void; name?: string; } const PerformerStashBoxModal: React.FC = ({ @@ -28,17 +28,19 @@ const PerformerStashBoxModal: React.FC = ({ const intl = useIntl(); const inputRef = useRef(null); const [query, setQuery] = useState(name ?? ""); - const { data, loading } = GQL.useQueryStashBoxPerformerQuery({ + const { data, loading } = GQL.useScrapeSinglePerformerQuery({ variables: { - input: { + source: { stash_box_index: instance.index, - q: query, + }, + input: { + query, }, }, skip: query === "", }); - const performers = data?.queryStashBoxPerformer?.[0].results ?? []; + const performers = data?.scrapeSinglePerformer ?? []; const onInputChange = debounce((input: string) => { setQuery(input); diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx index f38749729..c08d5060e 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneEditPanel.tsx @@ -277,12 +277,12 @@ export const SceneEditPanel: React.FC = ({ setIsLoading(true); try { const result = await queryStashBoxScene(stashBoxIndex, scene.id); - if (!result.data || !result.data.queryStashBoxScene) { + if (!result.data || !result.data.scrapeSingleScene) { return; } - if (result.data.queryStashBoxScene.length > 0) { - setScrapedScene(result.data.queryStashBoxScene[0]); + if (result.data.scrapeSingleScene.length > 0) { + setScrapedScene(result.data.scrapeSingleScene[0]); } else { Toast.success({ content: "No scenes found", @@ -298,17 +298,15 @@ export const SceneEditPanel: React.FC = ({ async function onScrapeClicked(scraper: GQL.Scraper) { setIsLoading(true); try { - const result = await queryScrapeScene( - scraper.id, - getSceneInput(formik.values) - ); - if (!result.data || !result.data.scrapeScene) { + const result = await queryScrapeScene(scraper.id, scene.id); + if (!result.data || !result.data.scrapeSingleScene?.length) { Toast.success({ content: "No scenes found", }); return; } - setScrapedScene(result.data.scrapeScene); + // assume one returned scene + setScrapedScene(result.data.scrapeSingleScene[0]); } catch (e) { Toast.error(e); } finally { diff --git a/ui/v2.5/src/components/Scenes/SceneDetails/SceneScrapeDialog.tsx b/ui/v2.5/src/components/Scenes/SceneDetails/SceneScrapeDialog.tsx index 86989d358..430758f71 100644 --- a/ui/v2.5/src/components/Scenes/SceneDetails/SceneScrapeDialog.tsx +++ b/ui/v2.5/src/components/Scenes/SceneDetails/SceneScrapeDialog.tsx @@ -48,8 +48,8 @@ function renderScrapedStudioRow( title: string, result: ScrapeResult, onChange: (value: ScrapeResult) => void, - newStudio?: GQL.ScrapedSceneStudio, - onCreateNew?: (value: GQL.ScrapedSceneStudio) => void + newStudio?: GQL.ScrapedStudio, + onCreateNew?: (value: GQL.ScrapedStudio) => void ) { return ( , onChange: (value: ScrapeResult) => void, - newPerformers: GQL.ScrapedScenePerformer[], - onCreateNew?: (value: GQL.ScrapedScenePerformer) => void + newPerformers: GQL.ScrapedPerformer[], + onCreateNew?: (value: GQL.ScrapedPerformer) => void ) { + const performersCopy = newPerformers.map((p) => { + const name: string = p.name ?? ""; + return { ...p, name }; + }); + return ( ); @@ -142,9 +147,14 @@ function renderScrapedMoviesRow( title: string, result: ScrapeResult, onChange: (value: ScrapeResult) => void, - newMovies: GQL.ScrapedSceneMovie[], - onCreateNew?: (value: GQL.ScrapedSceneMovie) => void + newMovies: GQL.ScrapedMovie[], + onCreateNew?: (value: GQL.ScrapedMovie) => void ) { + const moviesCopy = newMovies.map((p) => { + const name: string = p.name ?? ""; + return { ...p, name }; + }); + return ( ); @@ -189,8 +199,8 @@ function renderScrapedTagsRow( title: string, result: ScrapeResult, onChange: (value: ScrapeResult) => void, - newTags: GQL.ScrapedSceneTag[], - onCreateNew?: (value: GQL.ScrapedSceneTag) => void + newTags: GQL.ScrapedTag[], + onCreateNew?: (value: GQL.ScrapedTag) => void ) { return ( = ( props.scraped.studio?.stored_id ) ); - const [newStudio, setNewStudio] = useState< - GQL.ScrapedSceneStudio | undefined - >( + const [newStudio, setNewStudio] = useState( props.scraped.studio && !props.scraped.studio.stored_id ? props.scraped.studio : undefined @@ -290,9 +298,9 @@ export const SceneScrapeDialog: React.FC = ( mapStoredIdObjects(props.scraped.performers ?? undefined) ) ); - const [newPerformers, setNewPerformers] = useState< - GQL.ScrapedScenePerformer[] - >(props.scraped.performers?.filter((t) => !t.stored_id) ?? []); + const [newPerformers, setNewPerformers] = useState( + props.scraped.performers?.filter((t) => !t.stored_id) ?? [] + ); const [movies, setMovies] = useState>( new ScrapeResult( @@ -300,7 +308,7 @@ export const SceneScrapeDialog: React.FC = ( mapStoredIdObjects(props.scraped.movies ?? undefined) ) ); - const [newMovies, setNewMovies] = useState( + const [newMovies, setNewMovies] = useState( props.scraped.movies?.filter((t) => !t.stored_id) ?? [] ); @@ -310,7 +318,7 @@ export const SceneScrapeDialog: React.FC = ( mapStoredIdObjects(props.scraped.tags ?? undefined) ) ); - const [newTags, setNewTags] = useState( + const [newTags, setNewTags] = useState( props.scraped.tags?.filter((t) => !t.stored_id) ?? [] ); @@ -339,7 +347,7 @@ export const SceneScrapeDialog: React.FC = ( return <>; } - async function createNewStudio(toCreate: GQL.ScrapedSceneStudio) { + async function createNewStudio(toCreate: GQL.ScrapedStudio) { try { const result = await createStudio({ variables: { @@ -366,7 +374,7 @@ export const SceneScrapeDialog: React.FC = ( } } - async function createNewPerformer(toCreate: GQL.ScrapedScenePerformer) { + async function createNewPerformer(toCreate: GQL.ScrapedPerformer) { const input = makePerformerCreateInput(toCreate); try { @@ -401,7 +409,7 @@ export const SceneScrapeDialog: React.FC = ( } } - async function createNewMovie(toCreate: GQL.ScrapedSceneMovie) { + async function createNewMovie(toCreate: GQL.ScrapedMovie) { let movieInput: GQL.MovieCreateInput = { name: "" }; try { movieInput = Object.assign(movieInput, toCreate); @@ -450,7 +458,7 @@ export const SceneScrapeDialog: React.FC = ( } } - async function createNewTag(toCreate: GQL.ScrapedSceneTag) { + async function createNewTag(toCreate: GQL.ScrapedTag) { const tagInput: GQL.TagCreateInput = { name: toCreate.name ?? "" }; try { const result = await createTag({ diff --git a/ui/v2.5/src/components/Tagger/StashSearchResult.tsx b/ui/v2.5/src/components/Tagger/StashSearchResult.tsx index d370f2bd8..46c1fdc6b 100755 --- a/ui/v2.5/src/components/Tagger/StashSearchResult.tsx +++ b/ui/v2.5/src/components/Tagger/StashSearchResult.tsx @@ -105,7 +105,7 @@ interface IStashSearchResultProps { setTags: boolean; endpoint: string; queueFingerprintSubmission: (sceneId: string, endpoint: string) => void; - createNewTag: (toCreate: GQL.ScrapedSceneTag) => void; + createNewTag: (toCreate: GQL.ScrapedTag) => void; excludedFields: Record; setExcludedFields: (v: Record) => void; } diff --git a/ui/v2.5/src/components/Tagger/TaggerList.tsx b/ui/v2.5/src/components/Tagger/TaggerList.tsx index 7860c6809..5edba84b0 100644 --- a/ui/v2.5/src/components/Tagger/TaggerList.tsx +++ b/ui/v2.5/src/components/Tagger/TaggerList.tsx @@ -8,7 +8,6 @@ import { stashBoxSceneBatchQuery, useTagCreate } from "src/core/StashService"; import { SceneQueue } from "src/models/sceneQueue"; import { useToast } from "src/hooks"; -import { uniqBy } from "lodash"; import { ITaggerConfig } from "./constants"; import { selectScenes, IStashBoxScene } from "./utils"; import { TaggerScene } from "./TaggerScene"; @@ -25,7 +24,7 @@ interface ITaggerListProps { queue?: SceneQueue; selectedEndpoint: { endpoint: string; index: number }; config: ITaggerConfig; - queryScene: (searchVal: string) => Promise; + queryScene: (searchVal: string) => Promise; fingerprintQueue: IFingerprintQueue; } @@ -42,27 +41,8 @@ function fingerprintSearchResults( return ret; } - // perform matching here - scenes.forEach((scene) => { - // ignore where scene entry is not in results - if ( - (scene.checksum && fingerprints[scene.checksum] !== undefined) || - (scene.oshash && fingerprints[scene.oshash] !== undefined) || - (scene.phash && fingerprints[scene.phash] !== undefined) - ) { - const fingerprintMatches = uniqBy( - [ - ...(fingerprints[scene.checksum ?? ""] ?? []), - ...(fingerprints[scene.oshash ?? ""] ?? []), - ...(fingerprints[scene.phash ?? ""] ?? []), - ].flat(), - (f) => f.stash_id - ); - - ret[scene.id] = fingerprintMatches; - } else { - delete ret[scene.id]; - } + scenes.forEach((s) => { + ret[s.id] = fingerprints[s.id]; }); return ret; @@ -119,7 +99,7 @@ export const TaggerList: React.FC = ({ queryScene(searchVal) .then((queryData) => { - const s = selectScenes(queryData.queryStashBoxScene); + const s = selectScenes(queryData.scrapeSingleScene); setSearchResults({ ...searchResults, [sceneID]: s, @@ -179,26 +159,10 @@ export const TaggerList: React.FC = ({ // clear search errors setSearchErrors({}); - selectScenes(results.data?.queryStashBoxScene).forEach((scene) => { - scene.fingerprints?.forEach((f) => { - newFingerprints[f.hash] = newFingerprints[f.hash] - ? [...newFingerprints[f.hash], scene] - : [scene]; - }); - }); - - // Null any ids that are still undefined since it means they weren't found - filteredScenes.forEach((scene) => { - if (scene.oshash) { - newFingerprints[scene.oshash] = newFingerprints[scene.oshash] ?? null; - } - if (scene.checksum) { - newFingerprints[scene.checksum] = - newFingerprints[scene.checksum] ?? null; - } - if (scene.phash) { - newFingerprints[scene.phash] = newFingerprints[scene.phash] ?? null; - } + sceneIDs.forEach((sceneID, index) => { + newFingerprints[sceneID] = selectScenes( + results.data.scrapeMultiScenes[index] + ); }); const newSearchResults = fingerprintSearchResults(scenes, newFingerprints); @@ -210,7 +174,7 @@ export const TaggerList: React.FC = ({ setFingerprintError(""); }; - async function createNewTag(toCreate: GQL.ScrapedSceneTag) { + async function createNewTag(toCreate: GQL.ScrapedTag) { const tagInput: GQL.TagCreateInput = { name: toCreate.name ?? "" }; try { const result = await createTag({ @@ -259,20 +223,12 @@ export const TaggerList: React.FC = ({ const canFingerprintSearch = () => scenes.some( - (s) => - s.stash_ids.length === 0 && - (!s.oshash || fingerprints[s.oshash] === undefined) && - (!s.checksum || fingerprints[s.checksum] === undefined) && - (!s.phash || fingerprints[s.phash] === undefined) + (s) => s.stash_ids.length === 0 && fingerprints[s.id] === undefined ); const getFingerprintCount = () => { return scenes.filter( - (s) => - s.stash_ids.length === 0 && - ((s.checksum && fingerprints[s.checksum]) || - (s.oshash && fingerprints[s.oshash]) || - (s.phash && fingerprints[s.phash])) + (s) => s.stash_ids.length === 0 && fingerprints[s.id]?.length > 0 ).length; }; diff --git a/ui/v2.5/src/components/Tagger/TaggerScene.tsx b/ui/v2.5/src/components/Tagger/TaggerScene.tsx index d1f745780..3f4019522 100644 --- a/ui/v2.5/src/components/Tagger/TaggerScene.tsx +++ b/ui/v2.5/src/components/Tagger/TaggerScene.tsx @@ -95,7 +95,7 @@ export interface ITaggerScene { tagScene: (scene: Partial) => void; endpoint: string; queueFingerprintSubmission: (sceneId: string, endpoint: string) => void; - createNewTag: (toCreate: GQL.ScrapedSceneTag) => void; + createNewTag: (toCreate: GQL.ScrapedTag) => void; } export const TaggerScene: React.FC = ({ diff --git a/ui/v2.5/src/components/Tagger/performers/PerformerTagger.tsx b/ui/v2.5/src/components/Tagger/performers/PerformerTagger.tsx index 3ebb581e0..82e9cc622 100755 --- a/ui/v2.5/src/components/Tagger/performers/PerformerTagger.tsx +++ b/ui/v2.5/src/components/Tagger/performers/PerformerTagger.tsx @@ -97,9 +97,7 @@ const PerformerTaggerList: React.FC = ({ const doBoxSearch = (performerID: string, searchVal: string) => { stashBoxPerformerQuery(searchVal, selectedEndpoint.index) .then((queryData) => { - const s = selectPerformers( - queryData.data?.queryStashBoxPerformer?.[0].results ?? [] - ); + const s = selectPerformers(queryData.data?.scrapeSinglePerformer ?? []); setSearchResults({ ...searchResults, [performerID]: s, @@ -137,7 +135,7 @@ const PerformerTaggerList: React.FC = ({ stashBoxPerformerQuery(stashID, endpointIndex) .then((queryData) => { const data = selectPerformers( - queryData.data?.queryStashBoxPerformer?.[0].results ?? [] + queryData.data?.scrapeSinglePerformer ?? [] ); if (data.length > 0) { setModalPerformer({ diff --git a/ui/v2.5/src/components/Tagger/utils.ts b/ui/v2.5/src/components/Tagger/utils.ts index d403e1156..8ba60a5d5 100644 --- a/ui/v2.5/src/components/Tagger/utils.ts +++ b/ui/v2.5/src/components/Tagger/utils.ts @@ -201,7 +201,7 @@ export interface IStashBoxScene { fingerprints: IStashBoxFingerprint[]; } -const selectStudio = (studio: GQL.ScrapedSceneStudio): IStashBoxStudio => ({ +const selectStudio = (studio: GQL.ScrapedStudio): IStashBoxStudio => ({ id: studio?.stored_id ?? undefined, stash_id: studio.remote_site_id!, name: studio.name, @@ -212,14 +212,14 @@ const selectFingerprints = ( scene: GQL.ScrapedScene | null ): IStashBoxFingerprint[] => scene?.fingerprints ?? []; -const selectTags = (tags: GQL.ScrapedSceneTag[]): IStashBoxTag[] => +const selectTags = (tags: GQL.ScrapedTag[]): IStashBoxTag[] => tags.map((t) => ({ id: t.stored_id ?? undefined, name: t.name ?? "", })); export const selectPerformers = ( - performers: GQL.ScrapedScenePerformer[] + performers: GQL.ScrapedPerformer[] ): IStashBoxPerformer[] => performers.map((p) => ({ id: p.stored_id ?? undefined, diff --git a/ui/v2.5/src/core/StashService.ts b/ui/v2.5/src/core/StashService.ts index 1762a6ff9..a1bc343dd 100644 --- a/ui/v2.5/src/core/StashService.ts +++ b/ui/v2.5/src/core/StashService.ts @@ -257,17 +257,17 @@ export const useSceneMarkerDestroy = () => export const useListPerformerScrapers = () => GQL.useListPerformerScrapersQuery(); export const useScrapePerformerList = (scraperId: string, q: string) => - GQL.useScrapePerformerListQuery({ - variables: { scraper_id: scraperId, query: q }, + GQL.useScrapeSinglePerformerQuery({ + variables: { + source: { + scraper_id: scraperId, + }, + input: { + query: q, + }, + }, skip: q === "", }); -export const useScrapePerformer = ( - scraperId: string, - scrapedPerformer: GQL.ScrapedPerformerInput -) => - GQL.useScrapePerformerQuery({ - variables: { scraper_id: scraperId, scraped_performer: scrapedPerformer }, - }); export const useListSceneScrapers = () => GQL.useListSceneScrapersQuery(); @@ -814,11 +814,15 @@ export const queryScrapePerformer = ( scraperId: string, scrapedPerformer: GQL.ScrapedPerformerInput ) => - client.query({ - query: GQL.ScrapePerformerDocument, + client.query({ + query: GQL.ScrapeSinglePerformerDocument, variables: { - scraper_id: scraperId, - scraped_performer: scrapedPerformer, + source: { + scraper_id: scraperId, + }, + input: { + performer_input: scrapedPerformer, + }, }, fetchPolicy: "network-only", }); @@ -859,26 +863,29 @@ export const queryScrapeMovieURL = (url: string) => fetchPolicy: "network-only", }); -export const queryScrapeScene = ( - scraperId: string, - scene: GQL.SceneUpdateInput -) => - client.query({ - query: GQL.ScrapeSceneDocument, +export const queryScrapeScene = (scraperId: string, sceneId: string) => + client.query({ + query: GQL.ScrapeSingleSceneDocument, variables: { - scraper_id: scraperId, - scene, + source: { + scraper_id: scraperId, + }, + input: { + scene_id: sceneId, + }, }, fetchPolicy: "network-only", }); export const queryStashBoxScene = (stashBoxIndex: number, sceneID: string) => - client.query({ - query: GQL.QueryStashBoxSceneDocument, + client.query({ + query: GQL.ScrapeSingleSceneDocument, variables: { - input: { + source: { stash_box_index: stashBoxIndex, - scene_ids: [sceneID], + }, + input: { + scene_id: sceneID, }, }, }); @@ -887,25 +894,28 @@ export const queryStashBoxPerformer = ( stashBoxIndex: number, performerID: string ) => - client.query({ - query: GQL.QueryStashBoxPerformerDocument, + client.query({ + query: GQL.ScrapeSinglePerformerDocument, variables: { - input: { + source: { stash_box_index: stashBoxIndex, - performer_ids: [performerID], + }, + input: { + performer_id: performerID, }, }, }); -export const queryScrapeGallery = ( - scraperId: string, - gallery: GQL.GalleryUpdateInput -) => - client.query({ - query: GQL.ScrapeGalleryDocument, +export const queryScrapeGallery = (scraperId: string, galleryId: string) => + client.query({ + query: GQL.ScrapeSingleGalleryDocument, variables: { - scraper_id: scraperId, - gallery, + source: { + scraper_id: scraperId, + }, + input: { + gallery_id: galleryId, + }, }, fetchPolicy: "network-only", }); @@ -1017,11 +1027,9 @@ export const queryParseSceneFilenames = ( fetchPolicy: "network-only", }); -export const makePerformerCreateInput = ( - toCreate: GQL.ScrapedScenePerformer -) => { +export const makePerformerCreateInput = (toCreate: GQL.ScrapedPerformer) => { const input: GQL.PerformerCreateInput = { - name: toCreate.name, + name: toCreate.name ?? "", url: toCreate.url, gender: stringToGender(toCreate.gender), birthdate: toCreate.birthdate, @@ -1051,37 +1059,47 @@ export const makePerformerCreateInput = ( }; export const stashBoxSceneQuery = (searchVal: string, stashBoxIndex: number) => - client?.query< - GQL.QueryStashBoxSceneQuery, - GQL.QueryStashBoxSceneQueryVariables - >({ - query: GQL.QueryStashBoxSceneDocument, - variables: { input: { q: searchVal, stash_box_index: stashBoxIndex } }, + client.query({ + query: GQL.ScrapeSingleSceneDocument, + variables: { + source: { + stash_box_index: stashBoxIndex, + }, + input: { + query: searchVal, + }, + }, }); export const stashBoxPerformerQuery = ( searchVal: string, stashBoxIndex: number ) => - client?.query< - GQL.QueryStashBoxPerformerQuery, - GQL.QueryStashBoxPerformerQueryVariables - >({ - query: GQL.QueryStashBoxPerformerDocument, - variables: { input: { q: searchVal, stash_box_index: stashBoxIndex } }, + client.query({ + query: GQL.ScrapeSinglePerformerDocument, + variables: { + source: { + stash_box_index: stashBoxIndex, + }, + input: { + query: searchVal, + }, + }, }); export const stashBoxSceneBatchQuery = ( sceneIds: string[], stashBoxIndex: number ) => - client?.query< - GQL.QueryStashBoxSceneQuery, - GQL.QueryStashBoxSceneQueryVariables - >({ - query: GQL.QueryStashBoxSceneDocument, + client.query({ + query: GQL.ScrapeMultiScenesDocument, variables: { - input: { scene_ids: sceneIds, stash_box_index: stashBoxIndex }, + source: { + stash_box_index: stashBoxIndex, + }, + input: { + scene_ids: sceneIds, + }, }, }); @@ -1089,12 +1107,14 @@ export const stashBoxPerformerBatchQuery = ( performerIds: string[], stashBoxIndex: number ) => - client?.query< - GQL.QueryStashBoxPerformerQuery, - GQL.QueryStashBoxPerformerQueryVariables - >({ - query: GQL.QueryStashBoxPerformerDocument, + client.query({ + query: GQL.ScrapeMultiPerformersDocument, variables: { - input: { performer_ids: performerIds, stash_box_index: stashBoxIndex }, + source: { + stash_box_index: stashBoxIndex, + }, + input: { + performer_ids: performerIds, + }, }, }); From 7a468413da4a5e270e1f5d9e150a1d453078965e Mon Sep 17 00:00:00 2001 From: gitgiggety <79809426+gitgiggety@users.noreply.github.com> Date: Tue, 7 Sep 2021 04:16:33 +0200 Subject: [PATCH 02/86] Add movies tab to Studios and Performers page (#1675) * Add movies tab to Studios page * Add performers filter to movies * Add movies tab to performers page --- graphql/schema/types/filters.graphql | 2 ++ pkg/sqlite/movies.go | 30 +++++++++++++++++++ .../src/components/Changelog/Changelog.tsx | 10 +++++-- .../components/Changelog/versions/v0100.md | 3 ++ ui/v2.5/src/components/Movies/MovieList.tsx | 7 ++++- .../Performers/PerformerDetails/Performer.tsx | 6 ++++ .../PerformerDetails/PerformerMoviesPanel.tsx | 14 +++++++++ .../Studios/StudioDetails/Studio.tsx | 7 ++++- .../StudioDetails/StudioMoviesPanel.tsx | 12 ++++++++ ui/v2.5/src/models/list-filter/movies.ts | 2 ++ 10 files changed, 89 insertions(+), 4 deletions(-) create mode 100644 ui/v2.5/src/components/Changelog/versions/v0100.md create mode 100644 ui/v2.5/src/components/Performers/PerformerDetails/PerformerMoviesPanel.tsx create mode 100644 ui/v2.5/src/components/Studios/StudioDetails/StudioMoviesPanel.tsx diff --git a/graphql/schema/types/filters.graphql b/graphql/schema/types/filters.graphql index 989b95863..76640bb24 100644 --- a/graphql/schema/types/filters.graphql +++ b/graphql/schema/types/filters.graphql @@ -176,6 +176,8 @@ input MovieFilterType { is_missing: String """Filter by url""" url: StringCriterionInput + """Filter to only include movies where performer appears in a scene""" + performers: MultiCriterionInput } input StudioFilterType { diff --git a/pkg/sqlite/movies.go b/pkg/sqlite/movies.go index 49d970459..9e69b8451 100644 --- a/pkg/sqlite/movies.go +++ b/pkg/sqlite/movies.go @@ -126,6 +126,7 @@ func (qb *movieQueryBuilder) makeFilter(movieFilter *models.MovieFilterType) *fi query.handleCriterion(movieIsMissingCriterionHandler(qb, movieFilter.IsMissing)) query.handleCriterion(stringCriterionHandler(movieFilter.URL, "movies.url")) query.handleCriterion(movieStudioCriterionHandler(qb, movieFilter.Studios)) + query.handleCriterion(moviePerformersCriterionHandler(qb, movieFilter.Performers)) return query } @@ -204,6 +205,35 @@ func movieStudioCriterionHandler(qb *movieQueryBuilder, studios *models.Hierarch return h.handler(studios) } +func moviePerformersCriterionHandler(qb *movieQueryBuilder, performers *models.MultiCriterionInput) criterionHandlerFunc { + return func(f *filterBuilder) { + if performers != nil && len(performers.Value) > 0 { + var args []interface{} + for _, arg := range performers.Value { + args = append(args, arg) + } + + // Hack, can't apply args to join, nor inner join on a left join, so use CTE instead + f.addWith(`movies_performers AS ( + SELECT movies_scenes.movie_id, performers_scenes.performer_id + FROM movies_scenes + INNER JOIN performers_scenes ON movies_scenes.scene_id = performers_scenes.scene_id + WHERE performers_scenes.performer_id IN`+getInBinding(len(performers.Value))+` + )`, args...) + f.addJoin("movies_performers", "", "movies.id = movies_performers.movie_id") + + if performers.Modifier == models.CriterionModifierIncludes { + f.addWhere("movies_performers.performer_id IS NOT NULL") + } else if performers.Modifier == models.CriterionModifierIncludesAll { + f.addWhere("movies_performers.performer_id IS NOT NULL") + f.addHaving("COUNT(DISTINCT movies_performers.performer_id) = ?", len(performers.Value)) + } else if performers.Modifier == models.CriterionModifierExcludes { + f.addWhere("movies_performers.performer_id IS NULL") + } + } + } +} + func (qb *movieQueryBuilder) getMovieSort(findFilter *models.FindFilterType) string { var sort string var direction string diff --git a/ui/v2.5/src/components/Changelog/Changelog.tsx b/ui/v2.5/src/components/Changelog/Changelog.tsx index 87b8ff1db..f504f81de 100644 --- a/ui/v2.5/src/components/Changelog/Changelog.tsx +++ b/ui/v2.5/src/components/Changelog/Changelog.tsx @@ -12,6 +12,7 @@ import V060 from "./versions/v060.md"; import V070 from "./versions/v070.md"; import V080 from "./versions/v080.md"; import V090 from "./versions/v090.md"; +import V0100 from "./versions/v0100.md"; import { MarkdownPage } from "../Shared/MarkdownPage"; // to avoid use of explicit any @@ -50,9 +51,9 @@ const Changelog: React.FC = () => { // after new release: // add entry to releases, using the current* fields // then update the current fields. - const currentVersion = stashVersion || "v0.9.0"; + const currentVersion = stashVersion || "v0.10.0"; const currentDate = buildDate; - const currentPage = V090; + const currentPage = V0100; const releases: IStashRelease[] = [ { @@ -61,6 +62,11 @@ const Changelog: React.FC = () => { page: currentPage, defaultOpen: true, }, + { + version: "v0.9.0", + date: "2021-09-06", + page: V090, + }, { version: "v0.8.0", date: "2021-07-02", diff --git a/ui/v2.5/src/components/Changelog/versions/v0100.md b/ui/v2.5/src/components/Changelog/versions/v0100.md new file mode 100644 index 000000000..3a7b1e1d1 --- /dev/null +++ b/ui/v2.5/src/components/Changelog/versions/v0100.md @@ -0,0 +1,3 @@ +### ✨ New Features +* Added Movies tab to Studio and Performer pages. ([#1675](https://github.com/stashapp/stash/pull/1675)) +* Support filtering Movies by Performers. ([#1675](https://github.com/stashapp/stash/pull/1675)) diff --git a/ui/v2.5/src/components/Movies/MovieList.tsx b/ui/v2.5/src/components/Movies/MovieList.tsx index 4329e9af3..ac5b3a5bc 100644 --- a/ui/v2.5/src/components/Movies/MovieList.tsx +++ b/ui/v2.5/src/components/Movies/MovieList.tsx @@ -18,7 +18,11 @@ import { import { ExportDialog, DeleteEntityDialog } from "src/components/Shared"; import { MovieCard } from "./MovieCard"; -export const MovieList: React.FC = () => { +interface IMovieList { + filterHook?: (filter: ListFilterModel) => ListFilterModel; +} + +export const MovieList: React.FC = ({ filterHook }) => { const intl = useIntl(); const history = useHistory(); const [isExportDialogOpen, setIsExportDialogOpen] = useState(false); @@ -73,6 +77,7 @@ export const MovieList: React.FC = () => { selectable: true, persistState: PersistanceLevel.ALL, renderDeleteDialog, + filterHook, }); async function viewRandom( diff --git a/ui/v2.5/src/components/Performers/PerformerDetails/Performer.tsx b/ui/v2.5/src/components/Performers/PerformerDetails/Performer.tsx index 5297e74f9..92f602679 100644 --- a/ui/v2.5/src/components/Performers/PerformerDetails/Performer.tsx +++ b/ui/v2.5/src/components/Performers/PerformerDetails/Performer.tsx @@ -22,6 +22,7 @@ import { PerformerDetailsPanel } from "./PerformerDetailsPanel"; import { PerformerOperationsPanel } from "./PerformerOperationsPanel"; import { PerformerScenesPanel } from "./PerformerScenesPanel"; import { PerformerGalleriesPanel } from "./PerformerGalleriesPanel"; +import { PerformerMoviesPanel } from "./PerformerMoviesPanel"; import { PerformerImagesPanel } from "./PerformerImagesPanel"; import { PerformerEditPanel } from "./PerformerEditPanel"; @@ -70,6 +71,7 @@ export const Performer: React.FC = () => { tab === "scenes" || tab === "galleries" || tab === "images" || + tab === "movies" || tab === "edit" || tab === "operations" ? tab @@ -91,6 +93,7 @@ export const Performer: React.FC = () => { Mousetrap.bind("e", () => setActiveTabKey("edit")); Mousetrap.bind("c", () => setActiveTabKey("scenes")); Mousetrap.bind("g", () => setActiveTabKey("galleries")); + Mousetrap.bind("m", () => setActiveTabKey("movies")); Mousetrap.bind("o", () => setActiveTabKey("operations")); Mousetrap.bind("f", () => setFavorite(!performer.favorite)); @@ -140,6 +143,9 @@ export const Performer: React.FC = () => { + + + ; +} + +export const PerformerMoviesPanel: React.FC = ({ + performer, +}) => { + return ; +}; diff --git a/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx b/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx index aaeb8ff4b..52323bc87 100644 --- a/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx +++ b/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx @@ -28,6 +28,7 @@ import { StudioChildrenPanel } from "./StudioChildrenPanel"; import { StudioPerformersPanel } from "./StudioPerformersPanel"; import { StudioEditPanel } from "./StudioEditPanel"; import { StudioDetailsPanel } from "./StudioDetailsPanel"; +import { StudioMoviesPanel } from "./StudioMoviesPanel"; interface IStudioParams { id?: string; @@ -186,7 +187,8 @@ export const Studio: React.FC = () => { tab === "childstudios" || tab === "images" || tab === "galleries" || - tab === "performers" + tab === "performers" || + tab === "movies" ? tab : "scenes"; const setActiveTabKey = (newTab: string | null) => { @@ -276,6 +278,9 @@ export const Studio: React.FC = () => { > + + + ; +} + +export const StudioMoviesPanel: React.FC = ({ studio }) => { + return ; +}; diff --git a/ui/v2.5/src/models/list-filter/movies.ts b/ui/v2.5/src/models/list-filter/movies.ts index 65b7af112..dcf495f76 100644 --- a/ui/v2.5/src/models/list-filter/movies.ts +++ b/ui/v2.5/src/models/list-filter/movies.ts @@ -5,6 +5,7 @@ import { import { MovieIsMissingCriterionOption } from "./criteria/is-missing"; import { RatingCriterionOption } from "./criteria/rating"; import { StudiosCriterionOption } from "./criteria/studios"; +import { PerformersCriterionOption } from "./criteria/performers"; import { ListFilterOptions } from "./filter-options"; import { DisplayMode } from "./types"; @@ -28,6 +29,7 @@ const criterionOptions = [ createStringCriterionOption("synopsis"), createMandatoryNumberCriterionOption("duration"), RatingCriterionOption, + PerformersCriterionOption, ]; export const MovieListFilterOptions = new ListFilterOptions( From b2b05fb3328878e861e225d8b00f19ad569ed983 Mon Sep 17 00:00:00 2001 From: gitgiggety <79809426+gitgiggety@users.noreply.github.com> Date: Tue, 7 Sep 2021 04:44:18 +0200 Subject: [PATCH 03/86] Add Movie option to Scene bulk edit (#1676) * Add Movie option to Scene bulk edit --- graphql/schema/types/scene.graphql | 1 + pkg/api/resolver_mutation_scene.go | 54 ++++++++++++++ .../components/Changelog/versions/v0100.md | 1 + .../components/Scenes/EditScenesDialog.tsx | 71 ++++++++++++++++++- ui/v2.5/src/components/Shared/MultiSet.tsx | 2 +- 5 files changed, 126 insertions(+), 3 deletions(-) diff --git a/graphql/schema/types/scene.graphql b/graphql/schema/types/scene.graphql index 734b5f596..4e2b0281b 100644 --- a/graphql/schema/types/scene.graphql +++ b/graphql/schema/types/scene.graphql @@ -103,6 +103,7 @@ input BulkSceneUpdateInput { gallery_ids: BulkUpdateIds performer_ids: BulkUpdateIds tag_ids: BulkUpdateIds + movie_ids: BulkUpdateIds } input SceneDestroyInput { diff --git a/pkg/api/resolver_mutation_scene.go b/pkg/api/resolver_mutation_scene.go index a0e788454..090599665 100644 --- a/pkg/api/resolver_mutation_scene.go +++ b/pkg/api/resolver_mutation_scene.go @@ -304,6 +304,18 @@ func (r *mutationResolver) BulkSceneUpdate(ctx context.Context, input models.Bul return err } } + + // Save the movies + if translator.hasField("movie_ids") { + movies, err := adjustSceneMovieIDs(qb, sceneID, *input.MovieIds) + if err != nil { + return err + } + + if err := qb.UpdateMovies(sceneID, movies); err != nil { + return err + } + } } return nil @@ -395,6 +407,48 @@ func adjustSceneGalleryIDs(qb models.SceneReader, sceneID int, ids models.BulkUp return adjustIDs(ret, ids), nil } +func adjustSceneMovieIDs(qb models.SceneReader, sceneID int, updateIDs models.BulkUpdateIds) ([]models.MoviesScenes, error) { + existingMovies, err := qb.GetMovies(sceneID) + if err != nil { + return nil, err + } + + // if we are setting the ids, just return the ids + if updateIDs.Mode == models.BulkUpdateIDModeSet { + existingMovies = []models.MoviesScenes{} + for _, idStr := range updateIDs.Ids { + id, _ := strconv.Atoi(idStr) + existingMovies = append(existingMovies, models.MoviesScenes{MovieID: id}) + } + + return existingMovies, nil + } + + for _, idStr := range updateIDs.Ids { + id, _ := strconv.Atoi(idStr) + + // look for the id in the list + foundExisting := false + for idx, existingMovie := range existingMovies { + if existingMovie.MovieID == id { + if updateIDs.Mode == models.BulkUpdateIDModeRemove { + // remove from the list + existingMovies = append(existingMovies[:idx], existingMovies[idx+1:]...) + } + + foundExisting = true + break + } + } + + if !foundExisting && updateIDs.Mode != models.BulkUpdateIDModeRemove { + existingMovies = append(existingMovies, models.MoviesScenes{MovieID: id}) + } + } + + return existingMovies, err +} + func (r *mutationResolver) SceneDestroy(ctx context.Context, input models.SceneDestroyInput) (bool, error) { sceneID, err := strconv.Atoi(input.ID) if err != nil { diff --git a/ui/v2.5/src/components/Changelog/versions/v0100.md b/ui/v2.5/src/components/Changelog/versions/v0100.md index 3a7b1e1d1..423c6007e 100644 --- a/ui/v2.5/src/components/Changelog/versions/v0100.md +++ b/ui/v2.5/src/components/Changelog/versions/v0100.md @@ -1,3 +1,4 @@ ### ✨ New Features +* Added Movies to Scene bulk edit dialog. ([#1676](https://github.com/stashapp/stash/pull/1676)) * Added Movies tab to Studio and Performer pages. ([#1675](https://github.com/stashapp/stash/pull/1675)) * Support filtering Movies by Performers. ([#1675](https://github.com/stashapp/stash/pull/1675)) diff --git a/ui/v2.5/src/components/Scenes/EditScenesDialog.tsx b/ui/v2.5/src/components/Scenes/EditScenesDialog.tsx index 71e18e641..72237531b 100644 --- a/ui/v2.5/src/components/Scenes/EditScenesDialog.tsx +++ b/ui/v2.5/src/components/Scenes/EditScenesDialog.tsx @@ -33,6 +33,11 @@ export const EditScenesDialog: React.FC = ( ); const [tagIds, setTagIds] = useState(); const [existingTagIds, setExistingTagIds] = useState(); + const [movieMode, setMovieMode] = React.useState( + GQL.BulkUpdateIdMode.Add + ); + const [movieIds, setMovieIds] = useState(); + const [existingMovieIds, setExistingMovieIds] = useState(); const [organized, setOrganized] = useState(); const [updateScenes] = useBulkSceneUpdate(getSceneInput()); @@ -58,6 +63,7 @@ export const EditScenesDialog: React.FC = ( const aggregateStudioId = getStudioId(props.selected); const aggregatePerformerIds = getPerformerIds(props.selected); const aggregateTagIds = getTagIds(props.selected); + const aggregateMovieIds = getMovieIds(props.selected); const sceneInput: GQL.BulkSceneUpdateInput = { ids: props.selected.map((scene) => { @@ -127,6 +133,21 @@ export const EditScenesDialog: React.FC = ( sceneInput.tag_ids = makeBulkUpdateIds(tagIds || [], tagMode); } + // if movieIds non-empty, then we are setting them + if ( + movieMode === GQL.BulkUpdateIdMode.Set && + (!movieIds || movieIds.length === 0) + ) { + // and all scenes have the same ids, + if (aggregateMovieIds.length > 0) { + // then unset the movieIds, otherwise ignore + sceneInput.movie_ids = makeBulkUpdateIds(movieIds || [], movieMode); + } + } else { + // if movieIds non-empty, then we are setting them + sceneInput.movie_ids = makeBulkUpdateIds(movieIds || [], movieMode); + } + if (organized !== undefined) { sceneInput.organized = organized; } @@ -228,12 +249,35 @@ export const EditScenesDialog: React.FC = ( return ret; } + function getMovieIds(state: GQL.SlimSceneDataFragment[]) { + let ret: string[] = []; + let first = true; + + state.forEach((scene: GQL.SlimSceneDataFragment) => { + if (first) { + ret = scene.movies ? scene.movies.map((m) => m.movie.id).sort() : []; + first = false; + } else { + const mIds = scene.movies + ? scene.movies.map((m) => m.movie.id).sort() + : []; + + if (!_.isEqual(ret, mIds)) { + ret = []; + } + } + }); + + return ret; + } + useEffect(() => { const state = props.selected; let updateRating: number | undefined; let updateStudioID: string | undefined; let updatePerformerIds: string[] = []; let updateTagIds: string[] = []; + let updateMovieIds: string[] = []; let updateOrganized: boolean | undefined; let first = true; @@ -244,12 +288,14 @@ export const EditScenesDialog: React.FC = ( .map((p) => p.id) .sort(); const sceneTagIDs = (scene.tags ?? []).map((p) => p.id).sort(); + const sceneMovieIDs = (scene.movies ?? []).map((m) => m.movie.id).sort(); if (first) { updateRating = sceneRating ?? undefined; updateStudioID = sceneStudioID; updatePerformerIds = scenePerformerIDs; updateTagIds = sceneTagIDs; + updateMovieIds = sceneMovieIDs; first = false; updateOrganized = scene.organized; } else { @@ -265,6 +311,9 @@ export const EditScenesDialog: React.FC = ( if (!_.isEqual(sceneTagIDs, updateTagIds)) { updateTagIds = []; } + if (!_.isEqual(sceneMovieIDs, updateMovieIds)) { + updateMovieIds = []; + } if (scene.organized !== updateOrganized) { updateOrganized = undefined; } @@ -275,8 +324,9 @@ export const EditScenesDialog: React.FC = ( setStudioId(updateStudioID); setExistingPerformerIds(updatePerformerIds); setExistingTagIds(updateTagIds); + setExistingMovieIds(updateMovieIds); setOrganized(updateOrganized); - }, [props.selected, performerMode, tagMode]); + }, [props.selected, performerMode, tagMode, movieMode]); useEffect(() => { if (checkboxRef.current) { @@ -285,7 +335,7 @@ export const EditScenesDialog: React.FC = ( }, [organized, checkboxRef]); function renderMultiSelect( - type: "performers" | "tags", + type: "performers" | "tags" | "movies", ids: string[] | undefined ) { let mode = GQL.BulkUpdateIdMode.Add; @@ -299,6 +349,10 @@ export const EditScenesDialog: React.FC = ( mode = tagMode; existingIds = existingTagIds; break; + case "movies": + mode = movieMode; + existingIds = existingMovieIds; + break; } return ( @@ -313,6 +367,9 @@ export const EditScenesDialog: React.FC = ( case "tags": setTagIds(itemIDs); break; + case "movies": + setMovieIds(itemIDs); + break; } }} onSetMode={(newMode) => { @@ -323,6 +380,9 @@ export const EditScenesDialog: React.FC = ( case "tags": setTagMode(newMode); break; + case "movies": + setMovieMode(newMode); + break; } }} ids={ids ?? []} @@ -409,6 +469,13 @@ export const EditScenesDialog: React.FC = ( {renderMultiSelect("tags", tagIds)} + + + + + {renderMultiSelect("movies", movieIds)} + + Date: Tue, 7 Sep 2021 05:18:32 +0200 Subject: [PATCH 04/86] Remove assignments to _ (#1685) It's a no-op if we are not using it, so it can be safely removed. --- pkg/api/changeset_translator.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/api/changeset_translator.go b/pkg/api/changeset_translator.go index 3864b1082..e1fc3868a 100644 --- a/pkg/api/changeset_translator.go +++ b/pkg/api/changeset_translator.go @@ -20,7 +20,7 @@ func getArgumentMap(ctx context.Context) map[string]interface{} { func getUpdateInputMap(ctx context.Context) map[string]interface{} { args := getArgumentMap(ctx) - input, _ := args[updateInputField] + input := args[updateInputField] var ret map[string]interface{} if input != nil { ret, _ = input.(map[string]interface{}) @@ -36,7 +36,7 @@ func getUpdateInputMap(ctx context.Context) map[string]interface{} { func getUpdateInputMaps(ctx context.Context) []map[string]interface{} { args := getArgumentMap(ctx) - input, _ := args[updateInputField] + input := args[updateInputField] var ret []map[string]interface{} if input != nil { // convert []interface{} into []map[string]interface{} From b76283df0860576b4b98bc62ffd6453128e1baaa Mon Sep 17 00:00:00 2001 From: SmallCoccinelle <89733524+SmallCoccinelle@users.noreply.github.com> Date: Tue, 7 Sep 2021 05:30:26 +0200 Subject: [PATCH 05/86] Fix data race in progress_test (#1696) The call to p.ExecuteTask happens in a separate go-routine. It writes, under m.mutex, into the job's details. However, the test goroutine itself reads j.Details which is a read race. Protect the reads in the test by the lock. This makes `package job` pass `go test -race` --- pkg/job/progress_test.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/job/progress_test.go b/pkg/job/progress_test.go index f2dbc0bc9..5bca05ae4 100644 --- a/pkg/job/progress_test.go +++ b/pkg/job/progress_test.go @@ -132,14 +132,18 @@ func TestExecuteTask(t *testing.T) { assert := assert.New(t) + m.mutex.Lock() // ensure task is added to the job details assert.Equal(taskDesciption, j.Details[0]) + m.mutex.Unlock() // allow task to finish close(c) time.Sleep(sleepTime) + m.mutex.Lock() // ensure task is removed from the job details assert.Len(j.Details, 0) + m.mutex.Unlock() } From 651d2e6373820a6c2683b38fc9786438b750ad1a Mon Sep 17 00:00:00 2001 From: fnoopv Date: Tue, 7 Sep 2021 11:30:59 +0800 Subject: [PATCH 06/86] Add missing translation (#1701) --- ui/v2.5/src/locales/zh-CN.json | 48 +++++++++++++++++++++++++++------- 1 file changed, 39 insertions(+), 9 deletions(-) diff --git a/ui/v2.5/src/locales/zh-CN.json b/ui/v2.5/src/locales/zh-CN.json index c65d6810e..3e12dd8d9 100644 --- a/ui/v2.5/src/locales/zh-CN.json +++ b/ui/v2.5/src/locales/zh-CN.json @@ -79,7 +79,11 @@ }, "temp_disable": "暂时关闭...", "temp_enable": "暂时启用...", - "view_random": "随机查看" + "view_random": "随机查看", + "confirm": "确认", + "select_folders": "选择目录", + "browse_for_image": "浏览图片…", + "open_random": "开启随机" }, "actions_name": "操作", "age": "年龄", @@ -243,6 +247,8 @@ "scraping": { "entity_metadata": "{entityType} 元数据", "entity_scrapers": "{entityType} 爬虫", + "excluded_tag_patterns_desc": "从抓取结果中排除的标签名称的正则表达式", + "excluded_tag_patterns_head": "排除标签的正则表达式", "scrapers": "爬虫", "search_by_name": "按名称搜索", "supported_types": "支持类型", @@ -293,6 +299,7 @@ "escape_chars": "使用\\转义特殊字符", "filename": "文件名称", "filename_pattern": "文件名规则", + "ignore_organized": "忽略已经整理的短片", "ignored_words": "忽略字符", "matches_with": "使用 {i} 进行匹配", "select_parser_recipe": "选择需要分析的字段", @@ -312,6 +319,10 @@ "description": "用于互动场景的快速连接密钥", "heading": "快速连接密钥" }, + "funscript_offset": { + "description": "交互式脚本播放的时间偏移量(以毫秒为单位)。", + "heading": "Funscript偏移量(毫秒)" + }, "language": { "heading": "语言" }, @@ -323,6 +334,14 @@ "description": "在导航栏上显示或隐藏不同类型的内容", "heading": "菜单列表" }, + "performers": { + "options": { + "image_location": { + "heading": "自定义演员图像路径", + "description": "默认演员图像的自定义路径。 留空以使用内置默认值" + } + } + }, "preview_type": { "description": "影片预览设置", "heading": "预览类型", @@ -372,6 +391,11 @@ "country": "国家", "cover_image": "封面图片", "created_at": "创建时间", + "criterion": { + "greater_than": "大于", + "less_than": "小于", + "value": "值" + }, "criterion_modifier": { "equals": "是", "excludes": "不包含", @@ -384,14 +408,17 @@ "matches_regex": "符合正则表达式", "not_equals": "不是", "not_matches_regex": "不符合正则表达式", - "not_null": "不为空" + "not_null": "不为空", + "between": "在中间", + "not_between": "不在中间" }, + "custom": "自定义", "date": "日期", "death_date": "去世日期", "death_year": "去世年份", "descending": "降序", "detail": "详情", - "details": "介绍", + "details": "简介", "developmentVersion": "开发版本", "dialogs": { "delete_confirm": "确定要删除 {entityName} 吗?", @@ -456,24 +483,24 @@ "search_accuracy_label": "搜索准确度", "title": "重复短片" }, - "duration": "长度", + "duration": "时长", "effect_filters": { "aspect": "比例", - "blue": "蓝", + "blue": "蓝色", "blur": "模糊", "brightness": "亮度", "contrast": "对比", "gamma": "伽马", - "green": "绿", + "green": "绿色", "hue": "色调", "name": "滤镜", "name_transforms": "视频变换", - "red": "红", + "red": "红色", "reset_filters": "重置滤镜", "reset_transforms": "重置变换", "rotate": "旋转", - "rotate_left_and_scale": "向左旋转 & 度", - "rotate_right_and_scale": "向右旋转 & 度", + "rotate_left_and_scale": "向左旋转 90 度", + "rotate_right_and_scale": "向右旋转 90 度", "saturation": "饱和", "scale": "大小", "warmth": "色温" @@ -489,6 +516,7 @@ "filter_name": "过滤器名称", "filters": "过滤器", "framerate": "帧率", + "frames_per_second": "{value} 帧每秒", "galleries": "图库", "gallery": "图库", "gallery_count": "图库数量", @@ -525,6 +553,7 @@ "stream": "视频流地址", "video_codec": "视频编码" }, + "megabits_per_second": "{value} Mbps/s", "metadata": "元数据", "movie": "电影", "movie_scene_number": "电影短片编码", @@ -557,6 +586,7 @@ "scene_count": "短片数量", "scene_id": "短片ID", "scenes": "短片", + "scenes-duration": "短片时长", "scenes-size": "短片大小", "scenes_updated_at": "短片更新时间", "sceneTagger": "短片标记器", From a3f38d8edf1614d753d55d0ed2e6b887bafe99bd Mon Sep 17 00:00:00 2001 From: SmallCoccinelle <89733524+SmallCoccinelle@users.noreply.github.com> Date: Tue, 7 Sep 2021 06:28:40 +0200 Subject: [PATCH 07/86] When stopping, close the database (#1686) * When stopping, close the database This patch is likely to cause errornous behavior in the application. This is due to the fact that the application doesn't gracefully shut down, but is forcefully terminated. However, the purpose is to uncover what needs to be done, to make it a more graceful shutdown. --- main.go | 6 ++++++ pkg/database/database.go | 7 +++++++ pkg/manager/manager.go | 7 +++++++ 3 files changed, 20 insertions(+) diff --git a/main.go b/main.go index e08271a7f..653438e01 100644 --- a/main.go +++ b/main.go @@ -8,6 +8,7 @@ import ( "syscall" "github.com/stashapp/stash/pkg/api" + "github.com/stashapp/stash/pkg/logger" "github.com/stashapp/stash/pkg/manager" _ "github.com/golang-migrate/migrate/v4/database/sqlite3" @@ -21,6 +22,11 @@ func main() { // stop any profiling at exit defer pprof.StopCPUProfile() blockForever() + + err := manager.GetInstance().Shutdown() + if err != nil { + logger.Errorf("Error when closing: %s", err) + } } func blockForever() { diff --git a/pkg/database/database.go b/pkg/database/database.go index c992601d8..50735b95d 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -89,6 +89,13 @@ func Initialize(databasePath string) error { return nil } +func Close() error { + WriteMu.Lock() + defer WriteMu.Unlock() + + return DB.Close() +} + func open(databasePath string, disableForeignKeys bool) *sqlx.DB { // https://github.com/mattn/go-sqlite3 url := "file:" + databasePath + "?_journal=WAL" diff --git a/pkg/manager/manager.go b/pkg/manager/manager.go index 638332619..6581ec3fe 100644 --- a/pkg/manager/manager.go +++ b/pkg/manager/manager.go @@ -378,3 +378,10 @@ func (s *singleton) GetSystemStatus() *models.SystemStatus { ConfigPath: &configFile, } } + +// Shutdown gracefully stops the manager +func (s *singleton) Shutdown() error { + // TODO: Each part of the manager needs to gracefully stop at some point + // for now, we just close the database. + return database.Close() +} From b482fbc79600037c3e19ab899105799d4dea9513 Mon Sep 17 00:00:00 2001 From: Alpaca Serious <80459176+alpacaserious@users.noreply.github.com> Date: Wed, 8 Sep 2021 03:08:07 +0200 Subject: [PATCH 08/86] Swedish Translation (#1691) * Added Swedish translation * Update SettingsInterfacePanel.tsx * Update index.ts * Update sv-SE.json --- .../components/Changelog/versions/v0100.md | 3 + .../SettingsInterfacePanel.tsx | 1 + ui/v2.5/src/locales/index.ts | 2 + ui/v2.5/src/locales/sv-SE.json | 634 ++++++++++++++++++ 4 files changed, 640 insertions(+) create mode 100644 ui/v2.5/src/locales/sv-SE.json diff --git a/ui/v2.5/src/components/Changelog/versions/v0100.md b/ui/v2.5/src/components/Changelog/versions/v0100.md index 423c6007e..5cb28b991 100644 --- a/ui/v2.5/src/components/Changelog/versions/v0100.md +++ b/ui/v2.5/src/components/Changelog/versions/v0100.md @@ -2,3 +2,6 @@ * Added Movies to Scene bulk edit dialog. ([#1676](https://github.com/stashapp/stash/pull/1676)) * Added Movies tab to Studio and Performer pages. ([#1675](https://github.com/stashapp/stash/pull/1675)) * Support filtering Movies by Performers. ([#1675](https://github.com/stashapp/stash/pull/1675)) + +### 🎨 Improvements +* Added sv-SE language option. ([#1691](https://github.com/stashapp/stash/pull/1691)) diff --git a/ui/v2.5/src/components/Settings/SettingsInterfacePanel/SettingsInterfacePanel.tsx b/ui/v2.5/src/components/Settings/SettingsInterfacePanel/SettingsInterfacePanel.tsx index e464ae610..311c2ed8b 100644 --- a/ui/v2.5/src/components/Settings/SettingsInterfacePanel/SettingsInterfacePanel.tsx +++ b/ui/v2.5/src/components/Settings/SettingsInterfacePanel/SettingsInterfacePanel.tsx @@ -124,6 +124,7 @@ export const SettingsInterfacePanel: React.FC = () => { + diff --git a/ui/v2.5/src/locales/index.ts b/ui/v2.5/src/locales/index.ts index f42a9f66c..85a2ec62a 100644 --- a/ui/v2.5/src/locales/index.ts +++ b/ui/v2.5/src/locales/index.ts @@ -2,6 +2,7 @@ import deDE from "./de-DE.json"; import enGB from "./en-GB.json"; import enUS from "./en-US.json"; import ptBR from "./pt-BR.json"; +import svSE from "./sv-SE.json"; import zhTW from "./zh-TW.json"; import zhCN from "./zh-CN.json"; @@ -10,6 +11,7 @@ export default { enGB, enUS, ptBR, + svSE, zhTW, zhCN, }; diff --git a/ui/v2.5/src/locales/sv-SE.json b/ui/v2.5/src/locales/sv-SE.json new file mode 100644 index 000000000..e5aff66e2 --- /dev/null +++ b/ui/v2.5/src/locales/sv-SE.json @@ -0,0 +1,634 @@ +{ + "actions": { + "add": "Lägg till", + "add_directory": "Lägg till mapp", + "add_entity": "Lägg till {entityType}", + "add_to_entity": "Lägg till {entityType}", + "allow": "Tillåt", + "allow_temporarily": "Tillåt temporärt", + "apply": "Applicera", + "auto_tag": "Auto Tagga", + "backup": "Backup", + "cancel": "Avbryt", + "clean": "Rensa", + "clear_back_image": "Rensa bak-bild", + "clear_front_image": "Rensa fram-bild", + "clear_image": "Rensa bild", + "close": "Stäng", + "create": "Skapa", + "create_entity": "Skapa {entityType}", + "create_marker": "Skapa Markör", + "created_entity": "Skapade {entity_type}: {entity_name}", + "delete": "Radera", + "delete_entity": "Radera {entityType}", + "delete_file": "Radera fil", + "delete_generated_supporting_files": "Radera genererade filer", + "disallow": "Tillåt ej", + "download": "Ladda ner", + "download_backup": "Ladda ner backup", + "edit": "Redigera", + "export": "Exportera…", + "export_all": "Exportera allt…", + "find": "Sök", + "from_file": "Från fil…", + "from_url": "Från URL…", + "full_export": "Fullständig export", + "full_import": "Fullständig import", + "generate": "Generera", + "generate_thumb_default": "Generera miniatyrbild", + "generate_thumb_from_current": "Generera miniatyrbild från nuvarande", + "hash_migration": "Hash migration", + "hide": "Göm", + "import": "Importera…", + "import_from_file": "Importera från fil", + "merge": "Slå samman", + "merge_from": "Slå samman från", + "merge_into": "Slå samman till", + "not_running": "Kör ej", + "overwrite": "Skriv över", + "play_random": "Spela slumpad", + "play_selected": "Spela vald", + "preview": "Förhandsvisa", + "refresh": "Uppdatera", + "reload_plugins": "Ladda om plugins", + "reload_scrapers": "Ladda om skrapare", + "remove": "Ta bort", + "rename_gen_files": "Döp om genererade filer", + "rescan": "Skanna om", + "reshuffle": "Ny ordning", + "running": "kör", + "save": "Spara", + "save_filter": "Spara filter", + "scan": "Skanna", + "scrape_with": "Skrapa med…", + "search": "Sök", + "select_all": "Välj allt", + "select_none": "Välj inget", + "selective_auto_tag": "Selektiv Auto Tag", + "selective_scan": "Selektiv skanning", + "set_as_default": "Välj som standard", + "set_back_image": "Bak-bild…", + "set_front_image": "Fram-bild…", + "set_image": "Välj bild…", + "show": "Visa", + "skip": "Hoppa över", + "tasks": { + "clean_confirm_message": "Är du säker att du vill rensa? Detta kommer radera databasinformation och genererade filer för alla scener och gallerier som inte längre finns på filsystemet.", + "dry_mode_selected": "Torrt läge valt. Inget kommer raderas utan bara loggande kommer ske.", + "import_warning": "Är du säker att du vill importera? Detta kommer radera databasen och importera från din tidigare exporterade metadata." + }, + "temp_disable": "Stäng av tillfälligt…", + "temp_enable": "Sätt på tillfälligt…", + "view_random": "Visa slumpad", + "confirm": "Bekräfta", + "select_folders": "Välj mappar", + "browse_for_image": "Bläddra efter bild…", + "open_random": "Öppna slumpad" + }, + "actions_name": "Handlingar", + "age": "Ålder", + "aliases": "Alias", + "also_known_as": "Också känd som", + "ascending": "Uppåtstigande", + "average_resolution": "Genomsnittlig upplösning", + "birth_year": "Födelseår", + "birthdate": "Födelsedatum", + "bitrate": "Bithastighet", + "career_length": "Karriärlängd", + "child_studios": "Underordnade studior", + "component_tagger": { + "config": { + "active_instance": "Aktiv stash-box instans:", + "blacklist_desc": "Svartlistade objekt exkluderas från sökningar. Notera att de är regex och skiftlägeskänsliga. Vissa karaktärer måste unvikas med ett snedstreck: {chars_require_escape}", + "blacklist_label": "Svartlista", + "query_mode_auto": "Auto", + "query_mode_auto_desc": "Andänder metadata om det finns, annars filnamn", + "query_mode_dir": "Mapp", + "query_mode_dir_desc": "Använder enbart videofilens mapp", + "query_mode_filename": "Filnamn", + "query_mode_filename_desc": "Använder enbart filnamn", + "query_mode_label": "Sökläge", + "query_mode_metadata": "Metadata", + "query_mode_metadata_desc": "Använder enbart metadata", + "query_mode_path": "Filsökväg", + "query_mode_path_desc": "Använder komplett Filsökväg", + "set_cover_desc": "Skriv över miniatyrbild om en hittas.", + "set_cover_label": "Välj scenens miniatyrbild", + "set_tag_desc": "Tagga scenen antingen genom att skriva över eller slå samman med de redan existerande.", + "set_tag_label": "Tagga", + "show_male_desc": "Välj huruvida manliga stjärnor kommer vara tillgängliga att tagga.", + "show_male_label": "Visa manliga stjärnor" + }, + "noun_query": "Query", + "results": { + "fp_found": "{fpCount, plural, =0 {Inga nya fingeravtryck hittades} other {# Nya fingeravtryck hittades}}", + "fp_matches": "Speltid matchar", + "fp_matches_multi": "Speltiden matchar {matchCount}/{durationsLength} fingeravtryck", + "hash_matches": "{hash_type} matchar", + "match_failed_already_tagged": "Scen redan taggad", + "match_failed_no_result": "Inga resultat hittades", + "match_success": "Scen taggad", + "duration_off": "Speltiden fel med minst {number}s", + "duration_unknown": "Okänd speltid" + }, + "verb_match_fp": "Matcha fingeravtryck", + "verb_matched": "Matchad", + "verb_submit_fp": "Skicka {fpCount, plural, one{# Fingeravtryck} other{# Fingeravtryck}}", + "verb_toggle_config": "{toggle} {configuration}", + "verb_toggle_unmatched": "{toggle} ej matchade scener" + }, + "config": { + "about": { + "build_hash": "Bygghash:", + "build_time": "Byggtid:", + "check_for_new_version": "Kolla efter ny version", + "latest_version_build_hash": "Senaste versionens bygghash:", + "new_version_notice": "[Ny]", + "stash_discord": "Gå med i vår {url} kanal", + "stash_home": "Stash hemma vid {url}", + "stash_open_collective": "Stötta oss genom {url}", + "stash_wiki": "Stash {url} sida", + "version": "Version" + }, + "categories": { + "about": "Om", + "interface": "Gränssnitt", + "logs": "Loggar", + "plugins": "Plugins", + "scrapers": "Skrapare", + "tasks": "Jobb", + "tools": "Verktyg" + }, + "dlna": { + "allow_temp_ip": "Tillåt {tempIP}", + "allowed_ip_addresses": "Tillåtna IP-adresser", + "default_ip_whitelist": "Standard IP Whitelist", + "default_ip_whitelist_desc": "Standard IP-adress för att komma åt DLNA. Använd {wildcard} för att tillåta alla IP-adresser.", + "enabled_by_default": "På som standard", + "network_interfaces": "Nätverksgränssnitt", + "network_interfaces_desc": "Nätverksgränssnitt att visa DLNA på. En tom lista gör att DLNA kör på alla gränssnitt. Kräver DLNA-omstart efter ändring.", + "recent_ip_addresses": "Senaste IP-adresser", + "server_display_name": "Servers Uppvisningsnamn", + "server_display_name_desc": "Uppvisningsnamnet för DLNA-servern. Återgår till standard {server_name} om tom.", + "until_restart": "tills omstart" + }, + "general": { + "auth": { + "api_key": "API-nyckel", + "api_key_desc": "API-nyckel för externa system. Krävs bara när användarnamn/lösenord är satt. Användarnamn måste vara sparat innan API-nyckeln genereras.", + "authentication": "Autentisering", + "clear_api_key": "Rensa API-nyckel", + "generate_api_key": "Generera API-nyckel", + "log_file": "Logg-fil", + "log_file_desc": "Sökväg till en fil att logga till. Tom för att inte logga till fil. Kräver omstart.", + "log_http": "Logga http-tillgång", + "log_http_desc": "Loggar http-tillgång till terminalen. Kräver omstart.", + "log_to_terminal": "Logga till terminal", + "log_to_terminal_desc": "Loggar till terminalen samt en fil. Alltid sant om filloggning är avstängt. Kräver omstart.", + "maximum_session_age": "Maximal inloggningstid", + "maximum_session_age_desc": "Maximal sovtid innan inloggingen bryts uttryckt i sekunder.", + "password": "Lösenord", + "password_desc": "Lösenord till Stash. Lämna tom för att avaktivera användarautentisering", + "stash-box_integration": "Stash-box integration", + "username": "Användernamn", + "username_desc": "Användarnamn till Stash. Lämna tom för att avaktivera användarautentisering" + }, + "cache_location": "Mappsökväg till Cache", + "cache_path_head": "Cache-sökväg", + "calculate_md5_and_ohash_desc": "Beräkna MD5 checksumma i tillägg med ohash. Aktivering kan sakta ner första skanningar. Hashen måste vara vald till ohash för att avaktivera MD5-beräkning.", + "calculate_md5_and_ohash_label": "Beräkna MD5 för videor", + "check_for_insecure_certificates": "Kolla efter osäkra certifikat", + "check_for_insecure_certificates_desc": "Vissa sajter använder osäkra SSl-certifikat. När detta är avstängt kommer skraparen att kunna skrapa sajter med osäkra certifikat. Stäng av detta om du får certifikatfel vid skrapning.", + "chrome_cdp_path": "Chrome CDP sökväg", + "chrome_cdp_path_desc": "Filsökväg till en Chrome exe, eller en fjärradress (börjar med http:// eller https://, till exempel http://localhost:9222/json/version) till en Chrome-instans.", + "create_galleries_from_folders_desc": "Om sant, skapar gallerier från mappar som innehåller bilder.", + "create_galleries_from_folders_label": "Skapa gallerier från mappar som innehåller bilder", + "db_path_head": "Databas filsökväg", + "directory_locations_to_your_content": "Sökväg till ditt innehåll", + "exclude_image": "Exkludera Bild", + "exclude_video": "Exkludera Video", + "excluded_image_gallery_patterns_desc": "Regexps av bilder och gallery filer/sökväg att exkludera från Skanna och lägga till på Rensa", + "excluded_image_gallery_patterns_head": "Mönster för Bild/Galleri exklusion", + "excluded_video_patterns_desc": "Regexps av video filer/sökväg att exkludera från Skanna och lägga till på Rensa", + "excluded_video_patterns_head": "Exluderade video-mönster", + "gallery_ext_desc": "Komma-avgränsad lista av filtillägg som kommer identifieras som galleri zip-filer.", + "gallery_ext_head": "Galleri zip-tillägg", + "generated_file_naming_hash_desc": "Använd MD5 eller ohash för att döpa genererade filer. Ett byte kräver att alla scener har ett värde för MD5/oshash. Efter ett byte måste existerande genererade filer migreras eller återgenereras. Se Job-sidan för migration.", + "generated_file_naming_hash_head": "Genererad fil namn-hash", + "generated_files_location": "Mappsökväg för genererade filer (markörer, förhandsvisningar, sprites, m.m.)", + "generated_path_head": "Genererad Filsökväg", + "hashing": "Hashande", + "image_ext_desc": "Komma-avgränsad lista av filtillägg som kommer identifieras som bilder.", + "image_ext_head": "Bild filtillägg", + "logging": "Loggande", + "maximum_streaming_transcode_size_desc": "Maximal storlek av transkodade strömmar", + "maximum_streaming_transcode_size_head": "Maximal transkodningsstorlek på strömmar", + "maximum_transcode_size_desc": "Maximal storlek för genererade trankodningar", + "maximum_transcode_size_head": "Maximal transkodningsstorlek", + "number_of_parallel_task_for_scan_generation_desc": "Ställ på 0 för auto. Varning-Att köra fler jobb än vad som krävs för att nå 100% CPU-användning kommer försämra prestanda och kan potentiellt skapa andra problem.", + "number_of_parallel_task_for_scan_generation_head": "Antal parallella jobb för skan/generering", + "parallel_scan_head": "Parallell Skan/Generering", + "preview_generation": "Generera förhandsvisningar", + "scraper_user_agent": "Användaragent för skrapning", + "scraper_user_agent_desc": "Användaragent-sträng som används under http-förfrågningar under skrapning", + "scraping": "Skrapning", + "sqlite_location": "Filsökväg för SQLite databasen (kräver omstart)", + "video_ext_desc": "Komma-avgränsad lista av filtillägg som kommer identifieras som videor.", + "video_ext_head": "Video filtillägg", + "video_head": "Video" + }, + "logs": { + "log_level": "Logg nivå" + }, + "plugins": { + "hooks": "Krokar", + "triggers_on": "Triggar på" + }, + "scraping": { + "entity_metadata": "{entityType} Metadata", + "entity_scrapers": "{entityType} skrapare", + "excluded_tag_patterns_desc": "Regexps av tagg-namn att exkludera från skrapresultat", + "excluded_tag_patterns_head": "Exkluderade tagg-mönster", + "scrapers": "Scrapers", + "search_by_name": "Sök med namn", + "supported_types": "Stöttade typer", + "supported_urls": "URL:er" + }, + "stashbox": { + "add_instance": "Lägg till stash-box instans", + "api_key": "API-nyckel", + "description": "Stash-box möjliggör automatiskt taggande baserat på fingeravtryck och filnamn.\nAdress och API-nyckel kan hittas på din kontosida på stash-box instansen. Namn är obligatoriskt när flera instanser läggs till.", + "endpoint": "Adress", + "graphql_endpoint": "GraphQL adress", + "name": "Namn", + "title": "Stash-box Adresser" + }, + "tasks": { + "added_job_to_queue": "Köade {operation_name}", + "auto_tag_based_on_filenames": "Auto-taga innehåll baserat på filnamn.", + "auto_tagging": "Auto-Taggar", + "backing_up_database": "Backar upp databas", + "backup_and_download": "Gör en backup av databasen och laddar ner den resulterande filen.", + "backup_database": "Skapar en backup av databasen i databasens mapp med filnamnsformatet {filename_format}", + "cleanup_desc": "Kollar efter saknade filer och raderar de från databasen. Detta är en destruktiv handling.", + "dont_include_file_extension_as_part_of_the_title": "Inkludera inte filtillägget som del av titeln", + "export_to_json": "Exporterar databasens innehåll i JSON format till metadatamappen.", + "generate_desc": "Generera stöttande bilder, sprites, videor, vtt och andra filer.", + "generate_phashes_during_scan": "Generera phash under skanning (för de-duplikation och scenidentifiering)", + "generate_previews_during_scan": "Generera bildförhandsvisning under skanning (animerade WebP förhandsvisningar, krävs bara om typ är Animerad Bild)", + "generate_sprites_during_scan": "Generera sprites under skanning (för scen-skrubbaren)", + "generate_video_previews_during_scan": "Generera förhandsvisningar under skanning (videoförhandsvisning som visas när man håller över en video)", + "generated_content": "Genererat Material", + "import_from_exported_json": "Importera från den exporterade JSON-filen i metadatamappen. Rensar den existerande databasen.", + "incremental_import": "Stegvis import från en exporterad zip-fil.", + "job_queue": "Jobbkö", + "maintenance": "Underhåll", + "migrate_hash_files": "Används efter att genererade filers namn-hash har ändrats för att döpa om existerande filer till ny namnhash.", + "migrations": "Migration", + "only_dry_run": "Kör bara en torr rensning. Radera ingenting", + "plugin_tasks": "Plugin Jobb", + "scan_for_content_desc": "Skannar för nytt innehåll och lägger till det i databasen.", + "set_name_date_details_from_metadata_if_present": "Sätt namn, datum, beskrivning från metadata (om det finns)" + }, + "tools": { + "scene_duplicate_checker": "Scen Duplikat Kontroll", + "scene_filename_parser": { + "add_field": "Lägg till fält", + "capitalize_title": "Lägg till versaler i titel", + "display_fields": "Visa fält", + "escape_chars": "Använd \\ för att undvika speciella karaktärer", + "filename": "Filnamn", + "filename_pattern": "Filnamnsmönster", + "ignore_organized": "Ignorera organiserade filer", + "ignored_words": "Ignorerade ord", + "matches_with": "Matchar med {i}", + "select_parser_recipe": "Välj Parser Recept", + "title": "Scen Filnamn Parser", + "whitespace_chars": "Blankstegskaraktärer", + "whitespace_chars_desc": "Dessa karaktärer kommer ersättas med blanksteg i titeln" + }, + "scene_tools": "Scenverktyg" + }, + "ui": { + "custom_css": { + "description": "Sidan måste laddas om för att ändringar ska ta effekt.", + "heading": "Egen CSS", + "option_label": "Egen CSS aktiverad" + }, + "handy_connection_key": { + "description": "Handy uppkopplingsnyckel för interaktiva scener.", + "heading": "Handy Connection Key" + }, + "funscript_offset": { + "description": "Tidsfördröjning i millisekunder för interaktiva skripter.", + "heading": "Funscript fördröjning (ms)" + }, + "language": { + "heading": "Språk" + }, + "max_loop_duration": { + "description": "Maximal scenspeltid där spelaren kommer loopa videon - 0 för att avaktivera", + "heading": "Maximal loopvaraktighet" + }, + "menu_items": { + "description": "Visa eller göm olika flikar på navigationsremsan", + "heading": "Menyobjekt" + }, + "performers": { + "options": { + "image_location": { + "heading": "Sökväg för egna stjärnbilder", + "description": "Sökväg för egna standardstjärnbilder. Lämna blank för att använda inbyggd standard" + } + } + }, + "preview_type": { + "description": "Konfiguration av väggobjekt", + "heading": "Förhandsvisningstyp", + "options": { + "animated": "Animerad bild", + "static": "Statisk bild", + "video": "Video" + } + }, + "scene_list": { + "heading": "Scenlista", + "options": { + "show_studio_as_text": "Visa studior som text" + } + }, + "scene_player": { + "heading": "Scenspelaren", + "options": { + "auto_start_video": "Auto-starta video" + } + }, + "scene_wall": { + "heading": "Scen / Markör Vägg", + "options": { + "display_title": "Visa titel och taggar", + "toggle_sound": "Aktivera ljud" + } + }, + "slideshow_delay": { + "description": "Bildspel är tillgängligt för gallerier i väggvisningsläge", + "heading": "Bildspelsfördröjning" + }, + "title": "Användargränssnitt" + } + }, + "configuration": "Konfiguration", + "countables": { + "galleries": "{count, plural, one {Galleri} other {Gallerier}}", + "images": "{count, plural, one {Bild} other {Bilder}}", + "markers": "{count, plural, one {Markör} other {Markörer}}", + "movies": "{count, plural, one {Film} other {Filmer}}", + "performers": "{count, plural, one {Stjärna} other {Stjärnor}}", + "scenes": "{count, plural, one {Scen} other {Scener}}", + "studios": "{count, plural, one {Studio} other {Studior}}", + "tags": "{count, plural, one {Tagg} other {Taggar}}" + }, + "country": "Land", + "cover_image": "Omslagsbild", + "created_at": "Skapad vid", + "criterion": { + "greater_than": "Större än", + "less_than": "Mindre än", + "value": "Värde" + }, + "criterion_modifier": { + "equals": "är", + "excludes": "exkluderar", + "format_string": "{criterion} {modifierString} {valueString}", + "greater_than": "är större än", + "includes": "inkluderar", + "includes_all": "inkluderar allt", + "is_null": "är null", + "less_than": "är mindre än", + "matches_regex": "matchar regex", + "not_equals": "är inte", + "not_matches_regex": "matchar ej regex", + "not_null": "är inte null", + "between": "mellan", + "not_between": "inte mellan" + }, + "custom": "Custom", + "date": "Datum", + "death_date": "Dödsdatum", + "death_year": "Dödsår", + "descending": "Nedåtstigande", + "detail": "Beskrivning", + "details": "Beskrivningar", + "developmentVersion": "Utvecklingsversion", + "dialogs": { + "delete_confirm": "Är du säker på att du vill radera {entityName}?", + "delete_entity_desc": "{count, plural, one {Är du säker att du vill radera detta {singularEntity}? Sålänge filen inte också raderas, kommer {singularEntity} att läggas till igen vid nästa skanning.} other {Är du säker att du vill radera dessa {pluralEntity}? Sålänge filen inte också raderas, kommer dessa {pluralEntity} att läggas till igen vid nästa skanning.}}", + "delete_entity_title": "{count, plural, one {Radera {singularEntity}} other {Radera {pluralEntity}}}", + "delete_object_desc": "Är du säker på att du vill radera {count, plural, one {denna {singularEntity}} other {dessa {pluralEntity}}}?", + "delete_object_overflow": "…och {count} other {count, plural, one {{singularEntity}} other {{pluralEntity}}}.", + "delete_object_title": "Radera {count, plural, one {{singularEntity}} other {{pluralEntity}}}", + "edit_entity_title": "Edit {count, plural, one {{singularEntity}} other {{pluralEntity}}}", + "export_include_related_objects": "Inkludera relaterade objekt i exporten", + "export_title": "Exportera", + "merge_tags": { + "destination": "Destination", + "source": "Källa" + }, + "overwrite_filter_confirm": "Är du säker på att du vill skriva över existerande sökning {entityName}?", + "scene_gen": { + "image_previews": "Bildförhandsvisning (animerade WebP förhandsvisningar, krävs enbart om typ är vald till Animerad Bild)", + "markers": "Markörer (20 sekunders videor som börjar vid given tidsstämpel)", + "overwrite": "Skrv över existerande genererade filer", + "phash": "Perceptuella hashar (för de-duplikation)", + "preview_exclude_end_time_desc": "Exkludera de sista x sekunderna från videoförhandsvisning. Detta kan vara ett värde i sekunder, eller en procent (ex. 2%) av scenes totala speltid.", + "preview_exclude_end_time_head": "Exludera sluttid", + "preview_exclude_start_time_desc": "Exkludera de första x sekunderna från videoförhandsvisning. Detta kan vara ett värde i sekunder, eller en procent (ex. 2%) av scenes totala speltid.", + "preview_exclude_start_time_head": "Exkludera starttid", + "preview_options": "Förhandsvisningsinställningar", + "preview_preset_desc": "Förinställningarna bestämmer storlek, kvalite och kodningstiden of förhandsvisningsgeneration. Förinställningar bortom “långsam” har minskande vinst och är inte rekommenderade.", + "preview_preset_head": "Förinställningar för förhandsvisningskodning", + "preview_seg_count_desc": "Antal segment i förhandsvisningsfiler.", + "preview_seg_count_head": "Antal segment i förhandsvisning", + "preview_seg_duration_desc": "Varaktighet av varje segment i sekunder.", + "preview_seg_duration_head": "Segmentvaraktighet", + "sprites": "Sprites (för scenskrubbaren)", + "transcodes": "Transkoder (MP4 konvertion från videoformat utan stöd)", + "video_previews": "Förhandsvisning (Videoförhandsvisning som visas när man svävar över en scen)" + }, + "scrape_entity_title": "{entity_type} Skrapade resultat", + "scrape_results_existing": "Existerande", + "scrape_results_scraped": "Skrapade", + "set_image_url_title": "URL till bild", + "unsaved_changes": "Osparade ändringar. Är du säker att du vill lämna?" + }, + "dimensions": "Mått", + "director": "Regissör", + "display_mode": { + "grid": "Rutnät", + "list": "Lista", + "tagger": "Taggaren", + "unknown": "Okänd", + "wall": "Vägg" + }, + "donate": "Donera", + "dupe_check": { + "description": "Nivåer under 'Exakt' kan ta längre tid att beräkninga. Falskt positiva svar riskeras också genom att välja en lägre nivå.", + "found_sets": "{setCount, plural, one{# kopia hittades.} other {# antal kopior hittades.}}", + "options": { + "exact": "Exakt", + "high": "Hög", + "low": "Låg", + "medium": "Medium" + }, + "search_accuracy_label": "Sökprecision", + "title": "Duplikata scener" + }, + "duration": "Varaktighet", + "effect_filters": { + "aspect": "Bildförhållande", + "blue": "Blå", + "blur": "Suddighet", + "brightness": "Ljusstyrka", + "contrast": "Konstrast", + "gamma": "Gamma", + "green": "Grön", + "hue": "Nyans", + "name": "Filter", + "name_transforms": "Förvandlingar", + "red": "Röd", + "reset_filters": "Återställ Filter", + "reset_transforms": "Återställ Förvandlingar", + "rotate": "Rotera", + "rotate_left_and_scale": "Rotera vänster och skala", + "rotate_right_and_scale": "Rotera höger och skala", + "saturation": "Mättnad", + "scale": "Skala", + "warmth": "Värme" + }, + "ethnicity": "Etnicitet", + "eye_color": "Ögonfärg", + "fake_tits": "Fejkbröst", + "favourite": "Favorit", + "file_info": "Info om Filen", + "file_mod_time": "Filens Modifikationstid", + "filesize": "Filstorlek", + "filter": "Filter", + "filter_name": "Filternamn", + "filters": "Filter", + "framerate": "Bildhastighet", + "frames_per_second": "{value} bilder i sekunden", + "galleries": "Gallerier", + "gallery": "Galleri", + "gallery_count": "Antal Gallerier", + "gender": "Kön", + "hair_color": "Hårfärg", + "hasMarkers": "Har markörer", + "height": "Längd", + "help": "Hjälp", + "image": "Bild", + "image_count": "Antal bilder", + "images": "Bilder", + "images-size": "Bildstorlek", + "include_child_studios": "Inkludera underordnade studior", + "instagram": "Instagram", + "interactive": "Interaktiv", + "isMissing": "Saknas", + "library": "Bibliotek", + "loading": { + "generic": "Laddar…" + }, + "marker_count": "Antal markörer", + "markers": "Markörer", + "measurements": "Mått", + "media_info": { + "audio_codec": "Ljudkodek", + "checksum": "Checksumma", + "downloaded_from": "Nedladdad från", + "hash": "Hash", + "performer_card": { + "age": "{age} {years_old}", + "age_context": "{age} {years_old} i den här scenen" + }, + "phash": "PHash", + "stream": "Ström", + "video_codec": "Videokodek" + }, + "megabits_per_second": "{value} megabit i sekunden", + "metadata": "Metadata", + "movie": "Film", + "movie_scene_number": "Filmscennummer", + "movies": "Filmer", + "name": "Namn", + "new": "Ny", + "none": "Ingen", + "o_counter": "O-Räknare", + "operations": "Operationer", + "organized": "Organiserad", + "pagination": { + "first": "Första", + "last": "Sista", + "next": "Nästa", + "previous": "Tidigare" + }, + "parent_studios": "Överordnad studio", + "path": "Sökväg", + "performer": "Stjärna", + "performer_count": "Antal stjärnor", + "performer_image": "Stjärnbild", + "performers": "Stjärnor", + "performerTags": "Stjärntagg", + "piercings": "Piercingar", + "queue": "Kö", + "random": "Slumpad", + "rating": "Betyg", + "resolution": "Upplösning", + "scene": "Scen", + "scene_count": "Antal scener", + "scene_id": "Scenens ID", + "scenes": "Scener", + "scenes-duration": "Total speltid", + "scenes-size": "Scenens storlek", + "scenes_updated_at": "Scen uppdaterad vid", + "sceneTagger": "Scentaggaren", + "sceneTags": "Scentaggar", + "search_filter": { + "add_filter": "Lägg till filter", + "name": "Filter", + "saved_filters": "Sparade filter", + "update_filter": "Uppdatera filter" + }, + "seconds": "Sekunder", + "settings": "Inställningar", + "stash_id": "Stash ID", + "status": "Status: {statusText}", + "studio": "Studio", + "studio_depth": "Nivåer (tom för allt)", + "studios": "Studior", + "synopsis": "Sammanfattning", + "tag": "Tagg", + "tag_count": "Antal taggar", + "tags": "Taggar", + "tattoos": "Tatueringar", + "title": "Titel", + "toast": { + "added_entity": "Lade till {entity}", + "added_generation_job_to_queue": "Köade genereringsjobb", + "create_entity": "Skapade {entity}", + "default_filter_set": "Standardfilter valt", + "delete_entity": "Radera {count, plural, one {{singularEntity}} other {{pluralEntity}}}", + "delete_past_tense": "Raderade {count, plural, one {{singularEntity}} other {{pluralEntity}}}", + "generating_screenshot": "Genererar skärmbild…", + "merged_tags": "Slog samman taggar", + "rescanning_entity": "Återskannar {count, plural, one {{singularEntity}} other {{pluralEntity}}}…", + "started_auto_tagging": "Började auto-tagga", + "saved_entity": "Sparade {entity}", + "updated_entity": "Uppdaterade {entity}" + }, + "total": "Total", + "twitter": "Twitter", + "up-dir": "Upp en mapp", + "updated_at": "Uppdaterad vid", + "url": "URL", + "weight": "Vikt", + "years_old": "År gammal" +} From d2a0a8fe4c743250156e5bc7f6fc2ab5eaf8ad72 Mon Sep 17 00:00:00 2001 From: SmallCoccinelle <89733524+SmallCoccinelle@users.noreply.github.com> Date: Wed, 8 Sep 2021 03:22:56 +0200 Subject: [PATCH 09/86] Correct error propagation (#1703) Rename an inner error to imageErr. This avoids a shadowing of an error in the following lines which aren't returned otherwise. --- pkg/manager/task_stash_box_tag.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/manager/task_stash_box_tag.go b/pkg/manager/task_stash_box_tag.go index 5bd5c2252..edbcd83e4 100644 --- a/pkg/manager/task_stash_box_tag.go +++ b/pkg/manager/task_stash_box_tag.go @@ -229,9 +229,9 @@ func (t *StashBoxPerformerTagTask) stashBoxPerformerTag() { } if len(performer.Images) > 0 { - image, err := utils.ReadImageFromURL(performer.Images[0]) - if err != nil { - return err + image, imageErr := utils.ReadImageFromURL(performer.Images[0]) + if imageErr != nil { + return imageErr } err = r.Performer().UpdateImage(createdPerformer.ID, image) } From e7f6cb22b70a349e7645893c1b8df94f68322e51 Mon Sep 17 00:00:00 2001 From: SmallCoccinelle <89733524+SmallCoccinelle@users.noreply.github.com> Date: Wed, 8 Sep 2021 03:23:10 +0200 Subject: [PATCH 10/86] Error strings noncapitalized (#1704) * Fix error string capitalization Error strings often follow another string. Hence, they should not be capitalized, unless referencing a name. * Uncapitalize more error strings While here, use %v on the error directly, which makes it easier to wrap the error later with %w if need be. * Uncapitalize more error strings While here, rename Url to URL as a nitpick. --- pkg/api/check_version.go | 7 +++++-- pkg/database/database.go | 6 +++--- pkg/ffmpeg/ffprobe.go | 2 +- pkg/manager/config/config.go | 6 +++++- pkg/manager/task_generate_screenshot.go | 6 +++--- pkg/plugin/raw.go | 6 +++--- pkg/scraper/url.go | 2 +- pkg/sqlite/repository.go | 4 ++-- pkg/sqlite/tag.go | 2 +- pkg/utils/file.go | 4 ++-- 10 files changed, 26 insertions(+), 19 deletions(-) diff --git a/pkg/api/check_version.go b/pkg/api/check_version.go index 659e01ec7..0044d79c4 100644 --- a/pkg/api/check_version.go +++ b/pkg/api/check_version.go @@ -117,10 +117,12 @@ func makeGithubRequest(url string, output interface{}) error { response, err := client.Do(req) if err != nil { + //lint:ignore ST1005 Github is a proper capitalized noun return fmt.Errorf("Github API request failed: %s", err) } if response.StatusCode != http.StatusOK { + //lint:ignore ST1005 Github is a proper capitalized noun return fmt.Errorf("Github API request failed: %s", response.Status) } @@ -128,12 +130,13 @@ func makeGithubRequest(url string, output interface{}) error { data, err := ioutil.ReadAll(response.Body) if err != nil { + //lint:ignore ST1005 Github is a proper capitalized noun return fmt.Errorf("Github API read response failed: %s", err) } err = json.Unmarshal(data, output) if err != nil { - return fmt.Errorf("Unmarshalling Github API response failed: %s", err) + return fmt.Errorf("unmarshalling Github API response failed: %s", err) } return nil @@ -196,7 +199,7 @@ func GetLatestVersion(shortHash bool) (latestVersion string, latestRelease strin } if latestVersion == "" { - return "", "", fmt.Errorf("No version found for \"%s\"", version) + return "", "", fmt.Errorf("no version found for \"%s\"", version) } return latestVersion, latestRelease, nil } diff --git a/pkg/database/database.go b/pkg/database/database.go index 50735b95d..cedcc9428 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -156,7 +156,7 @@ func Backup(db *sqlx.DB, backupPath string) error { logger.Infof("Backing up database into: %s", backupPath) _, err := db.Exec(`VACUUM INTO "` + backupPath + `"`) if err != nil { - return fmt.Errorf("Vacuum failed: %s", err) + return fmt.Errorf("vacuum failed: %s", err) } return nil @@ -272,7 +272,7 @@ func registerCustomDriver() { for name, fn := range funcs { if err := conn.RegisterFunc(name, fn, true); err != nil { - return fmt.Errorf("Error registering function %s: %s", name, err.Error()) + return fmt.Errorf("error registering function %s: %s", name, err.Error()) } } @@ -286,7 +286,7 @@ func registerCustomDriver() { }) if err != nil { - return fmt.Errorf("Error registering natural sort collation: %s", err.Error()) + return fmt.Errorf("error registering natural sort collation: %s", err.Error()) } return nil diff --git a/pkg/ffmpeg/ffprobe.go b/pkg/ffmpeg/ffprobe.go index 2dc1e2ad2..cb526fcdf 100644 --- a/pkg/ffmpeg/ffprobe.go +++ b/pkg/ffmpeg/ffprobe.go @@ -236,7 +236,7 @@ func NewVideoFile(ffprobePath string, videoPath string, stripExt bool) (*VideoFi probeJSON := &FFProbeJSON{} if err := json.Unmarshal(out, probeJSON); err != nil { - return nil, fmt.Errorf("Error unmarshalling video data for <%s>: %s", videoPath, err.Error()) + return nil, fmt.Errorf("error unmarshalling video data for <%s>: %s", videoPath, err.Error()) } return parse(videoPath, probeJSON, stripExt) diff --git a/pkg/manager/config/config.go b/pkg/manager/config/config.go index c412880ca..21d5e5c58 100644 --- a/pkg/manager/config/config.go +++ b/pkg/manager/config/config.go @@ -656,17 +656,21 @@ func (i *Instance) ValidateStashBoxes(boxes []*models.StashBoxInput) error { re, err := regexp.Compile("^http.*graphql$") if err != nil { - return errors.New("Failure to generate regular expression") + return errors.New("failure to generate regular expression") } for _, box := range boxes { if box.APIKey == "" { + //lint:ignore ST1005 Stash-box is a name return errors.New("Stash-box API Key cannot be blank") } else if box.Endpoint == "" { + //lint:ignore ST1005 Stash-box is a name return errors.New("Stash-box Endpoint cannot be blank") } else if !re.Match([]byte(box.Endpoint)) { + //lint:ignore ST1005 Stash-box is a name return errors.New("Stash-box Endpoint is invalid") } else if isMulti && box.Name == "" { + //lint:ignore ST1005 Stash-box is a name return errors.New("Stash-box Name cannot be blank") } } diff --git a/pkg/manager/task_generate_screenshot.go b/pkg/manager/task_generate_screenshot.go index 21a340a92..3be100c86 100644 --- a/pkg/manager/task_generate_screenshot.go +++ b/pkg/manager/task_generate_screenshot.go @@ -70,18 +70,18 @@ func (t *GenerateScreenshotTask) Start(wg *sync.WaitGroup) { } if err := SetSceneScreenshot(checksum, coverImageData); err != nil { - return fmt.Errorf("Error writing screenshot: %s", err.Error()) + return fmt.Errorf("error writing screenshot: %v", err) } // update the scene cover table if err := qb.UpdateCover(t.Scene.ID, coverImageData); err != nil { - return fmt.Errorf("Error setting screenshot: %s", err.Error()) + return fmt.Errorf("error setting screenshot: %v", err) } // update the scene with the update date _, err = qb.Update(updatedScene) if err != nil { - return fmt.Errorf("Error updating scene: %s", err.Error()) + return fmt.Errorf("error updating scene: %v", err) } return nil diff --git a/pkg/plugin/raw.go b/pkg/plugin/raw.go index fe44c2f6d..3ebe29600 100644 --- a/pkg/plugin/raw.go +++ b/pkg/plugin/raw.go @@ -56,18 +56,18 @@ func (t *rawPluginTask) Start() error { stderr, err := cmd.StderrPipe() if err != nil { - logger.Error("Plugin stderr not available: " + err.Error()) + logger.Error("plugin stderr not available: " + err.Error()) } stdout, err := cmd.StdoutPipe() if nil != err { - logger.Error("Plugin stdout not available: " + err.Error()) + logger.Error("plugin stdout not available: " + err.Error()) } t.waitGroup.Add(1) t.done = make(chan bool, 1) if err = cmd.Start(); err != nil { - return fmt.Errorf("Error running plugin: %s", err.Error()) + return fmt.Errorf("error running plugin: %s", err.Error()) } go t.handlePluginStderr(stderr) diff --git a/pkg/scraper/url.go b/pkg/scraper/url.go index 4fc78caf9..e2af1cb97 100644 --- a/pkg/scraper/url.go +++ b/pkg/scraper/url.go @@ -109,7 +109,7 @@ func loadURL(url string, scraperConfig config, globalConfig GlobalConfig) (io.Re func urlFromCDP(url string, driverOptions scraperDriverOptions, globalConfig GlobalConfig) (io.Reader, error) { if !driverOptions.UseCDP { - return nil, fmt.Errorf("Url shouldn't be feetched through CDP") + return nil, fmt.Errorf("URL shouldn't be fetched through CDP") } sleepDuration := scrapeDefaultSleep diff --git a/pkg/sqlite/repository.go b/pkg/sqlite/repository.go index 182d0223f..6550d0d67 100644 --- a/pkg/sqlite/repository.go +++ b/pkg/sqlite/repository.go @@ -269,10 +269,10 @@ func (r *repository) executeFindQuery(body string, args []interface{}, sortAndPa idsResult, idsErr = r.runIdsQuery(idsQuery, args) if countErr != nil { - return nil, 0, fmt.Errorf("Error executing count query with SQL: %s, args: %v, error: %s", countQuery, args, countErr.Error()) + return nil, 0, fmt.Errorf("error executing count query with SQL: %s, args: %v, error: %s", countQuery, args, countErr.Error()) } if idsErr != nil { - return nil, 0, fmt.Errorf("Error executing find query with SQL: %s, args: %v, error: %s", idsQuery, args, idsErr.Error()) + return nil, 0, fmt.Errorf("error executing find query with SQL: %s, args: %v, error: %s", idsQuery, args, idsErr.Error()) } return idsResult, countResult, nil diff --git a/pkg/sqlite/tag.go b/pkg/sqlite/tag.go index ca97e5460..c67a8dea5 100644 --- a/pkg/sqlite/tag.go +++ b/pkg/sqlite/tag.go @@ -78,7 +78,7 @@ func (qb *tagQueryBuilder) Destroy(id int) error { } if primaryMarkers > 0 { - return errors.New("Cannot delete tag used as a primary tag in scene markers") + return errors.New("cannot delete tag used as a primary tag in scene markers") } return qb.destroyExisting([]int{id}) diff --git a/pkg/utils/file.go b/pkg/utils/file.go index 87f7538b3..7fe3170ff 100644 --- a/pkg/utils/file.go +++ b/pkg/utils/file.go @@ -193,12 +193,12 @@ func IsZipFileUncompressed(path string) (bool, error) { func WriteFile(path string, file []byte) error { pathErr := EnsureDirAll(filepath.Dir(path)) if pathErr != nil { - return fmt.Errorf("Cannot ensure path %s", pathErr) + return fmt.Errorf("cannot ensure path %s", pathErr) } err := ioutil.WriteFile(path, file, 0755) if err != nil { - return fmt.Errorf("Write error for thumbnail %s: %s ", path, err) + return fmt.Errorf("write error for thumbnail %s: %s ", path, err) } return nil } From 4545da9af0f9dfae498f4690147d3027aed81eb5 Mon Sep 17 00:00:00 2001 From: SmallCoccinelle <89733524+SmallCoccinelle@users.noreply.github.com> Date: Wed, 8 Sep 2021 03:23:21 +0200 Subject: [PATCH 11/86] Avoid redundant break statements (#1705) In Go, a switch-case automatically breaks on end. This makes the break statement redundant. --- pkg/sqlite/sql.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/pkg/sqlite/sql.go b/pkg/sqlite/sql.go index b3bcdd514..7c9477bdc 100644 --- a/pkg/sqlite/sql.go +++ b/pkg/sqlite/sql.go @@ -209,20 +209,16 @@ func getIntCriterionWhereClause(column string, input models.IntCriterionInput) ( switch input.Modifier { case "EQUALS", "NOT_EQUALS": args = []interface{}{input.Value} - break case "LESS_THAN": args = []interface{}{input.Value} - break case "GREATER_THAN": args = []interface{}{input.Value} - break case "BETWEEN", "NOT_BETWEEN": upper := 0 if input.Value2 != nil { upper = *input.Value2 } args = []interface{}{input.Value, upper} - break } return column + " " + binding, args From 265d5f4c7043eebd75f233c6e02dca781f589606 Mon Sep 17 00:00:00 2001 From: kermieisinthehouse Date: Wed, 8 Sep 2021 05:30:15 +0000 Subject: [PATCH 12/86] Apple Silicon Support, Bump Go to 1.17, refactor docker/build/x86_64/Dockerfile (#1646) * Bump Go to 1.17, refactor build/x86_64 Dockerfile to make better use of multi-stage * Bump to 1.17 from 1.16 * Bump packr version, provide needed legacy env var * Add apple silicon support, fix macos build chain * Update unused travis ci --- .github/workflows/build.yml | 7 +- .goreleaser.yml | 12 + .travis.yml.disabled | 9 +- Makefile | 51 ++- README.md | 4 +- docker/build/x86_64/Dockerfile | 78 ++-- docker/ci/x86_64/Dockerfile | 2 +- docker/compiler/Dockerfile | 35 +- docker/compiler/Makefile | 2 +- docker/compiler/README.md | 4 +- docker/compiler/create_osx_sdk.sh | 11 - docker/develop/x86_64/Dockerfile | 27 -- docker/production/README.md | 50 +-- docker/production/docker-compose.yml | 2 +- docker/production/x86_64/Dockerfile | 2 +- go.mod | 51 ++- pkg/api/check_version.go | 1 + pkg/autotag/integration_test.go | 1 + pkg/plugin/examples/common/graphql.go | 1 + pkg/plugin/examples/goraw/main.go | 1 + pkg/plugin/examples/gorpc/main.go | 1 + pkg/sqlite/gallery_test.go | 1 + pkg/sqlite/image_test.go | 1 + pkg/sqlite/movies_test.go | 1 + pkg/sqlite/performer_test.go | 1 + pkg/sqlite/saved_filter_test.go | 1 + pkg/sqlite/scene_marker_test.go | 1 + pkg/sqlite/scene_test.go | 1 + pkg/sqlite/setup_test.go | 1 + pkg/sqlite/stash_id_test.go | 1 + pkg/sqlite/studio_test.go | 1 + pkg/sqlite/tag_test.go | 1 + scripts/cross-compile.sh | 9 +- tools.go | 1 + vendor/github.com/99designs/gqlgen/go.mod | 33 -- vendor/github.com/99designs/gqlgen/go.sum | 114 ----- vendor/github.com/Yamashou/gqlgenc/go.mod | 14 - vendor/github.com/Yamashou/gqlgenc/go.sum | 122 ------ vendor/github.com/agnivade/levenshtein/go.mod | 8 - vendor/github.com/agnivade/levenshtein/go.sum | 4 - vendor/github.com/antchfx/htmlquery/go.mod | 9 - vendor/github.com/antchfx/htmlquery/go.sum | 11 - vendor/github.com/chromedp/cdproto/go.mod | 8 - vendor/github.com/chromedp/cdproto/go.sum | 6 - vendor/github.com/chromedp/chromedp/go.mod | 10 - vendor/github.com/chromedp/chromedp/go.sum | 17 - vendor/github.com/chromedp/sysutil/go.mod | 3 - vendor/github.com/corona10/goimagehash/go.mod | 3 - vendor/github.com/corona10/goimagehash/go.sum | 2 - .../github.com/disintegration/imaging/go.mod | 3 - .../github.com/disintegration/imaging/go.sum | 2 - vendor/github.com/fvbommel/sortorder/go.mod | 3 - vendor/github.com/fvbommel/sortorder/go.sum | 0 vendor/github.com/gobuffalo/logger/go.mod | 8 - vendor/github.com/gobuffalo/logger/go.sum | 17 - vendor/github.com/gobuffalo/packd/go.mod | 5 - vendor/github.com/gobuffalo/packd/go.sum | 11 - vendor/github.com/gobuffalo/packr/v2/go.mod | 19 - vendor/github.com/gobuffalo/packr/v2/go.sum | 180 -------- vendor/github.com/gobwas/httphead/go.mod | 3 - vendor/github.com/gobwas/ws/go.mod | 9 - vendor/github.com/gobwas/ws/go.sum | 6 - .../golang-migrate/migrate/v4/go.mod | 54 --- .../golang-migrate/migrate/v4/go.sum | 300 -------------- vendor/github.com/gorilla/sessions/go.mod | 3 - vendor/github.com/gorilla/sessions/go.sum | 2 - vendor/github.com/gorilla/websocket/go.mod | 3 - vendor/github.com/gorilla/websocket/go.sum | 0 vendor/github.com/h2non/filetype/go.mod | 1 - vendor/github.com/hashicorp/errwrap/go.mod | 1 - .../github.com/hashicorp/go-multierror/go.mod | 3 - .../github.com/hashicorp/go-multierror/go.sum | 4 - vendor/github.com/hashicorp/golang-lru/go.mod | 1 - vendor/github.com/hashicorp/hcl/go.mod | 3 - vendor/github.com/hashicorp/hcl/go.sum | 2 - vendor/github.com/jmoiron/sqlx/go.mod | 7 - vendor/github.com/jmoiron/sqlx/go.sum | 6 - vendor/github.com/josharian/intern/go.mod | 3 - vendor/github.com/json-iterator/go/go.mod | 11 - vendor/github.com/json-iterator/go/go.sum | 14 - vendor/github.com/karrick/godirwalk/go.mod | 3 - vendor/github.com/karrick/godirwalk/go.sum | 0 .../github.com/magiconair/properties/go.mod | 1 - vendor/github.com/mailru/easyjson/go.mod | 5 - vendor/github.com/mailru/easyjson/go.sum | 2 - vendor/github.com/markbates/errx/go.mod | 3 - vendor/github.com/markbates/errx/go.sum | 0 vendor/github.com/markbates/oncer/go.mod | 8 - vendor/github.com/markbates/oncer/go.sum | 9 - vendor/github.com/markbates/safe/go.mod | 7 - vendor/github.com/markbates/safe/go.sum | 6 - vendor/github.com/mattn/go-sqlite3/go.mod | 3 - vendor/github.com/mattn/go-sqlite3/go.sum | 0 vendor/github.com/mitchellh/go-homedir/go.mod | 1 - .../github.com/mitchellh/mapstructure/go.mod | 1 - vendor/github.com/rs/cors/go.mod | 1 - vendor/github.com/rs/zerolog/go.mod | 9 - vendor/github.com/rs/zerolog/go.sum | 16 - .../github.com/russross/blackfriday/v2/go.mod | 1 - .../shurcooL/sanitized_anchor_name/go.mod | 1 - vendor/github.com/sirupsen/logrus/go.mod | 10 - vendor/github.com/sirupsen/logrus/go.sum | 8 - vendor/github.com/spf13/afero/go.mod | 3 - vendor/github.com/spf13/afero/go.sum | 2 - vendor/github.com/spf13/cast/go.mod | 7 - vendor/github.com/spf13/cast/go.sum | 6 - vendor/github.com/spf13/cobra/go.mod | 12 - vendor/github.com/spf13/cobra/go.sum | 149 ------- .../github.com/spf13/jwalterweatherman/go.mod | 1 - vendor/github.com/spf13/viper/go.mod | 40 -- vendor/github.com/spf13/viper/go.sum | 388 ------------------ vendor/github.com/tidwall/gjson/go.mod | 8 - vendor/github.com/tidwall/gjson/go.sum | 4 - vendor/github.com/tidwall/match/go.mod | 3 - vendor/github.com/tidwall/pretty/go.mod | 3 - vendor/github.com/urfave/cli/v2/go.mod | 9 - vendor/github.com/urfave/cli/v2/go.sum | 14 - vendor/github.com/vektah/dataloaden/go.mod | 9 - vendor/github.com/vektah/dataloaden/go.sum | 15 - vendor/github.com/vektah/gqlparser/v2/go.mod | 12 - vendor/github.com/vektah/gqlparser/v2/go.sum | 30 -- vendor/github.com/vektra/mockery/v2/go.mod | 15 - vendor/github.com/vektra/mockery/v2/go.sum | 343 ---------------- vendor/golang.org/x/term/go.mod | 5 - vendor/golang.org/x/term/go.sum | 2 - vendor/golang.org/x/xerrors/go.mod | 3 - vendor/gopkg.in/yaml.v2/go.mod | 5 - vendor/modules.txt | 92 +++++ 128 files changed, 303 insertions(+), 2393 deletions(-) delete mode 100644 docker/compiler/create_osx_sdk.sh delete mode 100644 docker/develop/x86_64/Dockerfile delete mode 100644 vendor/github.com/99designs/gqlgen/go.mod delete mode 100644 vendor/github.com/99designs/gqlgen/go.sum delete mode 100644 vendor/github.com/Yamashou/gqlgenc/go.mod delete mode 100644 vendor/github.com/Yamashou/gqlgenc/go.sum delete mode 100644 vendor/github.com/agnivade/levenshtein/go.mod delete mode 100644 vendor/github.com/agnivade/levenshtein/go.sum delete mode 100644 vendor/github.com/antchfx/htmlquery/go.mod delete mode 100644 vendor/github.com/antchfx/htmlquery/go.sum delete mode 100644 vendor/github.com/chromedp/cdproto/go.mod delete mode 100644 vendor/github.com/chromedp/cdproto/go.sum delete mode 100644 vendor/github.com/chromedp/chromedp/go.mod delete mode 100644 vendor/github.com/chromedp/chromedp/go.sum delete mode 100644 vendor/github.com/chromedp/sysutil/go.mod delete mode 100644 vendor/github.com/corona10/goimagehash/go.mod delete mode 100644 vendor/github.com/corona10/goimagehash/go.sum delete mode 100644 vendor/github.com/disintegration/imaging/go.mod delete mode 100644 vendor/github.com/disintegration/imaging/go.sum delete mode 100644 vendor/github.com/fvbommel/sortorder/go.mod delete mode 100644 vendor/github.com/fvbommel/sortorder/go.sum delete mode 100644 vendor/github.com/gobuffalo/logger/go.mod delete mode 100644 vendor/github.com/gobuffalo/logger/go.sum delete mode 100644 vendor/github.com/gobuffalo/packd/go.mod delete mode 100644 vendor/github.com/gobuffalo/packd/go.sum delete mode 100644 vendor/github.com/gobuffalo/packr/v2/go.mod delete mode 100644 vendor/github.com/gobuffalo/packr/v2/go.sum delete mode 100644 vendor/github.com/gobwas/httphead/go.mod delete mode 100644 vendor/github.com/gobwas/ws/go.mod delete mode 100644 vendor/github.com/gobwas/ws/go.sum delete mode 100644 vendor/github.com/golang-migrate/migrate/v4/go.mod delete mode 100644 vendor/github.com/golang-migrate/migrate/v4/go.sum delete mode 100644 vendor/github.com/gorilla/sessions/go.mod delete mode 100644 vendor/github.com/gorilla/sessions/go.sum delete mode 100644 vendor/github.com/gorilla/websocket/go.mod delete mode 100644 vendor/github.com/gorilla/websocket/go.sum delete mode 100644 vendor/github.com/h2non/filetype/go.mod delete mode 100644 vendor/github.com/hashicorp/errwrap/go.mod delete mode 100644 vendor/github.com/hashicorp/go-multierror/go.mod delete mode 100644 vendor/github.com/hashicorp/go-multierror/go.sum delete mode 100644 vendor/github.com/hashicorp/golang-lru/go.mod delete mode 100644 vendor/github.com/hashicorp/hcl/go.mod delete mode 100644 vendor/github.com/hashicorp/hcl/go.sum delete mode 100644 vendor/github.com/jmoiron/sqlx/go.mod delete mode 100644 vendor/github.com/jmoiron/sqlx/go.sum delete mode 100644 vendor/github.com/josharian/intern/go.mod delete mode 100644 vendor/github.com/json-iterator/go/go.mod delete mode 100644 vendor/github.com/json-iterator/go/go.sum delete mode 100644 vendor/github.com/karrick/godirwalk/go.mod delete mode 100644 vendor/github.com/karrick/godirwalk/go.sum delete mode 100644 vendor/github.com/magiconair/properties/go.mod delete mode 100644 vendor/github.com/mailru/easyjson/go.mod delete mode 100644 vendor/github.com/mailru/easyjson/go.sum delete mode 100644 vendor/github.com/markbates/errx/go.mod delete mode 100644 vendor/github.com/markbates/errx/go.sum delete mode 100644 vendor/github.com/markbates/oncer/go.mod delete mode 100644 vendor/github.com/markbates/oncer/go.sum delete mode 100644 vendor/github.com/markbates/safe/go.mod delete mode 100644 vendor/github.com/markbates/safe/go.sum delete mode 100644 vendor/github.com/mattn/go-sqlite3/go.mod delete mode 100644 vendor/github.com/mattn/go-sqlite3/go.sum delete mode 100644 vendor/github.com/mitchellh/go-homedir/go.mod delete mode 100644 vendor/github.com/mitchellh/mapstructure/go.mod delete mode 100644 vendor/github.com/rs/cors/go.mod delete mode 100644 vendor/github.com/rs/zerolog/go.mod delete mode 100644 vendor/github.com/rs/zerolog/go.sum delete mode 100644 vendor/github.com/russross/blackfriday/v2/go.mod delete mode 100644 vendor/github.com/shurcooL/sanitized_anchor_name/go.mod delete mode 100644 vendor/github.com/sirupsen/logrus/go.mod delete mode 100644 vendor/github.com/sirupsen/logrus/go.sum delete mode 100644 vendor/github.com/spf13/afero/go.mod delete mode 100644 vendor/github.com/spf13/afero/go.sum delete mode 100644 vendor/github.com/spf13/cast/go.mod delete mode 100644 vendor/github.com/spf13/cast/go.sum delete mode 100644 vendor/github.com/spf13/cobra/go.mod delete mode 100644 vendor/github.com/spf13/cobra/go.sum delete mode 100644 vendor/github.com/spf13/jwalterweatherman/go.mod delete mode 100644 vendor/github.com/spf13/viper/go.mod delete mode 100644 vendor/github.com/spf13/viper/go.sum delete mode 100644 vendor/github.com/tidwall/gjson/go.mod delete mode 100644 vendor/github.com/tidwall/gjson/go.sum delete mode 100644 vendor/github.com/tidwall/match/go.mod delete mode 100644 vendor/github.com/tidwall/pretty/go.mod delete mode 100644 vendor/github.com/urfave/cli/v2/go.mod delete mode 100644 vendor/github.com/urfave/cli/v2/go.sum delete mode 100644 vendor/github.com/vektah/dataloaden/go.mod delete mode 100644 vendor/github.com/vektah/dataloaden/go.sum delete mode 100644 vendor/github.com/vektah/gqlparser/v2/go.mod delete mode 100644 vendor/github.com/vektah/gqlparser/v2/go.sum delete mode 100644 vendor/github.com/vektra/mockery/v2/go.mod delete mode 100644 vendor/github.com/vektra/mockery/v2/go.sum delete mode 100644 vendor/golang.org/x/term/go.mod delete mode 100644 vendor/golang.org/x/term/go.sum delete mode 100644 vendor/golang.org/x/xerrors/go.mod delete mode 100644 vendor/gopkg.in/yaml.v2/go.mod diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e963bd17e..fe0ba72bb 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -9,7 +9,7 @@ on: types: [ published ] env: - COMPILER_IMAGE: stashapp/compiler:4 + COMPILER_IMAGE: stashapp/compiler:5 jobs: build: @@ -79,7 +79,8 @@ jobs: run: | docker exec -t build /bin/bash -c "make packr" docker exec -t build /bin/bash -c "make cross-compile-windows" - docker exec -t build /bin/bash -c "make cross-compile-osx" + docker exec -t build /bin/bash -c "make cross-compile-osx-intel" + docker exec -t build /bin/bash -c "make cross-compile-osx-applesilicon" docker exec -t build /bin/bash -c "make cross-compile-linux" docker exec -t build /bin/bash -c "make cross-compile-linux-arm64v8" docker exec -t build /bin/bash -c "make cross-compile-linux-arm32v7" @@ -133,6 +134,7 @@ jobs: title: "${{ env.STASH_VERSION }}: Latest development build" files: | dist/stash-osx + dist/stash-osx-applesilicon dist/stash-win.exe dist/stash-linux dist/stash-linux-arm64v8 @@ -148,6 +150,7 @@ jobs: allow_override: true files: | dist/stash-osx + dist/stash-osx-applesilicon dist/stash-win.exe dist/stash-linux dist/stash-linux-arm64v8 diff --git a/.goreleaser.yml b/.goreleaser.yml index 238fea065..7c10ffeb8 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -29,6 +29,18 @@ builds: - darwin goarch: - amd64 + - binary: stash-osx-applesilicon + env: + - CGO_ENABLED=1 + - CC=oa64-clang + - CXX=oa64-clang++ + flags: + - -tags + - extended + goos: + - darwin + goarch: + - arm64 - binary: stash-linux env: - CGO_ENABLED=1 diff --git a/.travis.yml.disabled b/.travis.yml.disabled index 3641d0dd9..91a12196f 100644 --- a/.travis.yml.disabled +++ b/.travis.yml.disabled @@ -5,12 +5,9 @@ git: depth: false language: go go: -- 1.13.x +- 1.17.x services: - docker -env: - global: - - GO111MODULE=on before_install: - set -e # Configure environment so changes are picked up when the Docker daemon is restarted after upgrading @@ -41,7 +38,7 @@ script: #- make lint - make fmt-check vet it after_success: -- docker pull stashapp/compiler:4 +- docker pull stashapp/compiler:5 - sh ./scripts/cross-compile.sh - git describe --tags --exclude latest_develop | tee CHECKSUMS_SHA1 - sha1sum dist/stash-* | sed 's/dist\///g' | tee -a CHECKSUMS_SHA1 @@ -62,6 +59,7 @@ deploy: secure: tGJ2q62CfPdayid2qEtW2aGRhMgCl3lBXYYQqp3eH0vFgIIf6cs7IDX7YC/x3XKMEQ/iMLZmtCXZvSTqNrD6Sk7MSnt30GIs+4uxIZDnnd8mV5X3K4n4gjD+NAORc4DrQBvUGrYMKJsR5gtkH0nu6diWb1o1If7OiJEuCPRhrmQYcza7NUdABnA9Z2wn2RNUV9Ga33WUCqLMEU5GtNBlfQPiP/khCQrqn/ocR6wUjYut3J6YagzqH4wsfJi3glHyWtowcNIw1LZi5zFxHD/bRBT4Tln7yypkjWNq9eQILA6i6kRUGf7ggyTx26/k8n4tnu+QD0vVh4EcjlThpU/LGyUXzKrrxjRwaDZnM0oYxg5AfHcBuAiAdo0eWnV3lEWRfTJMIVb9MPf4qDmzR4RREfB5OXOxwq3ODeCcJE8sTIMD/wBPZrlqS/QrRpND2gn2X4snkVukN9t9F4CMTFMtVSzFV7TDJW5E5Lq6VEExulteQhs6kcK9NRPNAaLgRQAw7X9kVWfDtiGUP+fE2i8F9Bo8bm7sOT5O5VPMPykx3EgeNg1IqIgMTCsMlhMJT4xBJoQUgmd2wWyf3Ryw+P+sFgdb5Sd7+lFgJBjMUUoOxMxAOiEgdFvCXcr+/Udyz2RdtetU1/6VzXzLPcKOw0wubZeBkISqu7o9gpfdMP9Eq00= file: - dist/stash-osx + - dist/stash-osx-applesilicon - dist/stash-win.exe - dist/stash-linux - dist/stash-linux-arm64v8 @@ -89,6 +87,7 @@ deploy: secure: tGJ2q62CfPdayid2qEtW2aGRhMgCl3lBXYYQqp3eH0vFgIIf6cs7IDX7YC/x3XKMEQ/iMLZmtCXZvSTqNrD6Sk7MSnt30GIs+4uxIZDnnd8mV5X3K4n4gjD+NAORc4DrQBvUGrYMKJsR5gtkH0nu6diWb1o1If7OiJEuCPRhrmQYcza7NUdABnA9Z2wn2RNUV9Ga33WUCqLMEU5GtNBlfQPiP/khCQrqn/ocR6wUjYut3J6YagzqH4wsfJi3glHyWtowcNIw1LZi5zFxHD/bRBT4Tln7yypkjWNq9eQILA6i6kRUGf7ggyTx26/k8n4tnu+QD0vVh4EcjlThpU/LGyUXzKrrxjRwaDZnM0oYxg5AfHcBuAiAdo0eWnV3lEWRfTJMIVb9MPf4qDmzR4RREfB5OXOxwq3ODeCcJE8sTIMD/wBPZrlqS/QrRpND2gn2X4snkVukN9t9F4CMTFMtVSzFV7TDJW5E5Lq6VEExulteQhs6kcK9NRPNAaLgRQAw7X9kVWfDtiGUP+fE2i8F9Bo8bm7sOT5O5VPMPykx3EgeNg1IqIgMTCsMlhMJT4xBJoQUgmd2wWyf3Ryw+P+sFgdb5Sd7+lFgJBjMUUoOxMxAOiEgdFvCXcr+/Udyz2RdtetU1/6VzXzLPcKOw0wubZeBkISqu7o9gpfdMP9Eq00= file: - dist/stash-osx + - dist/stash-osx-applesilicon - dist/stash-win.exe - dist/stash-linux - dist/stash-linux-arm64v8 diff --git a/Makefile b/Makefile index fae4673b7..c530747bc 100644 --- a/Makefile +++ b/Makefile @@ -44,14 +44,15 @@ endif build: pre-build $(eval LDFLAGS := $(LDFLAGS) -X 'github.com/stashapp/stash/pkg/api.version=$(STASH_VERSION)' -X 'github.com/stashapp/stash/pkg/api.buildstamp=$(BUILD_DATE)' -X 'github.com/stashapp/stash/pkg/api.githash=$(GITHASH)') - go build $(OUTPUT) -mod=vendor -v -tags "sqlite_omit_load_extension osusergo netgo" -ldflags "$(LDFLAGS) $(EXTRA_LDFLAGS)" + go build $(OUTPUT) -mod=vendor -v -tags "sqlite_omit_load_extension osusergo netgo" $(GO_BUILD_FLAGS) -ldflags "$(LDFLAGS) $(EXTRA_LDFLAGS)" # strips debug symbols from the release build -# consider -trimpath in go build if we move to go 1.13+ build-release: EXTRA_LDFLAGS := -s -w +build-release: GO_BUILD_FLAGS := -trimpath build-release: build build-release-static: EXTRA_LDFLAGS := -extldflags=-static -s -w +build-release-static: GO_BUILD_FLAGS := -trimpath build-release-static: build # cross-compile- targets should be run within the compiler docker container @@ -62,13 +63,21 @@ cross-compile-windows: export CXX := x86_64-w64-mingw32-g++ cross-compile-windows: OUTPUT := -o dist/stash-win.exe cross-compile-windows: build-release-static -cross-compile-osx: export GOOS := darwin -cross-compile-osx: export GOARCH := amd64 -cross-compile-osx: export CC := o64-clang -cross-compile-osx: export CXX := o64-clang++ -cross-compile-osx: OUTPUT := -o dist/stash-osx +cross-compile-osx-intel: export GOOS := darwin +cross-compile-osx-intel: export GOARCH := amd64 +cross-compile-osx-intel: export CC := o64-clang +cross-compile-osx-intel: export CXX := o64-clang++ +cross-compile-osx-intel: OUTPUT := -o dist/stash-osx # can't use static build for OSX -cross-compile-osx: build-release +cross-compile-osx-intel: build-release + +cross-compile-osx-applesilicon: export GOOS := darwin +cross-compile-osx-applesilicon: export GOARCH := arm64 +cross-compile-osx-applesilicon: export CC := oa64e-clang +cross-compile-osx-applesilicon: export CXX := oa64e-clang++ +cross-compile-osx-applesilicon: OUTPUT := -o dist/stash-osx-applesilicon +# can't use static build for OSX +cross-compile-osx-applesilicon: build-release cross-compile-linux: export GOOS := linux cross-compile-linux: export GOARCH := amd64 @@ -95,7 +104,14 @@ cross-compile-pi: export CC := arm-linux-gnueabi-gcc cross-compile-pi: OUTPUT := -o dist/stash-pi cross-compile-pi: build-release-static -cross-compile-all: cross-compile-windows cross-compile-osx cross-compile-linux cross-compile-linux-arm64v8 cross-compile-linux-arm32v7 cross-compile-pi +cross-compile-all: + make cross-compile-windows + make cross-compile-osx-intel + make cross-compile-osx-applesilicon + make cross-compile-linux + make cross-compile-linux-arm64v8 + make cross-compile-linux-arm32v7 + make cross-compile-pi install: packr2 install @@ -104,11 +120,16 @@ clean: packr2 clean # Regenerates GraphQL files -.PHONY: generate -generate: - go generate -mod=vendor +generate: generate-backend generate-frontend + +.PHONY: generate-frontend +generate-frontend: cd ui/v2.5 && yarn run gqlgen +.PHONY: generate-backend +generate-backend: + go generate -mod=vendor + # Regenerates stash-box client files .PHONY: generate-stash-box-client generate-stash-box-client: @@ -185,8 +206,14 @@ ui-validate: # rebuilding the UI .PHONY: packr packr: + $(SET) GO111MODULE=on packr2 # runs all of the tests and checks required for a PR to be accepted .PHONY: validate validate: ui-validate fmt-check vet lint it + +# locally builds and tags a 'stash/build' docker image +.PHONY: docker-build +docker-build: + docker build -t stash/build -f docker/build/x86_64/Dockerfile . \ No newline at end of file diff --git a/README.md b/README.md index 74ab1301c..0e97feefc 100644 --- a/README.md +++ b/README.md @@ -112,8 +112,7 @@ TODO 2. Download and install [MingW](https://sourceforge.net/projects/mingw-w64/) 3. Search for "advanced system settings" and open the system properties dialog. 1. Click the `Environment Variables` button - 2. Add `GO111MODULE=on` - 3. Under system variables find the `Path`. Edit and add `C:\Program Files\mingw-w64\*\mingw64\bin` (replace * with the correct path). + 2. Under system variables find the `Path`. Edit and add `C:\Program Files\mingw-w64\*\mingw64\bin` (replace * with the correct path). NOTE: The `make` command in Windows will be `mingw32-make` with MingW. @@ -121,6 +120,7 @@ NOTE: The `make` command in Windows will be `mingw32-make` with MingW. * `make generate` - Generate Go and UI GraphQL files * `make build` - Builds the binary (make sure to build the UI as well... see below) +* `make docker-build` - Locally builds and tags a complete 'stash/build' docker image * `make pre-ui` - Installs the UI dependencies. Only needs to be run once before building the UI for the first time, or if the dependencies are updated * `make fmt-ui` - Formats the UI source code. * `make ui` - Builds the frontend and the packr2 files diff --git a/docker/build/x86_64/Dockerfile b/docker/build/x86_64/Dockerfile index 3a2d8a198..389a665b3 100644 --- a/docker/build/x86_64/Dockerfile +++ b/docker/build/x86_64/Dockerfile @@ -1,61 +1,53 @@ -# this dockerfile must be built from the top-level stash directory -# ie from top=level stash: +# This dockerfile must be built from the top-level stash directory +# ie from top-level stash: # docker build -t stash/build -f docker/build/x86_64/Dockerfile . -FROM golang:1.13.15 as compiler +# Build Frontend +FROM node:alpine as frontend +RUN apk add --no-cache make git +## cache node_modules separately +COPY ./ui/v2.5/package.json ./ui/v2.5/yarn.lock /stash/ui/v2.5/ +WORKDIR /stash +RUN yarn --cwd ui/v2.5 install --frozen-lockfile. +COPY Makefile /stash/ +COPY ./.git /stash/.git +COPY ./graphql /stash/graphql/ +COPY ./ui /stash/ui/ +RUN make generate-frontend +RUN BUILD_DATE=$(date +"%Y-%m-%d %H:%M:%S") make ui-only -RUN apt-get update && apt-get install -y apt-transport-https -RUN curl -sL https://deb.nodesource.com/setup_lts.x | bash - - -# prevent caching of the key -ADD https://dl.yarnpkg.com/debian/pubkey.gpg yarn.gpg -RUN cat yarn.gpg | apt-key add - && \ - echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \ - rm yarn.gpg - -RUN apt-get update && \ - apt-get install -y nodejs yarn xz-utils --no-install-recommends || exit 1; \ - rm -rf /var/lib/apt/lists/*; - -ENV PACKR2_VERSION=2.0.2 -ENV PACKR2_SHA=f95ff4c96d7a28813220df030ad91700b8464fe292ab3e1dc9582305c2a338d2 +# Build Backend +FROM golang:1.17-alpine as backend +RUN apk add --no-cache xz make alpine-sdk +## install packr, ffmpeg +ENV PACKR2_VERSION=2.8.1 +ENV PACKR2_SHA=1cb2a0113550bc7962a8fda31a29877fcbbabd56b46c25e1fffbc225334162e7 ENV PACKR2_DOWNLOAD_FILE=packr_${PACKR2_VERSION}_linux_amd64.tar.gz ENV PACKR2_DOWNLOAD_URL=https://github.com/gobuffalo/packr/releases/download/v${PACKR2_VERSION}/${PACKR2_DOWNLOAD_FILE} - WORKDIR / RUN wget ${PACKR2_DOWNLOAD_URL}; \ - echo "$PACKR2_SHA $PACKR2_DOWNLOAD_FILE" | sha256sum -c - || exit 1; \ + echo "$PACKR2_SHA $PACKR2_DOWNLOAD_FILE" | sha256sum -c - || exit 1; \ tar -xzf $PACKR2_DOWNLOAD_FILE -C /usr/bin/ packr2; \ rm $PACKR2_DOWNLOAD_FILE; - -SHELL ["/bin/bash", "-o", "pipefail", "-c"] - RUN wget -O /ffmpeg.tar.xz https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz && \ tar xf /ffmpeg.tar.xz && \ rm ffmpeg.tar.xz && \ mv /ffmpeg*/ /ffmpeg/ - -# copy the ui yarn stuff so that it doesn't get rebuilt every time -COPY ./ui/v2.5/package.json ./ui/v2.5/yarn.lock /stash/ui/v2.5/ - WORKDIR /stash -RUN yarn --cwd ui/v2.5 install --frozen-lockfile - -COPY . /stash/ -ENV GO111MODULE=on - -RUN make generate -RUN make ui +COPY ./go* ./*.go Makefile gqlgen.yml .gqlgenc.yml /stash/ +COPY ./static /stash/static/ +COPY ./scripts /stash/scripts/ +COPY ./vendor /stash/vendor/ +COPY ./pkg /stash/pkg/ +COPY --from=frontend /stash /stash/ +RUN make generate-backend +RUN make packr RUN make build -FROM ubuntu:20.04 as app - -RUN apt-get update && apt-get -y install ca-certificates -COPY --from=compiler /stash/stash /ffmpeg/ffmpeg /ffmpeg/ffprobe /usr/bin/ - +# Final Runnable Image +FROM alpine:latest +RUN apk add --no-cache ca-certificates +COPY --from=backend /stash/stash /ffmpeg/ffmpeg /ffmpeg/ffprobe /usr/bin/ ENV STASH_CONFIG_FILE=/root/.stash/config.yml - EXPOSE 9999 -CMD ["stash"] - - +ENTRYPOINT ["stash"] \ No newline at end of file diff --git a/docker/ci/x86_64/Dockerfile b/docker/ci/x86_64/Dockerfile index 9eb5d57d4..53f3efeb2 100644 --- a/docker/ci/x86_64/Dockerfile +++ b/docker/ci/x86_64/Dockerfile @@ -11,7 +11,7 @@ RUN if [ "$TARGETPLATFORM" = "linux/arm/v6" ]; then BIN=stash-pi; \ ENV DEBIAN_FRONTEND=noninteractive RUN apt update && apt install -y python3 python-is-python3 python3-requests python3-requests-toolbelt python3-lxml python3-pip && pip3 install cloudscraper FROM ubuntu:20.04 as app -run apt update && apt install -y python3 python-is-python3 python3-requests python3-requests-toolbelt python3-lxml python3-mechanicalsoup ffmpeg && rm -rf /var/lib/apt/lists/* +RUN apt update && apt install -y python3 python-is-python3 python3-requests python3-requests-toolbelt python3-lxml python3-mechanicalsoup ffmpeg && rm -rf /var/lib/apt/lists/* COPY --from=prep /stash /usr/bin/ COPY --from=prep /usr/local/lib/python3.8/dist-packages /usr/local/lib/python3.8/dist-packages diff --git a/docker/compiler/Dockerfile b/docker/compiler/Dockerfile index 5f3f4b4c6..0450dabc5 100644 --- a/docker/compiler/Dockerfile +++ b/docker/compiler/Dockerfile @@ -1,9 +1,9 @@ -FROM golang:1.13.15 +FROM golang:1.17 -LABEL maintainer="stashappdev@gmail.com" +LABEL maintainer="https://discord.gg/2TsNFKt" -ENV PACKR2_VERSION=2.0.2 -ENV PACKR2_SHA=f95ff4c96d7a28813220df030ad91700b8464fe292ab3e1dc9582305c2a338d2 +ENV PACKR2_VERSION=2.8.1 +ENV PACKR2_SHA=1cb2a0113550bc7962a8fda31a29877fcbbabd56b46c25e1fffbc225334162e7 ENV PACKR2_DOWNLOAD_FILE=packr_${PACKR2_VERSION}_linux_amd64.tar.gz ENV PACKR2_DOWNLOAD_URL=https://github.com/gobuffalo/packr/releases/download/v${PACKR2_VERSION}/${PACKR2_DOWNLOAD_FILE} @@ -18,10 +18,10 @@ RUN cat yarn.gpg | apt-key add - && \ rm yarn.gpg RUN apt-get update && \ - apt-get install -y automake autogen \ + apt-get install -y automake autogen cmake \ libtool libxml2-dev uuid-dev libssl-dev bash \ - patch make tar xz-utils bzip2 gzip sed cpio \ - gcc-8-multilib gcc-mingw-w64 g++-mingw-w64 clang llvm-dev \ + patch make tar xz-utils bzip2 gzip zlib1g-dev sed cpio \ + gcc-10-multilib gcc-mingw-w64 g++-mingw-w64 clang llvm-dev \ gcc-arm-linux-gnueabi libc-dev-armel-cross linux-libc-dev-armel-cross \ gcc-arm-linux-gnueabihf libc-dev-armhf-cross \ gcc-aarch64-linux-gnu libc-dev-arm64-cross \ @@ -29,21 +29,22 @@ RUN apt-get update && \ rm -rf /var/lib/apt/lists/*; # Cross compile setup -ENV OSX_SDK_VERSION 10.11 +ENV OSX_SDK_VERSION 11.3 ENV OSX_SDK_DOWNLOAD_FILE=MacOSX${OSX_SDK_VERSION}.sdk.tar.xz -ENV OSX_SDK_DOWNLOAD_URL=https://github.com/ndeloof/golang-cross/raw/113fix/${OSX_SDK_DOWNLOAD_FILE} -ENV OSX_SDK_SHA=98cdd56e0f6c1f9e1af25e11dd93d2e7d306a4aa50430a2bc6bc083ac67efbb8 +ENV OSX_SDK_DOWNLOAD_URL=https://github.com/phracker/MacOSX-SDKs/releases/download/${OSX_SDK_VERSION}/${OSX_SDK_DOWNLOAD_FILE} +ENV OSX_SDK_SHA=cd4f08a75577145b8f05245a2975f7c81401d75e9535dcffbb879ee1deefcbf4 ENV OSX_SDK MacOSX$OSX_SDK_VERSION.sdk ENV OSX_NDK_X86 /usr/local/osx-ndk-x86 RUN wget ${OSX_SDK_DOWNLOAD_URL} RUN echo "$OSX_SDK_SHA $OSX_SDK_DOWNLOAD_FILE" | sha256sum -c - || exit 1; \ - git clone https://github.com/tpoechtrager/osxcross.git && \ - git -C osxcross checkout a9317c18a3a457ca0a657f08cc4d0d43c6cf8953 || exit 1; \ - mv $OSX_SDK_DOWNLOAD_FILE osxcross/tarballs/ && \ - UNATTENDED=yes SDK_VERSION=${OSX_SDK_VERSION} OSX_VERSION_MIN=10.9 osxcross/build.sh || exit 1; \ - mv osxcross/target $OSX_NDK_X86; \ - rm -rf osxcross; + git clone https://github.com/tpoechtrager/osxcross.git; \ + mv $OSX_SDK_DOWNLOAD_FILE osxcross/tarballs/ + +RUN UNATTENDED=yes SDK_VERSION=${OSX_SDK_VERSION} OSX_VERSION_MIN=10.10 osxcross/build.sh || exit 1; +RUN cp osxcross/target/lib/* /usr/lib/ ; \ + mv osxcross/target $OSX_NDK_X86; \ + rm -rf osxcross; ENV PATH $OSX_NDK_X86/bin:$PATH @@ -66,4 +67,4 @@ CMD ["packr2", "version"] # Darwin # CC=o64-clang CXX=o64-clang++ GOOS=darwin GOARCH=amd64 CGO_ENABLED=1 go build -tags extended -# env GO111MODULE=on goreleaser --config=goreleaser-extended.yml --skip-publish --skip-validate --rm-dist --release-notes=temp/0.48-relnotes-ready.md +# env goreleaser --config=goreleaser-extended.yml --skip-publish --skip-validate --rm-dist --release-notes=temp/0.48-relnotes-ready.md diff --git a/docker/compiler/Makefile b/docker/compiler/Makefile index 6f444e73d..5c4ed71ba 100644 --- a/docker/compiler/Makefile +++ b/docker/compiler/Makefile @@ -1,6 +1,6 @@ user=stashapp repo=compiler -version=4 +version=5 latest: docker build -t ${user}/${repo}:latest . diff --git a/docker/compiler/README.md b/docker/compiler/README.md index abe4060b5..d25b4a6cd 100644 --- a/docker/compiler/README.md +++ b/docker/compiler/README.md @@ -1,3 +1,5 @@ Modified from https://github.com/bep/dockerfiles/tree/master/ci-goreleaser -When the dockerfile is changed, the version number should be incremented in the Makefile and the new version tag should be pushed to docker hub. The `scripts/cross-compile.sh` script should also be updated to use the new version number tag, and `.travis.yml` needs to be updated to pull the correct image tag. \ No newline at end of file +When the dockerfile is changed, the version number should be incremented in the Makefile and the new version tag should be pushed to docker hub. The `scripts/cross-compile.sh` script should also be updated to use the new version number tag, and `.travis.yml` needs to be updated to pull the correct image tag. + +A MacOS univeral binary can be created using `lipo -create -output stash-osx-universal stash-osx stash-osx-applesilicon`, available in the image. \ No newline at end of file diff --git a/docker/compiler/create_osx_sdk.sh b/docker/compiler/create_osx_sdk.sh deleted file mode 100644 index 7920f6e0e..000000000 --- a/docker/compiler/create_osx_sdk.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash - -TMP=$(mktemp -d /tmp/XXXXXXXXXXX) -SDK="MacOSX10.11.sdk" - -mkdir -p $TMP/$SDK/usr/include/c++ - -cp -rf /Applications/Xcode7.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/$SDK $TMP &>/dev/null || true -cp -rf /Applications/Xcode7.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1 $TMP/$SDK/usr/include/c++ || exit -1 - -tar -C $TMP -czf $SDK.tar.gz $SDK diff --git a/docker/develop/x86_64/Dockerfile b/docker/develop/x86_64/Dockerfile deleted file mode 100644 index c2efef3a1..000000000 --- a/docker/develop/x86_64/Dockerfile +++ /dev/null @@ -1,27 +0,0 @@ -FROM ubuntu:20.04 as prep -LABEL MAINTAINER="https://discord.gg/Uz29ny" - -RUN apt-get update && \ - apt-get -y install curl xz-utils && \ - apt-get autoclean -y && \ - rm -rf /var/lib/apt/lists/* -WORKDIR / -SHELL ["/bin/bash", "-o", "pipefail", "-c"] - -# added " to end of stash-linux clause so that it doesn't pick up the arm builds -RUN curl -L -o /stash $(curl -s https://api.github.com/repos/stashapp/stash/releases/tags/latest_develop | awk '/browser_download_url/ && /stash-linux"/' | sed -e 's/.*: "\(.*\)"/\1/') && \ - chmod +x /stash - -RUN curl --http1.1 -o /ffmpeg.tar.xz https://johnvansickle.com/ffmpeg/releases/ffmpeg-release-amd64-static.tar.xz && \ - tar xf /ffmpeg.tar.xz && \ - rm ffmpeg.tar.xz && \ - mv /ffmpeg*/ /ffmpeg/ - -FROM ubuntu:20.04 as app -RUN apt-get update && apt-get -y install ca-certificates -COPY --from=prep /stash /ffmpeg/ffmpeg /ffmpeg/ffprobe /usr/bin/ - -ENV STASH_CONFIG_FILE=/root/.stash/config.yml - -EXPOSE 9999 -CMD ["stash"] diff --git a/docker/production/README.md b/docker/production/README.md index 87d47142d..40fe157bc 100644 --- a/docker/production/README.md +++ b/docker/production/README.md @@ -1,53 +1,37 @@ -# Docker install on Ubuntu 18.04 -Installing StashApp can likely work on others if your OS either has it's own package manager or comes shipped with Docker and docker-compose. +# Docker Installation (for most 64-bit GNU/Linux systems) +StashApp is supported on most systems that support Docker and docker-compose. Your OS likely ships with or makes available the necessary packages. ## Dependencies -The goal is to avoid as many dependencies as possible so for now the only pre-requisites you are required to have are `curl`, `docker`, and `docker-compose` for the most part your understanding of the technologies can be superficial so long as you can follow commands and are open to reading a bit you should be fine. +Only `docker` and `docker-compose` are required. For the most part your understanding of the technologies can be superficial. So long as you can follow commands and are open to reading a bit, you should be fine. + +Installation instructions are available below, and if your distrobution's repository ships a current version of docker, you may use that. +https://docs.docker.com/engine/install/ ### Docker +Docker is effectively a cross-platform software package repository. It allows you to ship an entire environment in what's referred to as a container. Containers are intended to hold everything that is needed to run an application from one place to another, making it easy for everyone along the way to reproduce the environment. -Docker is effectively the cross-platform software package repository it allows you to ship an entire environment in what's referred to as a container. Containers are intended to hold everything that is needed to ship what's required to run an application from one place to another with a degree of a standard that makes it easy for everyone along the way to reproduce the environment for their step in the chain. +The StashApp docker container ships with everything you need to automatically build and run stash, including ffmpeg. -The other side of docker is it brings everything that we would typically have to teach you about the individual components of your soon to be installed StashApp and ffmpeg, docker-compose wraps it up nicely in a handful of easy to follow steps that should result in the same environment on everyone's host. +### docker-compose +Docker Compose lets you specify how and where to run your containers, and to manage their environment. The docker-compose.yml file in this folder gets you a fully working instance of StashApp exactly as you would need it to have a reasonable instance for testing / developing on. If you are deploying a live instance for production, a reverse proxy (such as NGINX or Traefik) is recommended, but not required. -The installation method we recommend is via the `docker.com` website however if your specific operating system's repository versions are at the latest along with docker you should be good to launch with you using whatever instructions you wish. The version of Docker we used in our deployment for testing this process was `Docker version 17.05.0-ce, build 89658be` however any versions later than this will be sufficient. At the writing of this tutorial, this was not the latest version of Docker. - -#### Just the link to installation instructions, please -Instructions for installing on Ubuntu are at the link that follows: -https://docs.docker.com/install/linux/docker-ce/ubuntu/ - -If you plan on using other versions of OS you should at least aim to be a Linux base with an x86_64 CPU and the appropriate minimum version of the dependencies. - -### Docker-compose -Docker Compose's role in this deployment is to get you a fully working instance of StashApp exactly as you would need it to have a reasonable instance for testing / developing on, you could technically deploy a live instance with this, but without a reverse proxy, is not recommended. You are encouraged to learn how to use the Docker-Compose format, but it's not a required prerequisite for getting this running you need to have it installed successfully. - -Install Docker Compose via this guide below, and it is essential if you're using an older version of Linux to use the official documentation from Docker.com because you require the more recent version of docker-compose at least version 3.4 aka 1.22.0 or newer. - -#### Just the link to installation instructions, please -https://docs.docker.com/compose/install/ - -### Install curl -This one's easy, copy paste. - -``` -apt update -y && \ -apt install -f curl -``` +The latest version is always recommended. ### Get the docker-compose.yml file -Now you can either navigate to the [docker-compose.yml](https://raw.githubusercontent.com/stashapp/stash/master/docker/production/docker-compose.yml) in the repository, OR you can make your Linux console do it for you with this. +Now you can either navigate to the [docker-compose.yml](https://raw.githubusercontent.com/stashapp/stash/master/docker/production/docker-compose.yml) in the repository, or if you have curl, you can make your Linux console do it for you: ``` -curl -o ~/docker-compose.yml https://raw.githubusercontent.com/stashapp/stash/master/docker/production/docker-compose.yml +mkdir stashapp && cd stashapp +curl -o docker-compose.yml https://raw.githubusercontent.com/stashapp/stash/master/docker/production/docker-compose.yml ``` -Once you have that file where you want it, you can either modify the settings as you please OR you can run the following to get it up and running instantly. +Once you have that file where you want it, modify the settings as you please, and then run: ``` -cd ~ && docker-compose up -d +docker-compose up -d ``` -Installing StashApp this way will by default bind stash to port 9999 or in web browser terms. http://YOURIP:9999 or if you're doing this on your machine locally which is the only recommended production version of this container as is with no security configurations set at all is http://localhost:9999 +Installing StashApp this way will by default bind stash to port 9999. This is available in your web browser locally at http://localhost:9999 or on your network as http://YOUR-LOCAL-IP:9999 Good luck and have fun! diff --git a/docker/production/docker-compose.yml b/docker/production/docker-compose.yml index 1d20eb42c..cbb98b78a 100644 --- a/docker/production/docker-compose.yml +++ b/docker/production/docker-compose.yml @@ -22,7 +22,7 @@ services: ## Adjust below paths (the left part) to your liking. ## E.g. you can change ./config:/root/.stash to ./stash:/root/.stash - ## Keep configs here. + ## Keep configs, scrapers, and plugins here. - ./config:/root/.stash ## Point this at your collection. - ./data:/data diff --git a/docker/production/x86_64/Dockerfile b/docker/production/x86_64/Dockerfile index 95a2516ed..607cd1fbb 100644 --- a/docker/production/x86_64/Dockerfile +++ b/docker/production/x86_64/Dockerfile @@ -1,5 +1,5 @@ FROM ubuntu:20.04 as prep -LABEL MAINTAINER="leopere [at] nixc [dot] us" +LABEL MAINTAINER="https://discord.gg/2TsNFKt" RUN apt-get update && \ apt-get -y install curl xz-utils && \ diff --git a/go.mod b/go.mod index 08de5b6be..933df6bad 100644 --- a/go.mod +++ b/go.mod @@ -48,6 +48,55 @@ require ( gopkg.in/yaml.v2 v2.3.0 ) +require ( + github.com/agnivade/levenshtein v1.1.0 // indirect + github.com/antchfx/xpath v1.1.6 // indirect + github.com/chromedp/sysutil v1.0.0 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/fsnotify/fsnotify v1.4.7 // indirect + github.com/gobuffalo/packd v1.0.0 // indirect + github.com/gobwas/httphead v0.1.0 // indirect + github.com/gobwas/pool v0.2.1 // indirect + github.com/gobwas/ws v1.1.0-rc.5 // indirect + github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e // indirect + github.com/hashicorp/errwrap v1.0.0 // indirect + github.com/hashicorp/go-multierror v1.0.0 // indirect + github.com/hashicorp/golang-lru v0.5.1 // indirect + github.com/hashicorp/hcl v1.0.0 // indirect + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/magiconair/properties v1.8.1 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/markbates/errx v1.1.0 // indirect + github.com/markbates/oncer v1.0.0 // indirect + github.com/markbates/safe v1.0.1 // indirect + github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.1.2 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.1 // indirect + github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 // indirect + github.com/pelletier/go-toml v1.2.0 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/rs/zerolog v1.18.0 // indirect + github.com/russross/blackfriday/v2 v2.0.1 // indirect + github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect + github.com/spf13/cast v1.3.0 // indirect + github.com/spf13/cobra v1.0.0 // indirect + github.com/spf13/jwalterweatherman v1.0.0 // indirect + github.com/stretchr/objx v0.1.1 // indirect + github.com/subosito/gotenv v1.2.0 // indirect + github.com/tidwall/match v1.0.3 // indirect + github.com/urfave/cli/v2 v2.1.1 // indirect + github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e // indirect + golang.org/x/mod v0.3.0 // indirect + golang.org/x/text v0.3.3 // indirect + golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + gopkg.in/ini.v1 v1.51.0 // indirect +) + replace git.apache.org/thrift.git => github.com/apache/thrift v0.0.0-20180902110319-2566ecd5d999 -go 1.13 +go 1.17 diff --git a/pkg/api/check_version.go b/pkg/api/check_version.go index 0044d79c4..0a3ca4220 100644 --- a/pkg/api/check_version.go +++ b/pkg/api/check_version.go @@ -29,6 +29,7 @@ var ErrNoVersion = errors.New("no stash version") var stashReleases = func() map[string]string { return map[string]string{ "darwin/amd64": "stash-osx", + "darwin/arm64": "stash-osx-applesilicon", "linux/amd64": "stash-linux", "windows/amd64": "stash-win.exe", "linux/arm": "stash-pi", diff --git a/pkg/autotag/integration_test.go b/pkg/autotag/integration_test.go index 685cae74a..264e82ea3 100644 --- a/pkg/autotag/integration_test.go +++ b/pkg/autotag/integration_test.go @@ -1,3 +1,4 @@ +//go:build integration // +build integration package autotag diff --git a/pkg/plugin/examples/common/graphql.go b/pkg/plugin/examples/common/graphql.go index 3ed5ec341..624c0d5d8 100644 --- a/pkg/plugin/examples/common/graphql.go +++ b/pkg/plugin/examples/common/graphql.go @@ -1,3 +1,4 @@ +//go:build plugin_example // +build plugin_example package common diff --git a/pkg/plugin/examples/goraw/main.go b/pkg/plugin/examples/goraw/main.go index 26060f32c..1a6736848 100644 --- a/pkg/plugin/examples/goraw/main.go +++ b/pkg/plugin/examples/goraw/main.go @@ -1,3 +1,4 @@ +//go:build plugin_example // +build plugin_example package main diff --git a/pkg/plugin/examples/gorpc/main.go b/pkg/plugin/examples/gorpc/main.go index 75e364549..1a86131cc 100644 --- a/pkg/plugin/examples/gorpc/main.go +++ b/pkg/plugin/examples/gorpc/main.go @@ -1,3 +1,4 @@ +//go:build plugin_example // +build plugin_example package main diff --git a/pkg/sqlite/gallery_test.go b/pkg/sqlite/gallery_test.go index 5ba8db446..03f03a544 100644 --- a/pkg/sqlite/gallery_test.go +++ b/pkg/sqlite/gallery_test.go @@ -1,3 +1,4 @@ +//go:build integration // +build integration package sqlite_test diff --git a/pkg/sqlite/image_test.go b/pkg/sqlite/image_test.go index 50006685b..6d866041f 100644 --- a/pkg/sqlite/image_test.go +++ b/pkg/sqlite/image_test.go @@ -1,3 +1,4 @@ +//go:build integration // +build integration package sqlite_test diff --git a/pkg/sqlite/movies_test.go b/pkg/sqlite/movies_test.go index d067f96d8..a81cc4586 100644 --- a/pkg/sqlite/movies_test.go +++ b/pkg/sqlite/movies_test.go @@ -1,3 +1,4 @@ +//go:build integration // +build integration package sqlite_test diff --git a/pkg/sqlite/performer_test.go b/pkg/sqlite/performer_test.go index c5292f384..a68e09e03 100644 --- a/pkg/sqlite/performer_test.go +++ b/pkg/sqlite/performer_test.go @@ -1,3 +1,4 @@ +//go:build integration // +build integration package sqlite_test diff --git a/pkg/sqlite/saved_filter_test.go b/pkg/sqlite/saved_filter_test.go index 4cd33c97a..5ec049290 100644 --- a/pkg/sqlite/saved_filter_test.go +++ b/pkg/sqlite/saved_filter_test.go @@ -1,3 +1,4 @@ +//go:build integration // +build integration package sqlite_test diff --git a/pkg/sqlite/scene_marker_test.go b/pkg/sqlite/scene_marker_test.go index bc3d95daa..9e71921cb 100644 --- a/pkg/sqlite/scene_marker_test.go +++ b/pkg/sqlite/scene_marker_test.go @@ -1,3 +1,4 @@ +//go:build integration // +build integration package sqlite_test diff --git a/pkg/sqlite/scene_test.go b/pkg/sqlite/scene_test.go index ec95447da..2be8e4d45 100644 --- a/pkg/sqlite/scene_test.go +++ b/pkg/sqlite/scene_test.go @@ -1,3 +1,4 @@ +//go:build integration // +build integration package sqlite_test diff --git a/pkg/sqlite/setup_test.go b/pkg/sqlite/setup_test.go index d25e3c4e4..f51f93a0e 100644 --- a/pkg/sqlite/setup_test.go +++ b/pkg/sqlite/setup_test.go @@ -1,3 +1,4 @@ +//go:build integration // +build integration package sqlite_test diff --git a/pkg/sqlite/stash_id_test.go b/pkg/sqlite/stash_id_test.go index 5c9b44f4c..0f57bef19 100644 --- a/pkg/sqlite/stash_id_test.go +++ b/pkg/sqlite/stash_id_test.go @@ -1,3 +1,4 @@ +//go:build integration // +build integration package sqlite_test diff --git a/pkg/sqlite/studio_test.go b/pkg/sqlite/studio_test.go index 8c97e50e7..929f86ca3 100644 --- a/pkg/sqlite/studio_test.go +++ b/pkg/sqlite/studio_test.go @@ -1,3 +1,4 @@ +//go:build integration // +build integration package sqlite_test diff --git a/pkg/sqlite/tag_test.go b/pkg/sqlite/tag_test.go index add3753a7..76d84e71f 100644 --- a/pkg/sqlite/tag_test.go +++ b/pkg/sqlite/tag_test.go @@ -1,3 +1,4 @@ +//go:build integration // +build integration package sqlite_test diff --git a/scripts/cross-compile.sh b/scripts/cross-compile.sh index e46abed28..a5db1b618 100755 --- a/scripts/cross-compile.sh +++ b/scripts/cross-compile.sh @@ -1,17 +1,18 @@ #!/bin/bash # "stashapp/compiler:develop" "stashapp/compiler:4" -COMPILER_CONTAINER="stashapp/compiler:4" +COMPILER_CONTAINER="stashapp/compiler:5" BUILD_DATE=`go run -mod=vendor scripts/getDate.go` GITHASH=`git rev-parse --short HEAD` STASH_VERSION=`git describe --tags --exclude latest_develop` SETENV="BUILD_DATE=\"$BUILD_DATE\" GITHASH=$GITHASH STASH_VERSION=\"$STASH_VERSION\"" -SETUP="export GO111MODULE=on; export CGO_ENABLED=1; set -e; echo '=== Running packr ==='; make packr;" -SETUP_FAST="export GO111MODULE=on; export CGO_ENABLED=1;" +SETUP="export CGO_ENABLED=1; set -e; echo '=== Running packr ==='; make packr;" +SETUP_FAST="export CGO_ENABLED=1;" WINDOWS="echo '=== Building Windows binary ==='; $SETENV make cross-compile-windows;" -DARWIN="echo '=== Building OSX binary ==='; $SETENV make cross-compile-osx;" +DARWIN="echo '=== Building OSX binary ==='; $SETENV make cross-compile-osx-intel;" +DARWIN-ARM64="echo '=== Building OSX (arm64) binary ==='; $SETENV make cross-compile-osx-applesilicon;" LINUX_AMD64="echo '=== Building Linux (amd64) binary ==='; $SETENV make cross-compile-linux;" LINUX_ARM64v8="echo '=== Building Linux (armv8/arm64) binary ==='; $SETENV make cross-compile-linux-arm64v8;" LINUX_ARM32v7="echo '=== Building Linux (armv7/armhf) binary ==='; $SETENV make cross-compile-linux-arm32v7;" diff --git a/tools.go b/tools.go index 9bc7b212d..fa219bb53 100644 --- a/tools.go +++ b/tools.go @@ -1,3 +1,4 @@ +//go:build tools // +build tools package main diff --git a/vendor/github.com/99designs/gqlgen/go.mod b/vendor/github.com/99designs/gqlgen/go.mod deleted file mode 100644 index 16affcf04..000000000 --- a/vendor/github.com/99designs/gqlgen/go.mod +++ /dev/null @@ -1,33 +0,0 @@ -module github.com/99designs/gqlgen - -go 1.12 - -require ( - github.com/agnivade/levenshtein v1.0.3 // indirect - github.com/go-chi/chi v3.3.2+incompatible - github.com/gogo/protobuf v1.0.0 // indirect - github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f // indirect - github.com/gorilla/mux v1.6.1 // indirect - github.com/gorilla/websocket v1.4.2 - github.com/hashicorp/golang-lru v0.5.0 - github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 - github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007 - github.com/mattn/go-colorable v0.1.4 - github.com/mattn/go-isatty v0.0.12 - github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047 - github.com/opentracing/basictracer-go v1.0.0 // indirect - github.com/opentracing/opentracing-go v1.0.2 - github.com/pkg/errors v0.8.1 - github.com/rs/cors v1.6.0 - github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371 // indirect - github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0 // indirect - github.com/stretchr/testify v1.4.0 - github.com/urfave/cli/v2 v2.1.1 - github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e - github.com/vektah/gqlparser v1.3.1 - github.com/vektah/gqlparser/v2 v2.0.1 - golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589 - gopkg.in/yaml.v2 v2.2.4 - sourcegraph.com/sourcegraph/appdash v0.0.0-20180110180208-2cc67fd64755 - sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67 // indirect -) diff --git a/vendor/github.com/99designs/gqlgen/go.sum b/vendor/github.com/99designs/gqlgen/go.sum deleted file mode 100644 index 02d393c3b..000000000 --- a/vendor/github.com/99designs/gqlgen/go.sum +++ /dev/null @@ -1,114 +0,0 @@ -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= -github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0= -github.com/agnivade/levenshtein v1.0.3/go.mod h1:4SFRZbbXWLF4MU1T9Qg0pGgH3Pjs+t6ie5efyrwRJXs= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= -github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c h1:TUuUh0Xgj97tLMNtWtNvI9mIV6isjEb9lBMNv+77IGM= -github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= -github.com/go-chi/chi v3.3.2+incompatible h1:uQNcQN3NsV1j4ANsPh42P4ew4t6rnRbJb8frvpp31qQ= -github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/gogo/protobuf v1.0.0 h1:2jyBKDKU/8v3v2xVR2PtiWQviFUyiaGk2rpfyFT8rTM= -github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f h1:9oNbS1z4rVpbnkHBdPZU4jo9bSmrLpII768arSyMFgk= -github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.1 h1:KOwqsTYZdeuMacU7CxjMNYEKeBvLbxW+psodrbcEa3A= -github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381 h1:bqDmpDG49ZRnB5PcgP0RXtQvnMSgIF14M7CBd2shtXs= -github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007 h1:reVOUXwnhsYv/8UqjvhrMOu5CNT9UapHFLbQ2JcXsmg= -github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= -github.com/mattn/go-colorable v0.1.4 h1:snbPLB8fVfU9iwbbo30TPtbLRzwWu6aJS6Xh4eaaviA= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047 h1:zCoDWFD5nrJJVjbXiDZcVhOBSzKn3o9LgRLLMRNuru8= -github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7lZWlQw5UXuoo= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rs/cors v1.6.0 h1:G9tHG9lebljV9mfp9SNPDL36nCDxmo3zTlAf1YgvzmI= -github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371 h1:SWV2fHctRpRrp49VXJ6UZja7gU9QLHwRpIPBN89SKEo= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0 h1:JJV9CsgM9EC9w2iVkwuz+sMx8yRFe89PJRUrv6hPCIA= -github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.1 h1:52QO5WkIUcHGIR7EnGagH88x1bUzqGXTC5/1bDTUQ7U= -github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k= -github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= -github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e h1:+w0Zm/9gaWpEAyDlU1eKOuk5twTjAjuevXqcJJw8hrg= -github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U= -github.com/vektah/gqlparser v1.3.1 h1:8b0IcD3qZKWJQHSzynbDlrtP3IxVydZ2DZepCGofqfU= -github.com/vektah/gqlparser v1.3.1/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74= -github.com/vektah/gqlparser/v2 v2.0.1 h1:xgl5abVnsd4hkN9rk65OJID9bfcLSMuTaTcZj777q1o= -github.com/vektah/gqlparser/v2 v2.0.1/go.mod h1:SyUiHgLATUR8BiYURfTirrTcGpcE+4XkV2se04Px1Ms= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42 h1:vEOn+mP2zCOVzKckCZy6YsCtDblrpj/w7B9nxGNELpg= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6 h1:iZgcI2DDp6zW5v9Z/5+f0NuqoxNdmzg4hivjk2WLXpY= -golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd h1:oMEQDWVXVNpceQoVd1JN3CQ7LYJJzs5qWqZIUcxXHHw= -golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589 h1:rjUrONFu4kLchcZTfp3/96bR8bW8dIa8uz3cR5n0cgM= -golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -sourcegraph.com/sourcegraph/appdash v0.0.0-20180110180208-2cc67fd64755 h1:d2maSb13hr/ArmfK3rW+wNUKKfytCol7W1/vDHxMPiE= -sourcegraph.com/sourcegraph/appdash v0.0.0-20180110180208-2cc67fd64755/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= -sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67 h1:e1sMhtVq9AfcEy8AXNb8eSg6gbzfdpYhoNqnPJa+GzI= -sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67/go.mod h1:L5q+DGLGOQFpo1snNEkLOJT2d1YTW66rWNzatr3He1k= diff --git a/vendor/github.com/Yamashou/gqlgenc/go.mod b/vendor/github.com/Yamashou/gqlgenc/go.mod deleted file mode 100644 index 7b4208f7d..000000000 --- a/vendor/github.com/Yamashou/gqlgenc/go.mod +++ /dev/null @@ -1,14 +0,0 @@ -module github.com/Yamashou/gqlgenc - -go 1.14 - -require ( - github.com/99designs/gqlgen v0.12.2 - github.com/agnivade/levenshtein v1.1.0 // indirect - github.com/google/go-cmp v0.5.2 - github.com/pkg/errors v0.9.1 // indirect - github.com/vektah/gqlparser/v2 v2.0.1 - golang.org/x/tools v0.0.0-20200827163409-021d7c6f1ec3 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 - gopkg.in/yaml.v2 v2.3.0 -) diff --git a/vendor/github.com/Yamashou/gqlgenc/go.sum b/vendor/github.com/Yamashou/gqlgenc/go.sum deleted file mode 100644 index 966cfd8d7..000000000 --- a/vendor/github.com/Yamashou/gqlgenc/go.sum +++ /dev/null @@ -1,122 +0,0 @@ -github.com/99designs/gqlgen v0.12.2 h1:aOdpsiCycFtCnAv8CAI1exnKrIDHMqtMzQoXeTziY4o= -github.com/99designs/gqlgen v0.12.2/go.mod h1:7zdGo6ry9u1YBp/qlb2uxSU5Mt2jQKLcBETQiKk+Bxo= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= -github.com/agnivade/levenshtein v1.0.3 h1:M5ZnqLOoZR8ygVq0FfkXsNOKzMCk0xRiow0R5+5VkQ0= -github.com/agnivade/levenshtein v1.0.3/go.mod h1:4SFRZbbXWLF4MU1T9Qg0pGgH3Pjs+t6ie5efyrwRJXs= -github.com/agnivade/levenshtein v1.1.0 h1:n6qGwyHG61v3ABce1rPVZklEYRT8NFpCMrpZdBUbYGM= -github.com/agnivade/levenshtein v1.1.0/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= -github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c h1:TUuUh0Xgj97tLMNtWtNvI9mIV6isjEb9lBMNv+77IGM= -github.com/dgryski/trifles v0.0.0-20190318185328-a8d75aae118c/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= -github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= -github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= -github.com/go-chi/chi v3.3.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= -github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM= -github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/gorilla/context v0.0.0-20160226214623-1ea25387ff6f/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/hashicorp/golang-lru v0.5.0 h1:CL2msUPvZTLb5O648aiLNJw3hnBxN2+1Jq8rCOH9wdo= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/logrusorgru/aurora v0.0.0-20200102142835-e9ef32dff381/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007 h1:reVOUXwnhsYv/8UqjvhrMOu5CNT9UapHFLbQ2JcXsmg= -github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007/go.mod h1:9ELz6aaclSIGnZBoaSLZ3NAl1VTufbOrXBPvtcy6WiQ= -github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= -github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= -github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY= -github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047 h1:zCoDWFD5nrJJVjbXiDZcVhOBSzKn3o9LgRLLMRNuru8= -github.com/mitchellh/mapstructure v0.0.0-20180203102830-a4e142e9c047/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= -github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg= -github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= -github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rs/cors v1.6.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/shurcooL/vfsgen v0.0.0-20180121065927-ffb13db8def0/go.mod h1:TrYk7fJVaAttu97ZZKrO9UbRa8izdowaMIZcxYMbVaw= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k= -github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= -github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e h1:+w0Zm/9gaWpEAyDlU1eKOuk5twTjAjuevXqcJJw8hrg= -github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e/go.mod h1:/HUdMve7rvxZma+2ZELQeNh88+003LL7Pf/CZ089j8U= -github.com/vektah/gqlparser v1.3.1 h1:8b0IcD3qZKWJQHSzynbDlrtP3IxVydZ2DZepCGofqfU= -github.com/vektah/gqlparser v1.3.1/go.mod h1:bkVf0FX+Stjg/MHnm8mEyubuaArhNEqfQhF+OTiAL74= -github.com/vektah/gqlparser/v2 v2.0.1 h1:xgl5abVnsd4hkN9rk65OJID9bfcLSMuTaTcZj777q1o= -github.com/vektah/gqlparser/v2 v2.0.1/go.mod h1:SyUiHgLATUR8BiYURfTirrTcGpcE+4XkV2se04Px1Ms= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4= -golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589 h1:rjUrONFu4kLchcZTfp3/96bR8bW8dIa8uz3cR5n0cgM= -golang.org/x/tools v0.0.0-20200114235610-7ae403b6b589/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200827163409-021d7c6f1ec3 h1:OjYQxZBKJFs+sJbHkvSGIKNMkZXDJQ9JsMpebGhkafI= -golang.org/x/tools v0.0.0-20200827163409-021d7c6f1ec3/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -sourcegraph.com/sourcegraph/appdash v0.0.0-20180110180208-2cc67fd64755/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= -sourcegraph.com/sourcegraph/appdash-data v0.0.0-20151005221446-73f23eafcf67/go.mod h1:L5q+DGLGOQFpo1snNEkLOJT2d1YTW66rWNzatr3He1k= diff --git a/vendor/github.com/agnivade/levenshtein/go.mod b/vendor/github.com/agnivade/levenshtein/go.mod deleted file mode 100644 index 4fcfe43e0..000000000 --- a/vendor/github.com/agnivade/levenshtein/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/agnivade/levenshtein - -go 1.13 - -require ( - github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 - github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 -) diff --git a/vendor/github.com/agnivade/levenshtein/go.sum b/vendor/github.com/agnivade/levenshtein/go.sum deleted file mode 100644 index 74d92aad1..000000000 --- a/vendor/github.com/agnivade/levenshtein/go.sum +++ /dev/null @@ -1,4 +0,0 @@ -github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q= -github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE= -github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g= -github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA= diff --git a/vendor/github.com/antchfx/htmlquery/go.mod b/vendor/github.com/antchfx/htmlquery/go.mod deleted file mode 100644 index 14169f5c3..000000000 --- a/vendor/github.com/antchfx/htmlquery/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module github.com/antchfx/htmlquery - -go 1.14 - -require ( - github.com/antchfx/xpath v1.1.6 - github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e - golang.org/x/net v0.0.0-20200421231249-e086a090c8fd -) diff --git a/vendor/github.com/antchfx/htmlquery/go.sum b/vendor/github.com/antchfx/htmlquery/go.sum deleted file mode 100644 index 2d6e03e34..000000000 --- a/vendor/github.com/antchfx/htmlquery/go.sum +++ /dev/null @@ -1,11 +0,0 @@ -github.com/antchfx/xpath v1.1.6 h1:6sVh6hB5T6phw1pFpHRQ+C4bd8sNI+O58flqtg7h0R0= -github.com/antchfx/xpath v1.1.6/go.mod h1:Yee4kTMuNiPYJ7nSNorELQMr1J33uOpXDMByNYhvtNk= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20200421231249-e086a090c8fd h1:QPwSajcTUrFriMF1nJ3XzgoqakqQEsnZf9LdXdi2nkI= -golang.org/x/net v0.0.0-20200421231249-e086a090c8fd/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/vendor/github.com/chromedp/cdproto/go.mod b/vendor/github.com/chromedp/cdproto/go.mod deleted file mode 100644 index 281ac76d2..000000000 --- a/vendor/github.com/chromedp/cdproto/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/chromedp/cdproto - -require ( - github.com/chromedp/sysutil v1.0.0 - github.com/mailru/easyjson v0.7.7 -) - -go 1.14 diff --git a/vendor/github.com/chromedp/cdproto/go.sum b/vendor/github.com/chromedp/cdproto/go.sum deleted file mode 100644 index 3cd107367..000000000 --- a/vendor/github.com/chromedp/cdproto/go.sum +++ /dev/null @@ -1,6 +0,0 @@ -github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= -github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= diff --git a/vendor/github.com/chromedp/chromedp/go.mod b/vendor/github.com/chromedp/chromedp/go.mod deleted file mode 100644 index 05c4903ce..000000000 --- a/vendor/github.com/chromedp/chromedp/go.mod +++ /dev/null @@ -1,10 +0,0 @@ -module github.com/chromedp/chromedp - -go 1.16 - -require ( - github.com/chromedp/cdproto v0.0.0-20210526005521-9e51b9051fd0 - github.com/gobwas/ws v1.1.0-rc.5 - github.com/mailru/easyjson v0.7.7 - golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea // indirect -) diff --git a/vendor/github.com/chromedp/chromedp/go.sum b/vendor/github.com/chromedp/chromedp/go.sum deleted file mode 100644 index 00e17ada9..000000000 --- a/vendor/github.com/chromedp/chromedp/go.sum +++ /dev/null @@ -1,17 +0,0 @@ -github.com/chromedp/cdproto v0.0.0-20210526005521-9e51b9051fd0 h1:aIcgRshD5I1MfJfB92KBDKpaXrYqj3fkqI8bHdtP3zA= -github.com/chromedp/cdproto v0.0.0-20210526005521-9e51b9051fd0/go.mod h1:At5TxYYdxkbQL0TSefRjhLE3Q0lgvqKKMSFUglJ7i1U= -github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= -github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= -github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= -github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= -github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= -github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -github.com/gobwas/ws v1.1.0-rc.5 h1:QOAag7FoBaBYYHRqzqkhhd8fq5RTubvI4v3Ft/gDVVQ= -github.com/gobwas/ws v1.1.0-rc.5/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea h1:+WiDlPBBaO+h9vPNZi8uJ3k4BkKQB7Iow3aqwHVA5hI= -golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/vendor/github.com/chromedp/sysutil/go.mod b/vendor/github.com/chromedp/sysutil/go.mod deleted file mode 100644 index 6f3128c43..000000000 --- a/vendor/github.com/chromedp/sysutil/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/chromedp/sysutil - -go 1.15 diff --git a/vendor/github.com/corona10/goimagehash/go.mod b/vendor/github.com/corona10/goimagehash/go.mod deleted file mode 100644 index 681fdc2bf..000000000 --- a/vendor/github.com/corona10/goimagehash/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/corona10/goimagehash - -require github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 diff --git a/vendor/github.com/corona10/goimagehash/go.sum b/vendor/github.com/corona10/goimagehash/go.sum deleted file mode 100644 index 96adbed66..000000000 --- a/vendor/github.com/corona10/goimagehash/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ= -github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8= diff --git a/vendor/github.com/disintegration/imaging/go.mod b/vendor/github.com/disintegration/imaging/go.mod deleted file mode 100644 index 126e8cc66..000000000 --- a/vendor/github.com/disintegration/imaging/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/disintegration/imaging - -require golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81 diff --git a/vendor/github.com/disintegration/imaging/go.sum b/vendor/github.com/disintegration/imaging/go.sum deleted file mode 100644 index 20c92e460..000000000 --- a/vendor/github.com/disintegration/imaging/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81 h1:00VmoueYNlNz/aHIilyyQz/MHSqGoWJzpFv/HW8xpzI= -golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs= diff --git a/vendor/github.com/fvbommel/sortorder/go.mod b/vendor/github.com/fvbommel/sortorder/go.mod deleted file mode 100644 index 57c8175e3..000000000 --- a/vendor/github.com/fvbommel/sortorder/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/fvbommel/sortorder - -go 1.13 diff --git a/vendor/github.com/fvbommel/sortorder/go.sum b/vendor/github.com/fvbommel/sortorder/go.sum deleted file mode 100644 index e69de29bb..000000000 diff --git a/vendor/github.com/gobuffalo/logger/go.mod b/vendor/github.com/gobuffalo/logger/go.mod deleted file mode 100644 index f55f05c69..000000000 --- a/vendor/github.com/gobuffalo/logger/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/gobuffalo/logger - -go 1.13 - -require ( - github.com/sirupsen/logrus v1.4.2 - golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf -) diff --git a/vendor/github.com/gobuffalo/logger/go.sum b/vendor/github.com/gobuffalo/logger/go.sum deleted file mode 100644 index 47ed4e3c8..000000000 --- a/vendor/github.com/gobuffalo/logger/go.sum +++ /dev/null @@ -1,17 +0,0 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68 h1:nxC68pudNYkKU6jWhgrqdreuFiOQWj1Fs7T3VrH4Pjw= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M= -golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= diff --git a/vendor/github.com/gobuffalo/packd/go.mod b/vendor/github.com/gobuffalo/packd/go.mod deleted file mode 100644 index 8fe09370f..000000000 --- a/vendor/github.com/gobuffalo/packd/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module github.com/gobuffalo/packd - -go 1.13 - -require github.com/stretchr/testify v1.4.0 diff --git a/vendor/github.com/gobuffalo/packd/go.sum b/vendor/github.com/gobuffalo/packd/go.sum deleted file mode 100644 index 8fdee5854..000000000 --- a/vendor/github.com/gobuffalo/packd/go.sum +++ /dev/null @@ -1,11 +0,0 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/gobuffalo/packr/v2/go.mod b/vendor/github.com/gobuffalo/packr/v2/go.mod deleted file mode 100644 index 82bf27a34..000000000 --- a/vendor/github.com/gobuffalo/packr/v2/go.mod +++ /dev/null @@ -1,19 +0,0 @@ -module github.com/gobuffalo/packr/v2 - -go 1.13 - -require ( - github.com/gobuffalo/logger v1.0.3 - github.com/gobuffalo/packd v1.0.0 - github.com/karrick/godirwalk v1.15.8 - github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect - github.com/markbates/errx v1.1.0 - github.com/markbates/oncer v1.0.0 - github.com/markbates/safe v1.0.1 - github.com/rogpeppe/go-internal v1.5.2 - github.com/sirupsen/logrus v1.4.2 - github.com/spf13/cobra v0.0.6 - github.com/stretchr/testify v1.5.1 - golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e - golang.org/x/tools v0.0.0-20200308013534-11ec41452d41 -) diff --git a/vendor/github.com/gobuffalo/packr/v2/go.sum b/vendor/github.com/gobuffalo/packr/v2/go.sum deleted file mode 100644 index 86abdab8c..000000000 --- a/vendor/github.com/gobuffalo/packr/v2/go.sum +++ /dev/null @@ -1,180 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gobuffalo/logger v1.0.3 h1:YaXOTHNPCvkqqA7w05A4v0k2tCdpr+sgFlgINbQ6gqc= -github.com/gobuffalo/logger v1.0.3/go.mod h1:SoeejUwldiS7ZsyCBphOGURmWdwUFXs0J7TCjEhjKxM= -github.com/gobuffalo/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM= -github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/karrick/godirwalk v1.15.8 h1:7+rWAZPn9zuRxaIqqT8Ohs2Q2Ac0msBqwRdxNCr2VVs= -github.com/karrick/godirwalk v1.15.8/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/markbates/errx v1.1.0 h1:QDFeR+UP95dO12JgW+tgi2UVfo0V8YBHiUIOaeBPiEI= -github.com/markbates/errx v1.1.0/go.mod h1:PLa46Oex9KNbVDZhKel8v1OT7hD5JZ2eI7AHhA0wswc= -github.com/markbates/oncer v1.0.0 h1:E83IaVAHygyndzPimgUYJjbshhDTALZyXxvk9FOlQRY= -github.com/markbates/oncer v1.0.0/go.mod h1:Z59JA581E9GP6w96jai+TGqafHPW+cPfRxz2aSZ0mcI= -github.com/markbates/safe v1.0.1 h1:yjZkbvRM6IzKj9tlu/zMJLS0n/V351OZWRnF3QfaUxI= -github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.5.2 h1:qLvObTrvO/XRCqmkKxUlOBc48bI3efyDuAZe25QiF0w= -github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= -github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.6 h1:breEStsVwemnKh2/s6gMvSdMEkwW0sK8vGStnlVBMCs= -github.com/spf13/cobra v0.0.6/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c h1:/nJuwDLoL/zrqY6gf57vxC+Pi+pZ8bfhpPkicO5H7W4= -golang.org/x/crypto v0.0.0-20191122220453-ac88ee75c92c/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e h1:vcxGaoTs7kV8m5Np9uUNQin4BrLOthgV7252N8V+FwY= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= -golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200308013534-11ec41452d41 h1:9Di9iYgOt9ThCipBxChBVhgNipDoE5mxO84rQV7D0FE= -golang.org/x/tools v0.0.0-20200308013534-11ec41452d41/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/vendor/github.com/gobwas/httphead/go.mod b/vendor/github.com/gobwas/httphead/go.mod deleted file mode 100644 index 2393cedc2..000000000 --- a/vendor/github.com/gobwas/httphead/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/gobwas/httphead - -go 1.15 diff --git a/vendor/github.com/gobwas/ws/go.mod b/vendor/github.com/gobwas/ws/go.mod deleted file mode 100644 index b0bc12a61..000000000 --- a/vendor/github.com/gobwas/ws/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module github.com/gobwas/ws - -go 1.15 - -require ( - github.com/gobwas/httphead v0.1.0 - github.com/gobwas/pool v0.2.1 - golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d // indirect -) diff --git a/vendor/github.com/gobwas/ws/go.sum b/vendor/github.com/gobwas/ws/go.sum deleted file mode 100644 index 40b998a0b..000000000 --- a/vendor/github.com/gobwas/ws/go.sum +++ /dev/null @@ -1,6 +0,0 @@ -github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= -github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= -github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= -github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= -golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d h1:MiWWjyhUzZ+jvhZvloX6ZrUsdEghn8a64Upd8EMHglE= -golang.org/x/sys v0.0.0-20201207223542-d4d67f95c62d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/golang-migrate/migrate/v4/go.mod b/vendor/github.com/golang-migrate/migrate/v4/go.mod deleted file mode 100644 index 58df5095c..000000000 --- a/vendor/github.com/golang-migrate/migrate/v4/go.mod +++ /dev/null @@ -1,54 +0,0 @@ -module github.com/golang-migrate/migrate/v4 - -require ( - cloud.google.com/go v0.37.4 - github.com/aws/aws-sdk-go v1.17.7 - github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 // indirect - github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 // indirect - github.com/cockroachdb/apd v1.1.0 // indirect - github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c - github.com/cznic/ql v1.2.0 - github.com/dhui/dktest v0.3.0 - github.com/docker/docker v0.7.3-0.20190108045446-77df18c24acf - github.com/fsouza/fake-gcs-server v1.7.0 - github.com/go-sql-driver/mysql v1.4.1 - github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4 - github.com/gogo/protobuf v1.2.1 // indirect - github.com/golang/protobuf v1.3.1 // indirect - github.com/golang/snappy v0.0.1 // indirect - github.com/google/go-github v17.0.0+incompatible - github.com/hashicorp/go-multierror v1.0.0 - github.com/hashicorp/golang-lru v0.5.1 // indirect - github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect - github.com/jackc/pgx v3.2.0+incompatible // indirect - github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect - github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect - github.com/kr/pretty v0.1.0 // indirect - github.com/kshvakov/clickhouse v1.3.5 - github.com/lib/pq v1.0.0 - github.com/mattn/go-sqlite3 v1.10.0 - github.com/mongodb/mongo-go-driver v0.3.0 - github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8 - github.com/pkg/errors v0.8.1 // indirect - github.com/satori/go.uuid v1.2.0 // indirect - github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 // indirect - github.com/sirupsen/logrus v1.4.1 // indirect - github.com/stretchr/testify v1.3.0 // indirect - github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51 // indirect - github.com/xanzy/go-gitlab v0.15.0 - github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c // indirect - github.com/xdg/stringprep v1.0.0 // indirect - gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b // indirect - golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 // indirect - golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6 - golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a // indirect - golang.org/x/sys v0.0.0-20190426135247-a129542de9ae // indirect - golang.org/x/text v0.3.2 // indirect - golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 // indirect - golang.org/x/tools v0.0.0-20190425222832-ad9eeb80039a - google.golang.org/api v0.4.0 - google.golang.org/appengine v1.5.0 // indirect - google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb - google.golang.org/grpc v1.20.1 // indirect - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect -) diff --git a/vendor/github.com/golang-migrate/migrate/v4/go.sum b/vendor/github.com/golang-migrate/migrate/v4/go.sum deleted file mode 100644 index 52d422433..000000000 --- a/vendor/github.com/golang-migrate/migrate/v4/go.sum +++ /dev/null @@ -1,300 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.37.4 h1:glPeL3BQJsbF6aIIYfZizMwc5LTYz250bDMjttbBGAU= -cloud.google.com/go v0.37.4/go.mod h1:NHPJ89PdicEuT9hdPXMROBD91xc5uRDxsMtSB16k7hw= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/Microsoft/go-winio v0.4.11 h1:zoIOcVf0xPN1tnMVbTtEdI+P8OofVk3NObnwOQ6nK2Q= -github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= -github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= -github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= -github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= -github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= -github.com/aws/aws-sdk-go v1.17.7 h1:/4+rDPe0W95KBmNGYCG+NUvdL8ssPYBMxL+aSCg6nIA= -github.com/aws/aws-sdk-go v1.17.7/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932 h1:mXoPYz/Ul5HYEDvkta6I8/rnYM5gSdSV2tJ6XbZuEtY= -github.com/bitly/go-hostpool v0.0.0-20171023180738-a3a6125de932/go.mod h1:NOuUCSz6Q9T7+igc/hlvDOUdtWKryOrtFyIVABv/p7k= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I= -github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= -github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c h1:2zRrJWIt/f9c9HhNHAgrRgq0San5gRRUJTBXLkchal0= -github.com/cockroachdb/cockroach-go v0.0.0-20181001143604-e0a95dfd547c/go.mod h1:XGLbWH/ujMcbPbhZq52Nv6UrCghb1yGn//133kEsvDk= -github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07 h1:UHFGPvSxX4C4YBApSPvmUfL8tTvWLj2ryqvT9K4Jcuk= -github.com/cznic/b v0.0.0-20180115125044-35e9bbe41f07/go.mod h1:URriBxXwVq5ijiJ12C7iIZqlA69nTlI+LgI6/pwftG8= -github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f h1:7uSNgsgcarNk4oiN/nNkO0J7KAjlsF5Yv5Gf/tFdHas= -github.com/cznic/fileutil v0.0.0-20180108211300-6a051e75936f/go.mod h1:8S58EK26zhXSxzv7NQFpnliaOQsmDUxvoQO3rt154Vg= -github.com/cznic/golex v0.0.0-20170803123110-4ab7c5e190e4 h1:CVAqftqbj+exlab+8KJQrE+kNIVlQfJt58j4GxCMF1s= -github.com/cznic/golex v0.0.0-20170803123110-4ab7c5e190e4/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc= -github.com/cznic/internal v0.0.0-20180608152220-f44710a21d00 h1:FHpbUtp2K8X53/b4aFNj4my5n+i3x+CQCZWNuHWH/+E= -github.com/cznic/internal v0.0.0-20180608152220-f44710a21d00/go.mod h1:olo7eAdKwJdXxb55TKGLiJ6xt1H0/tiiRCWKVLmtjY4= -github.com/cznic/lldb v1.1.0 h1:AIA+ham6TSJ+XkMe8imQ/g8KPzMUVWAwqUQQdtuMsHs= -github.com/cznic/lldb v1.1.0/go.mod h1:FIZVUmYUVhPwRiPzL8nD/mpFcJ/G7SSXjjXYG4uRI3A= -github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369 h1:XNT/Zf5l++1Pyg08/HV04ppB0gKxAqtZQBRYiYrUuYk= -github.com/cznic/mathutil v0.0.0-20180504122225-ca4c9f2c1369/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM= -github.com/cznic/ql v1.2.0 h1:lcKp95ZtdF0XkWhGnVIXGF8dVD2X+ClS08tglKtf+ak= -github.com/cznic/ql v1.2.0/go.mod h1:FbpzhyZrqr0PVlK6ury+PoW3T0ODUV22OeWIxcaOrSE= -github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65 h1:hxuZop6tSoOi0sxFzoGGYdRqNrPubyaIf9KoBG9tPiE= -github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ= -github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186 h1:0rkFMAbn5KBKNpJyHQ6Prb95vIKanmAe62KxsrN+sqA= -github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc= -github.com/cznic/zappy v0.0.0-20160723133515-2533cb5b45cc h1:YKKpTb2BrXN2GYyGaygIdis1vXbE7SSAG9axGWIMClg= -github.com/cznic/zappy v0.0.0-20160723133515-2533cb5b45cc/go.mod h1:Y1SNZ4dRUOKXshKUbwUapqNncRrho4mkjQebgEHZLj8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dhui/dktest v0.3.0 h1:kwX5a7EkLcjo7VpsPQSYJcKGbXBXdjI9FGjuUj1jn6I= -github.com/dhui/dktest v0.3.0/go.mod h1:cyzIUfGsBEbZ6BT7tnXqAShHSXCZhSNmFl70sZ7c1yc= -github.com/docker/distribution v2.7.0+incompatible h1:neUDAlf3wX6Ml4HdqTrbcOHXtfRN0TFIwt6YFL7N9RU= -github.com/docker/distribution v2.7.0+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v0.7.3-0.20190103212154-2b7e084dc98b/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v0.7.3-0.20190108045446-77df18c24acf h1:2v/98rHzs3v6X0AHtoCH9u+e56SdnpogB1Z2fFe1KqQ= -github.com/docker/docker v0.7.3-0.20190108045446-77df18c24acf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= -github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= -github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk= -github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= -github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= -github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= -github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712 h1:aaQcKT9WumO6JEJcRyTqFVq4XUZiUcKR2/GI31TOcz8= -github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsouza/fake-gcs-server v1.7.0 h1:Un0BXUXrRWYSmYyC1Rqm2e2WJfTPyDy/HGMz31emTi8= -github.com/fsouza/fake-gcs-server v1.7.0/go.mod h1:5XIRs4YvwNbNoz+1JF8j6KLAyDh7RHGAyAK3EP2EsNk= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-sql-driver/mysql v1.4.1 h1:g24URVg0OFbNUTx9qqY1IRZ9D9z3iPyi5zKhQZpNwpA= -github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4 h1:vF83LI8tAakwEwvWZtrIEx7pOySacl2TOxx6eXk4ePo= -github.com/gocql/gocql v0.0.0-20190301043612-f6df8288f9b4/go.mod h1:4Fw1eo5iaEhDUs8XyuhSVCVy52Jq3L+/3GJgYkwc+/0= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4= -github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0 h1:+dTQ8DZQJz0Mb/HjFlkptS1FeQ4cWSnN941F8aEG4SQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= -github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= -github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/googleapis/gax-go/v2 v2.0.4 h1:hU4mGcQI4DaAYW+IbTun+2qEZVFxK0ySjQLTbS0VQKc= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/gorilla/mux v1.7.1 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU= -github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= -github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc= -github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ= -github.com/jackc/pgx v3.2.0+incompatible h1:0Vihzu20St42/UDsvZGdNE6jak7oi/UOeMzwMPHkgFY= -github.com/jackc/pgx v3.2.0+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af h1:pmfjZENx5imkbgOkpRUYLnmbU7UEFbjtDA2hxJ1ichM= -github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 h1:iQTw/8FWTuc7uiaSepXwyf3o52HaUYcV+Tu66S3F5GA= -github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kshvakov/clickhouse v1.3.5 h1:PDTYk9VYgbjPAWry3AoDREeMgOVUFij6bh6IjlloHL0= -github.com/kshvakov/clickhouse v1.3.5/go.mod h1:DMzX7FxRymoNkVgizH0DWAL8Cur7wHLgx3MUnGwJqpE= -github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= -github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mongodb/mongo-go-driver v0.3.0 h1:00tKWMrabkVU1e57/TTP4ZBIfhn/wmjlSiRnIM9d0T8= -github.com/mongodb/mongo-go-driver v0.3.0/go.mod h1:NK/HWDIIZkaYsnYa0hmtP443T5ELr0KDecmIioVuuyU= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8 h1:P48LjvUQpTReR3TQRbxSeSBsMXzfK0uol7eRcr7VBYQ= -github.com/nakagami/firebirdsql v0.0.0-20190310045651-3c02a58cfed8/go.mod h1:86wM1zFnC6/uDBfZGNwB65O+pR2OFi5q/YQaEUid1qA= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= -github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= -github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= -github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= -github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw= -github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3-0.20190127221311-3c4408c8b829/go.mod h1:p2iRAGwDERtqlqzRXnrOVns+ignqQo//hLXqYxZYVNs= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24 h1:pntxY8Ary0t43dCZ5dqY4YTJCObLY1kIXl0uzMv+7DE= -github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/sirupsen/logrus v1.4.1 h1:GL2rEmy6nsikmW0r8opw9JIRScdMF5hA8cOYLH7In1k= -github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51 h1:BP2bjP495BBPaBcS5rmqviTfrOkN5rO5ceKAMRZCRFc= -github.com/tidwall/pretty v0.0.0-20180105212114-65a9db5fad51/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= -github.com/xanzy/go-gitlab v0.15.0 h1:rWtwKTgEnXyNUGrOArN7yyc3THRkpYcKXIXia9abywQ= -github.com/xanzy/go-gitlab v0.15.0/go.mod h1:8zdQa/ri1dfn8eS3Ir1SyfvOKlw7WBJ8DVThkpGiXrs= -github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c h1:u40Z8hqBAAQyv+vATcGgV0YCnDjqSL7/q/JyPhhJSPk= -github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I= -github.com/xdg/stringprep v1.0.0 h1:d9X0esnoa3dFsV0FG35rAT0RIhYFlPq7MiP+DW89La0= -github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y= -gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b h1:7gd+rd8P3bqcn/96gOZa3F5dpJr/vEiDQYlNb/y2uNs= -gitlab.com/nyarla/go-crypt v0.0.0-20160106005555-d9a5dc2b789b/go.mod h1:T3BPAOm2cqquPa0MKWeNkmOM5RQsRhkrwMWonFMN7fE= -go.opencensus.io v0.20.1 h1:pMEjRZ1M4ebWGikflH7nQpV6+Zr88KBMA2XJD3sbijw= -go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= -go.opencensus.io v0.21.0 h1:mU6zScU4U1YAFPHEHYk+3JC4SY7JxgkqS10ZOSyksNg= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734 h1:p/H982KKEjUnLJkM3tt/LemDnOc1GiZL5FCVlORJ5zo= -golang.org/x/crypto v0.0.0-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181108082009-03003ca0c849/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190125091013-d26f9f9a57f3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6 h1:FP8hkuE6yUEaJnK7O2eTuejKWwW+Rhfj80dQ2JcKxCU= -golang.org/x/net v0.0.0-20190424112056-4829fb13d2c6/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20181106182150-f42d05182288/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421 h1:Wo7BWFiOk0QRFMLYMqJGFMd9CgUAcGx7V+qEg/h5IBI= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a h1:tImsplftrFpALCYumobsd0K86vlAs/eXGFms2txfJfA= -golang.org/x/oauth2 v0.0.0-20190402181905-9f3314589c9a/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6 h1:bjcUS9ztw9kFmmIxJInhon/0Is3p+EHBKNgquIzo1OI= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190102155601-82a175fd1598/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190426135247-a129542de9ae h1:mQLHiymj/JXKnnjc62tb7nD5pZLs940/sXJu+Xp3DBA= -golang.org/x/sys v0.0.0-20190426135247-a129542de9ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425222832-ad9eeb80039a h1:jd4PGQGmrzmDZANUzIol3eClsCB/Jp5GmpGWMhi6hnY= -golang.org/x/tools v0.0.0-20190425222832-ad9eeb80039a/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.3.2 h1:iTp+3yyl/KOtxa/d1/JUE0GGSoR6FuW5udver22iwpw= -google.golang.org/api v0.3.2/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= -google.golang.org/api v0.4.0 h1:KKgc1aqhV8wDPbDzlDtpvyjZFY3vjz85FP7p4wcQUyI= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0 h1:KxkO13IPW4Lslp2bz+KHP2E3gtFlrIGNThxkZQ3g+4c= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107 h1:xtNn7qFlagY2mQNFHMSRPjT2RkOV4OXM7P5TVy9xATo= -google.golang.org/genproto v0.0.0-20190404172233-64821d5d2107/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb h1:i1Ppqkc3WQXikh8bXiwHqAN5Rv3/qDCcRk0/Otx73BY= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= -google.golang.org/grpc v1.19.0 h1:cfg4PD8YEdSFnm7qLV4++93WcmhH2nIUhMjhdCvl3j8= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1 h1:Hz2g2wirWK7H0qIIhGIqRGTuMwTE8HEKFnDZZ7lm9NU= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= -gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= -honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/vendor/github.com/gorilla/sessions/go.mod b/vendor/github.com/gorilla/sessions/go.mod deleted file mode 100644 index 9028bcf1c..000000000 --- a/vendor/github.com/gorilla/sessions/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/gorilla/sessions - -require github.com/gorilla/securecookie v1.1.1 diff --git a/vendor/github.com/gorilla/sessions/go.sum b/vendor/github.com/gorilla/sessions/go.sum deleted file mode 100644 index e6a7ed5f3..000000000 --- a/vendor/github.com/gorilla/sessions/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= diff --git a/vendor/github.com/gorilla/websocket/go.mod b/vendor/github.com/gorilla/websocket/go.mod deleted file mode 100644 index 1a7afd502..000000000 --- a/vendor/github.com/gorilla/websocket/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/gorilla/websocket - -go 1.12 diff --git a/vendor/github.com/gorilla/websocket/go.sum b/vendor/github.com/gorilla/websocket/go.sum deleted file mode 100644 index e69de29bb..000000000 diff --git a/vendor/github.com/h2non/filetype/go.mod b/vendor/github.com/h2non/filetype/go.mod deleted file mode 100644 index 765d393f8..000000000 --- a/vendor/github.com/h2non/filetype/go.mod +++ /dev/null @@ -1 +0,0 @@ -module github.com/h2non/filetype diff --git a/vendor/github.com/hashicorp/errwrap/go.mod b/vendor/github.com/hashicorp/errwrap/go.mod deleted file mode 100644 index c9b84022c..000000000 --- a/vendor/github.com/hashicorp/errwrap/go.mod +++ /dev/null @@ -1 +0,0 @@ -module github.com/hashicorp/errwrap diff --git a/vendor/github.com/hashicorp/go-multierror/go.mod b/vendor/github.com/hashicorp/go-multierror/go.mod deleted file mode 100644 index 2534331d5..000000000 --- a/vendor/github.com/hashicorp/go-multierror/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/hashicorp/go-multierror - -require github.com/hashicorp/errwrap v1.0.0 diff --git a/vendor/github.com/hashicorp/go-multierror/go.sum b/vendor/github.com/hashicorp/go-multierror/go.sum deleted file mode 100644 index 85b1f8ff3..000000000 --- a/vendor/github.com/hashicorp/go-multierror/go.sum +++ /dev/null @@ -1,4 +0,0 @@ -github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce h1:prjrVgOk2Yg6w+PflHoszQNLTUh4kaByUcEWM/9uin4= -github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= diff --git a/vendor/github.com/hashicorp/golang-lru/go.mod b/vendor/github.com/hashicorp/golang-lru/go.mod deleted file mode 100644 index 824cb97e8..000000000 --- a/vendor/github.com/hashicorp/golang-lru/go.mod +++ /dev/null @@ -1 +0,0 @@ -module github.com/hashicorp/golang-lru diff --git a/vendor/github.com/hashicorp/hcl/go.mod b/vendor/github.com/hashicorp/hcl/go.mod deleted file mode 100644 index 4debbbe35..000000000 --- a/vendor/github.com/hashicorp/hcl/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/hashicorp/hcl - -require github.com/davecgh/go-spew v1.1.1 diff --git a/vendor/github.com/hashicorp/hcl/go.sum b/vendor/github.com/hashicorp/hcl/go.sum deleted file mode 100644 index b5e2922e8..000000000 --- a/vendor/github.com/hashicorp/hcl/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/vendor/github.com/jmoiron/sqlx/go.mod b/vendor/github.com/jmoiron/sqlx/go.mod deleted file mode 100644 index 66c67561c..000000000 --- a/vendor/github.com/jmoiron/sqlx/go.mod +++ /dev/null @@ -1,7 +0,0 @@ -module github.com/jmoiron/sqlx - -require ( - github.com/go-sql-driver/mysql v1.4.0 - github.com/lib/pq v1.0.0 - github.com/mattn/go-sqlite3 v1.9.0 -) diff --git a/vendor/github.com/jmoiron/sqlx/go.sum b/vendor/github.com/jmoiron/sqlx/go.sum deleted file mode 100644 index a3239ada7..000000000 --- a/vendor/github.com/jmoiron/sqlx/go.sum +++ /dev/null @@ -1,6 +0,0 @@ -github.com/go-sql-driver/mysql v1.4.0 h1:7LxgVwFb2hIQtMm87NdgAVfXjnt4OePseqT1tKx+opk= -github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= -github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= -github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= -github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= -github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= diff --git a/vendor/github.com/josharian/intern/go.mod b/vendor/github.com/josharian/intern/go.mod deleted file mode 100644 index f2262ff0d..000000000 --- a/vendor/github.com/josharian/intern/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/josharian/intern - -go 1.5 diff --git a/vendor/github.com/json-iterator/go/go.mod b/vendor/github.com/json-iterator/go/go.mod deleted file mode 100644 index e05c42ff5..000000000 --- a/vendor/github.com/json-iterator/go/go.mod +++ /dev/null @@ -1,11 +0,0 @@ -module github.com/json-iterator/go - -go 1.12 - -require ( - github.com/davecgh/go-spew v1.1.1 - github.com/google/gofuzz v1.0.0 - github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 - github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 - github.com/stretchr/testify v1.3.0 -) diff --git a/vendor/github.com/json-iterator/go/go.sum b/vendor/github.com/json-iterator/go/go.sum deleted file mode 100644 index d778b5a14..000000000 --- a/vendor/github.com/json-iterator/go/go.sum +++ /dev/null @@ -1,14 +0,0 @@ -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw= -github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= -github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/vendor/github.com/karrick/godirwalk/go.mod b/vendor/github.com/karrick/godirwalk/go.mod deleted file mode 100644 index faf23fd98..000000000 --- a/vendor/github.com/karrick/godirwalk/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/karrick/godirwalk - -go 1.13 diff --git a/vendor/github.com/karrick/godirwalk/go.sum b/vendor/github.com/karrick/godirwalk/go.sum deleted file mode 100644 index e69de29bb..000000000 diff --git a/vendor/github.com/magiconair/properties/go.mod b/vendor/github.com/magiconair/properties/go.mod deleted file mode 100644 index 02a6f8655..000000000 --- a/vendor/github.com/magiconair/properties/go.mod +++ /dev/null @@ -1 +0,0 @@ -module github.com/magiconair/properties diff --git a/vendor/github.com/mailru/easyjson/go.mod b/vendor/github.com/mailru/easyjson/go.mod deleted file mode 100644 index f4945d347..000000000 --- a/vendor/github.com/mailru/easyjson/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module github.com/mailru/easyjson - -go 1.12 - -require github.com/josharian/intern v1.0.0 diff --git a/vendor/github.com/mailru/easyjson/go.sum b/vendor/github.com/mailru/easyjson/go.sum deleted file mode 100644 index cb47297b6..000000000 --- a/vendor/github.com/mailru/easyjson/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= diff --git a/vendor/github.com/markbates/errx/go.mod b/vendor/github.com/markbates/errx/go.mod deleted file mode 100644 index 179083056..000000000 --- a/vendor/github.com/markbates/errx/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/markbates/errx - -go 1.12 diff --git a/vendor/github.com/markbates/errx/go.sum b/vendor/github.com/markbates/errx/go.sum deleted file mode 100644 index e69de29bb..000000000 diff --git a/vendor/github.com/markbates/oncer/go.mod b/vendor/github.com/markbates/oncer/go.mod deleted file mode 100644 index e132fa3f9..000000000 --- a/vendor/github.com/markbates/oncer/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/markbates/oncer - -go 1.12 - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/stretchr/testify v1.3.0 -) diff --git a/vendor/github.com/markbates/oncer/go.sum b/vendor/github.com/markbates/oncer/go.sum deleted file mode 100644 index 4f76e62c1..000000000 --- a/vendor/github.com/markbates/oncer/go.sum +++ /dev/null @@ -1,9 +0,0 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= diff --git a/vendor/github.com/markbates/safe/go.mod b/vendor/github.com/markbates/safe/go.mod deleted file mode 100644 index d02a7b4b3..000000000 --- a/vendor/github.com/markbates/safe/go.mod +++ /dev/null @@ -1,7 +0,0 @@ -module github.com/markbates/safe - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/testify v1.2.2 -) diff --git a/vendor/github.com/markbates/safe/go.sum b/vendor/github.com/markbates/safe/go.sum deleted file mode 100644 index e03ee77d9..000000000 --- a/vendor/github.com/markbates/safe/go.sum +++ /dev/null @@ -1,6 +0,0 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= diff --git a/vendor/github.com/mattn/go-sqlite3/go.mod b/vendor/github.com/mattn/go-sqlite3/go.mod deleted file mode 100644 index 3d0854a0b..000000000 --- a/vendor/github.com/mattn/go-sqlite3/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/mattn/go-sqlite3 - -go 1.12 diff --git a/vendor/github.com/mattn/go-sqlite3/go.sum b/vendor/github.com/mattn/go-sqlite3/go.sum deleted file mode 100644 index e69de29bb..000000000 diff --git a/vendor/github.com/mitchellh/go-homedir/go.mod b/vendor/github.com/mitchellh/go-homedir/go.mod deleted file mode 100644 index 7efa09a04..000000000 --- a/vendor/github.com/mitchellh/go-homedir/go.mod +++ /dev/null @@ -1 +0,0 @@ -module github.com/mitchellh/go-homedir diff --git a/vendor/github.com/mitchellh/mapstructure/go.mod b/vendor/github.com/mitchellh/mapstructure/go.mod deleted file mode 100644 index d2a712562..000000000 --- a/vendor/github.com/mitchellh/mapstructure/go.mod +++ /dev/null @@ -1 +0,0 @@ -module github.com/mitchellh/mapstructure diff --git a/vendor/github.com/rs/cors/go.mod b/vendor/github.com/rs/cors/go.mod deleted file mode 100644 index 0a4c65210..000000000 --- a/vendor/github.com/rs/cors/go.mod +++ /dev/null @@ -1 +0,0 @@ -module github.com/rs/cors diff --git a/vendor/github.com/rs/zerolog/go.mod b/vendor/github.com/rs/zerolog/go.mod deleted file mode 100644 index 8c42ba88f..000000000 --- a/vendor/github.com/rs/zerolog/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module github.com/rs/zerolog - -require ( - github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e - github.com/pkg/errors v0.8.1 - github.com/rs/xid v1.2.1 - github.com/zenazn/goji v0.9.0 - golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74 -) diff --git a/vendor/github.com/rs/zerolog/go.sum b/vendor/github.com/rs/zerolog/go.sum deleted file mode 100644 index b14fd2b66..000000000 --- a/vendor/github.com/rs/zerolog/go.sum +++ /dev/null @@ -1,16 +0,0 @@ -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/rs/xid v1.2.1 h1:mhH9Nq+C1fY2l1XIpgxIiUOfNpRBYH1kKcr+qfKgjRc= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/zenazn/goji v0.9.0 h1:RSQQAbXGArQ0dIDEq+PI6WqN6if+5KHu6x2Cx/GXLTQ= -github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74 h1:4cFkmztxtMslUX2SctSl+blCyXfpzhGOy9LhKAqSMA4= -golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/vendor/github.com/russross/blackfriday/v2/go.mod b/vendor/github.com/russross/blackfriday/v2/go.mod deleted file mode 100644 index 620b74e0a..000000000 --- a/vendor/github.com/russross/blackfriday/v2/go.mod +++ /dev/null @@ -1 +0,0 @@ -module github.com/russross/blackfriday/v2 diff --git a/vendor/github.com/shurcooL/sanitized_anchor_name/go.mod b/vendor/github.com/shurcooL/sanitized_anchor_name/go.mod deleted file mode 100644 index 1e2553475..000000000 --- a/vendor/github.com/shurcooL/sanitized_anchor_name/go.mod +++ /dev/null @@ -1 +0,0 @@ -module github.com/shurcooL/sanitized_anchor_name diff --git a/vendor/github.com/sirupsen/logrus/go.mod b/vendor/github.com/sirupsen/logrus/go.mod deleted file mode 100644 index b3919d5ea..000000000 --- a/vendor/github.com/sirupsen/logrus/go.mod +++ /dev/null @@ -1,10 +0,0 @@ -module github.com/sirupsen/logrus - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/testify v1.2.2 - golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 -) - -go 1.13 diff --git a/vendor/github.com/sirupsen/logrus/go.sum b/vendor/github.com/sirupsen/logrus/go.sum deleted file mode 100644 index 694c18b84..000000000 --- a/vendor/github.com/sirupsen/logrus/go.sum +++ /dev/null @@ -1,8 +0,0 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= diff --git a/vendor/github.com/spf13/afero/go.mod b/vendor/github.com/spf13/afero/go.mod deleted file mode 100644 index 086855099..000000000 --- a/vendor/github.com/spf13/afero/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/spf13/afero - -require golang.org/x/text v0.3.0 diff --git a/vendor/github.com/spf13/afero/go.sum b/vendor/github.com/spf13/afero/go.sum deleted file mode 100644 index 6bad37b2a..000000000 --- a/vendor/github.com/spf13/afero/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/vendor/github.com/spf13/cast/go.mod b/vendor/github.com/spf13/cast/go.mod deleted file mode 100644 index c1c0232dd..000000000 --- a/vendor/github.com/spf13/cast/go.mod +++ /dev/null @@ -1,7 +0,0 @@ -module github.com/spf13/cast - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/testify v1.2.2 -) diff --git a/vendor/github.com/spf13/cast/go.sum b/vendor/github.com/spf13/cast/go.sum deleted file mode 100644 index e03ee77d9..000000000 --- a/vendor/github.com/spf13/cast/go.sum +++ /dev/null @@ -1,6 +0,0 @@ -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= diff --git a/vendor/github.com/spf13/cobra/go.mod b/vendor/github.com/spf13/cobra/go.mod deleted file mode 100644 index dea1030ba..000000000 --- a/vendor/github.com/spf13/cobra/go.mod +++ /dev/null @@ -1,12 +0,0 @@ -module github.com/spf13/cobra - -go 1.12 - -require ( - github.com/cpuguy83/go-md2man/v2 v2.0.0 - github.com/inconshreveable/mousetrap v1.0.0 - github.com/mitchellh/go-homedir v1.1.0 - github.com/spf13/pflag v1.0.3 - github.com/spf13/viper v1.4.0 - gopkg.in/yaml.v2 v2.2.2 -) diff --git a/vendor/github.com/spf13/cobra/go.sum b/vendor/github.com/spf13/cobra/go.sum deleted file mode 100644 index 3aaa2ac0f..000000000 --- a/vendor/github.com/spf13/cobra/go.sum +++ /dev/null @@ -1,149 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/vendor/github.com/spf13/jwalterweatherman/go.mod b/vendor/github.com/spf13/jwalterweatherman/go.mod deleted file mode 100644 index bce549c04..000000000 --- a/vendor/github.com/spf13/jwalterweatherman/go.mod +++ /dev/null @@ -1 +0,0 @@ -module github.com/spf13/jwalterweatherman diff --git a/vendor/github.com/spf13/viper/go.mod b/vendor/github.com/spf13/viper/go.mod deleted file mode 100644 index 7d108dcc2..000000000 --- a/vendor/github.com/spf13/viper/go.mod +++ /dev/null @@ -1,40 +0,0 @@ -module github.com/spf13/viper - -go 1.12 - -require ( - github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c - github.com/coreos/bbolt v1.3.2 // indirect - github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e // indirect - github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect - github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect - github.com/fsnotify/fsnotify v1.4.7 - github.com/gogo/protobuf v1.2.1 // indirect - github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef // indirect - github.com/gorilla/websocket v1.4.2 // indirect - github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 // indirect - github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect - github.com/grpc-ecosystem/grpc-gateway v1.9.0 // indirect - github.com/hashicorp/hcl v1.0.0 - github.com/jonboulle/clockwork v0.1.0 // indirect - github.com/magiconair/properties v1.8.1 - github.com/mitchellh/mapstructure v1.1.2 - github.com/pelletier/go-toml v1.2.0 - github.com/prometheus/client_golang v0.9.3 // indirect - github.com/smartystreets/goconvey v1.6.4 // indirect - github.com/soheilhy/cmux v0.1.4 // indirect - github.com/spf13/afero v1.1.2 - github.com/spf13/cast v1.3.0 - github.com/spf13/jwalterweatherman v1.0.0 - github.com/spf13/pflag v1.0.3 - github.com/stretchr/testify v1.3.0 - github.com/subosito/gotenv v1.2.0 - github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 // indirect - github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect - go.etcd.io/bbolt v1.3.2 // indirect - go.uber.org/atomic v1.4.0 // indirect - go.uber.org/multierr v1.1.0 // indirect - go.uber.org/zap v1.10.0 // indirect - gopkg.in/ini.v1 v1.51.0 - gopkg.in/yaml.v2 v2.2.4 -) diff --git a/vendor/github.com/spf13/viper/go.sum b/vendor/github.com/spf13/viper/go.sum deleted file mode 100644 index 463aa7dbf..000000000 --- a/vendor/github.com/spf13/viper/go.sum +++ /dev/null @@ -1,388 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3 h1:AVXDdKsrtX33oR9fbCMu/+c1o8Ofjq6Ku/MInaLVg5Y= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go/bigquery v1.0.1 h1:hL+ycaJpVE9M7nLoiXb/Pn10ENE2u+oddxbD8uu0ZVU= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/datastore v1.0.0 h1:Kt+gOPPp2LEPWp8CSfxhsM8ik9CcyE/gYu+0r+RnZvM= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/firestore v1.1.0 h1:9x7Bx0A9R5/M9jibeJeZWqjeVEIxYW9fZYqB9a70/bY= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/pubsub v1.0.1 h1:W9tAK3E57P75u0XLLR82LZyw8VpAnhmyTOxW9qzmyj8= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/storage v1.0.0 h1:VV2nUM3wwLLGh9lSABFgZMjInyUbJeaRSE64WuAIQ+4= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0 h1:HWo1m869IqiPhD389kmkxeTalrjNbbJTC8LXupb+sl0= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c h1:+0HFd5KSZ/mm3JmhmrDukiId5iR6w4+BdFtfSy4yWIc= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1 h1:/s5zKNz0uPFCZ5hddgPdo2TK2TVrUNMn0OOX8/aZMTE= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef h1:veQD95Isof8w9/WXiA+pa3tz3fJXkt5B7QaRBrM62gk= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0 h1:0udJVsspx3VBr5FwtLhQQtuAsVc79tTq0ocGIPAU6qo= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0 h1:Iju5GlWwrvL6UBg4zJJt3btmonfrMlCDdsejg4CZE7c= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0 h1:bM6ZAFZmc/wPFaRDi0d5L7hGEZEx/2u+Tmr2evNHDiI= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1 h1:LnuDWGNsoajlhGyHJvuWW6FVqRl8JOTPqS6CPTsYjhY= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/jonboulle/clockwork v0.1.0 h1:VKV+ZcuP6l3yW9doeqz6ziZGgcynBVQO+obU0+0hcPo= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v1.1.6 h1:MrUvLMLTMxbqFJ9kzlvat/rYZqZnW3u4wkLzWTaFwKs= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024 h1:rBMNdlhTLzJjJSDIjNEXX1Pz3Hmwmz91v+zycvx9PJc= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.0.14 h1:9jZdLNd/P4+SfEJ0TNyxYpsK8N4GtfylBLqtbYN1sbA= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0 h1:vKb8ShqSby24Yrqr/yDYkuFz8d0WUjys40rvnGC8aR0= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3 h1:9iH4JKXLzFbOAdtqv/a+j8aewx2Y8lAjAydhbaScPF8= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90 h1:S/YWwWx/RA8rT8tKFRuGUZhuA90OyIBpPCXkcbwU8DE= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0 h1:7etb9YClo3a6HjLzfl6rIQaU+FDfi0VSX39io3aQ+DM= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084 h1:sofwID9zm4tzrgykg80hfFph1mryUeLRsUfoocVVmRY= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/sirupsen/logrus v1.2.0 h1:juTguoYk5qI21pwyTXY3B3Y5cOTH3ZUyZCg1v/mihuo= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4 h1:0HKaf1o97UwFjHH9o5XsHUOF+tqmdA7KEzXLpiyaw0E= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5 h1:LnC5Kc/wtumK+WB441p7ynQJzVuNRJiqddSIE3IlSEQ= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.uber.org/atomic v1.4.0 h1:cxzIVoETapQEqDhQu3QfnvXAV4AlzcvUCxkVUFw3+EU= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0 h1:HoEmRHQPVSqub6w2z2d2EOVs2fjyFRGyofhKuyDq0QI= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0 h1:ORx85nbTijNz8ljznvCMR1ZBIPKFn3jQrag10X2AsuM= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5 h1:58fnuSXlxZmFdJyvtTFVmVhcMLU6v5fEb/ok4wyqtNU= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859 h1:R/3boaszxrf1GEUWTVDzSKVwLmSJpwZ1yqXm8j0v2QI= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 h1:SVwTIAaPC2U/AvvLNZ2a7OVsmBpC8L5BlwK1whH3hm0= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58 h1:8gQV6CLnAEikrhgkHFbMAEhagSSnXWGV915qUMm9mrU= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4 h1:SvFZT6jyqRaOeXpc5h/JSfZenJ2O330aBsf7JfSUXmQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384 h1:TFlARGu6Czu1z7q93HTxcP1P+/ZFC/IKythI5RzrnRg= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc h1:NCy3Ohtk6Iny5V/reW2Ktypo4zIpWBdRJ1uFMjBxdg8= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0 h1:Q3Ui3V3/CVinFWFiW39Iw0kMuVrRzYX0wN6OPFp0lTA= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1 h1:QzqyMA1tlu6CgqCDUtU9V+ZKhLFT2dkJuANu5QaxI3I= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a h1:Ob5/580gVHBJZgXnff1cZDbG+xLtMVE5mDRTe+nIsX4= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/vendor/github.com/tidwall/gjson/go.mod b/vendor/github.com/tidwall/gjson/go.mod deleted file mode 100644 index 9853a3096..000000000 --- a/vendor/github.com/tidwall/gjson/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module github.com/tidwall/gjson - -go 1.12 - -require ( - github.com/tidwall/match v1.0.3 - github.com/tidwall/pretty v1.1.0 -) diff --git a/vendor/github.com/tidwall/gjson/go.sum b/vendor/github.com/tidwall/gjson/go.sum deleted file mode 100644 index 739d13276..000000000 --- a/vendor/github.com/tidwall/gjson/go.sum +++ /dev/null @@ -1,4 +0,0 @@ -github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE= -github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= -github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8= -github.com/tidwall/pretty v1.1.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= diff --git a/vendor/github.com/tidwall/match/go.mod b/vendor/github.com/tidwall/match/go.mod deleted file mode 100644 index df19b5f77..000000000 --- a/vendor/github.com/tidwall/match/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/tidwall/match - -go 1.15 diff --git a/vendor/github.com/tidwall/pretty/go.mod b/vendor/github.com/tidwall/pretty/go.mod deleted file mode 100644 index 6106735a3..000000000 --- a/vendor/github.com/tidwall/pretty/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module github.com/tidwall/pretty - -go 1.16 diff --git a/vendor/github.com/urfave/cli/v2/go.mod b/vendor/github.com/urfave/cli/v2/go.mod deleted file mode 100644 index c38d41c14..000000000 --- a/vendor/github.com/urfave/cli/v2/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module github.com/urfave/cli/v2 - -go 1.11 - -require ( - github.com/BurntSushi/toml v0.3.1 - github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d - gopkg.in/yaml.v2 v2.2.2 -) diff --git a/vendor/github.com/urfave/cli/v2/go.sum b/vendor/github.com/urfave/cli/v2/go.sum deleted file mode 100644 index ef121ff5d..000000000 --- a/vendor/github.com/urfave/cli/v2/go.sum +++ /dev/null @@ -1,14 +0,0 @@ -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= -github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/vektah/dataloaden/go.mod b/vendor/github.com/vektah/dataloaden/go.mod deleted file mode 100644 index ba56ca03d..000000000 --- a/vendor/github.com/vektah/dataloaden/go.mod +++ /dev/null @@ -1,9 +0,0 @@ -module github.com/vektah/dataloaden - -require ( - github.com/davecgh/go-spew v1.1.0 // indirect - github.com/pkg/errors v0.8.1 - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/testify v1.2.1 - golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd -) diff --git a/vendor/github.com/vektah/dataloaden/go.sum b/vendor/github.com/vektah/dataloaden/go.sum deleted file mode 100644 index a350afb0a..000000000 --- a/vendor/github.com/vektah/dataloaden/go.sum +++ /dev/null @@ -1,15 +0,0 @@ -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.2.1 h1:52QO5WkIUcHGIR7EnGagH88x1bUzqGXTC5/1bDTUQ7U= -github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd h1:oMEQDWVXVNpceQoVd1JN3CQ7LYJJzs5qWqZIUcxXHHw= -golang.org/x/tools v0.0.0-20190515012406-7d7faa4812bd/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= diff --git a/vendor/github.com/vektah/gqlparser/v2/go.mod b/vendor/github.com/vektah/gqlparser/v2/go.mod deleted file mode 100644 index afdb99d8c..000000000 --- a/vendor/github.com/vektah/gqlparser/v2/go.mod +++ /dev/null @@ -1,12 +0,0 @@ -module github.com/vektah/gqlparser/v2 - -go 1.12 - -require ( - github.com/agnivade/levenshtein v1.0.1 - github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 - github.com/sergi/go-diff v1.1.0 // indirect - github.com/stretchr/testify v1.4.0 - golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6 - gopkg.in/yaml.v2 v2.2.4 -) diff --git a/vendor/github.com/vektah/gqlparser/v2/go.sum b/vendor/github.com/vektah/gqlparser/v2/go.sum deleted file mode 100644 index 38f4ee3d6..000000000 --- a/vendor/github.com/vektah/gqlparser/v2/go.sum +++ /dev/null @@ -1,30 +0,0 @@ -github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ= -github.com/agnivade/levenshtein v1.0.1/go.mod h1:CURSv5d9Uaml+FovSIICkLbAUZ9S4RqaHDIsdSBg7lM= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8= -github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6 h1:iZgcI2DDp6zW5v9Z/5+f0NuqoxNdmzg4hivjk2WLXpY= -golang.org/x/tools v0.0.0-20190125232054-d66bd3c5d5a6/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/vendor/github.com/vektra/mockery/v2/go.mod b/vendor/github.com/vektra/mockery/v2/go.mod deleted file mode 100644 index fd55e7e1c..000000000 --- a/vendor/github.com/vektra/mockery/v2/go.mod +++ /dev/null @@ -1,15 +0,0 @@ -module github.com/vektra/mockery/v2 - -go 1.14 - -require ( - github.com/mitchellh/go-homedir v1.1.0 - github.com/pkg/errors v0.8.1 - github.com/rs/zerolog v1.18.0 - github.com/spf13/cobra v1.0.0 - github.com/spf13/viper v1.7.0 - github.com/stretchr/testify v1.3.0 - golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 - golang.org/x/tools v0.0.0-20200323144430-8dcfad9e016e - gopkg.in/yaml.v2 v2.2.4 -) diff --git a/vendor/github.com/vektra/mockery/v2/go.sum b/vendor/github.com/vektra/mockery/v2/go.sum deleted file mode 100644 index 369441b6a..000000000 --- a/vendor/github.com/vektra/mockery/v2/go.sum +++ /dev/null @@ -1,343 +0,0 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= -github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= -github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= -github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= -github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= -github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= -github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4= -github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/zerolog v1.18.0 h1:CbAm3kP2Tptby1i9sYy2MGRg0uxIN9cyDb59Ys7W8z8= -github.com/rs/zerolog v1.18.0/go.mod h1:9nvC1axdVrAHcu/s9taAVfBuIdTZLVQmKQyvrUjF5+I= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v1.0.0 h1:6m/oheQuQ13N9ks4hubMG6BnvwOeaJrqSPLahSnczz8= -github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= -github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= -github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= -github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= -github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= -golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= -golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190412213103-97732733099d h1:+R4KGOnez64A81RvjARKc4UT5/tI9ujCIVX+P5KiHuI= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0 h1:HyfiK1WMnHj5FXFXatD+Qs1A/xC2Run6RzeW1SyHxpc= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190828213141-aed303cbaa74/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200323144430-8dcfad9e016e h1:ssd5ulOvVWlh4kDSUF2SqzmMeWfjmwDXM+uGw/aQjRE= -golang.org/x/tools v0.0.0-20200323144430-8dcfad9e016e/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4 h1:/eiJrUcujPVeJ3xlSWaiNi3uSVmDGBK1pDHUHAnao1I= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= diff --git a/vendor/golang.org/x/term/go.mod b/vendor/golang.org/x/term/go.mod deleted file mode 100644 index edf0e5b1d..000000000 --- a/vendor/golang.org/x/term/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module golang.org/x/term - -go 1.17 - -require golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 diff --git a/vendor/golang.org/x/term/go.sum b/vendor/golang.org/x/term/go.sum deleted file mode 100644 index ff132135e..000000000 --- a/vendor/golang.org/x/term/go.sum +++ /dev/null @@ -1,2 +0,0 @@ -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= -golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= diff --git a/vendor/golang.org/x/xerrors/go.mod b/vendor/golang.org/x/xerrors/go.mod deleted file mode 100644 index 870d4f612..000000000 --- a/vendor/golang.org/x/xerrors/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module golang.org/x/xerrors - -go 1.11 diff --git a/vendor/gopkg.in/yaml.v2/go.mod b/vendor/gopkg.in/yaml.v2/go.mod deleted file mode 100644 index 1934e8769..000000000 --- a/vendor/gopkg.in/yaml.v2/go.mod +++ /dev/null @@ -1,5 +0,0 @@ -module "gopkg.in/yaml.v2" - -require ( - "gopkg.in/check.v1" v0.0.0-20161208181325-20d25e280405 -) diff --git a/vendor/modules.txt b/vendor/modules.txt index 361218731..687cf951c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,4 +1,5 @@ # github.com/99designs/gqlgen v0.12.2 +## explicit; go 1.12 github.com/99designs/gqlgen github.com/99designs/gqlgen/api github.com/99designs/gqlgen/cmd @@ -25,6 +26,7 @@ github.com/99designs/gqlgen/plugin/modelgen github.com/99designs/gqlgen/plugin/resolvergen github.com/99designs/gqlgen/plugin/servergen # github.com/Yamashou/gqlgenc v0.0.0-20200902035953-4dbef3551953 +## explicit; go 1.14 github.com/Yamashou/gqlgenc github.com/Yamashou/gqlgenc/client github.com/Yamashou/gqlgenc/clientgen @@ -33,18 +35,23 @@ github.com/Yamashou/gqlgenc/generator github.com/Yamashou/gqlgenc/graphqljson github.com/Yamashou/gqlgenc/introspection # github.com/agnivade/levenshtein v1.1.0 +## explicit; go 1.13 github.com/agnivade/levenshtein # github.com/anacrolix/dms v1.2.2 +## explicit; go 1.12 github.com/anacrolix/dms/dlna github.com/anacrolix/dms/soap github.com/anacrolix/dms/ssdp github.com/anacrolix/dms/upnp github.com/anacrolix/dms/upnpav # github.com/antchfx/htmlquery v1.2.3 +## explicit; go 1.14 github.com/antchfx/htmlquery # github.com/antchfx/xpath v1.1.6 +## explicit github.com/antchfx/xpath # github.com/chromedp/cdproto v0.0.0-20210622022015-fe1827b46b84 +## explicit; go 1.14 github.com/chromedp/cdproto github.com/chromedp/cdproto/accessibility github.com/chromedp/cdproto/animation @@ -92,36 +99,49 @@ github.com/chromedp/cdproto/tracing github.com/chromedp/cdproto/webaudio github.com/chromedp/cdproto/webauthn # github.com/chromedp/chromedp v0.7.3 +## explicit; go 1.16 github.com/chromedp/chromedp github.com/chromedp/chromedp/device github.com/chromedp/chromedp/kb # github.com/chromedp/sysutil v1.0.0 +## explicit; go 1.15 github.com/chromedp/sysutil # github.com/corona10/goimagehash v1.0.3 +## explicit github.com/corona10/goimagehash github.com/corona10/goimagehash/etcs github.com/corona10/goimagehash/transforms # github.com/cpuguy83/go-md2man/v2 v2.0.0 +## explicit; go 1.12 github.com/cpuguy83/go-md2man/v2/md2man # github.com/davecgh/go-spew v1.1.1 +## explicit github.com/davecgh/go-spew/spew # github.com/dgrijalva/jwt-go v3.2.0+incompatible +## explicit github.com/dgrijalva/jwt-go # github.com/disintegration/imaging v1.6.0 +## explicit github.com/disintegration/imaging # github.com/fsnotify/fsnotify v1.4.7 +## explicit github.com/fsnotify/fsnotify # github.com/fvbommel/sortorder v1.0.2 +## explicit; go 1.13 github.com/fvbommel/sortorder # github.com/go-chi/chi v4.0.2+incompatible +## explicit github.com/go-chi/chi github.com/go-chi/chi/middleware # github.com/gobuffalo/logger v1.0.4 +## explicit; go 1.13 github.com/gobuffalo/logger # github.com/gobuffalo/packd v1.0.0 +## explicit; go 1.13 github.com/gobuffalo/packd github.com/gobuffalo/packd/internal/takeon/github.com/markbates/errx # github.com/gobuffalo/packr/v2 v2.8.1 +## explicit; go 1.13 github.com/gobuffalo/packr/v2 github.com/gobuffalo/packr/v2/file github.com/gobuffalo/packr/v2/file/resolver @@ -129,42 +149,55 @@ github.com/gobuffalo/packr/v2/file/resolver/encoding/hex github.com/gobuffalo/packr/v2/jam/parser github.com/gobuffalo/packr/v2/plog # github.com/gobwas/httphead v0.1.0 +## explicit; go 1.15 github.com/gobwas/httphead # github.com/gobwas/pool v0.2.1 +## explicit github.com/gobwas/pool github.com/gobwas/pool/internal/pmath github.com/gobwas/pool/pbufio github.com/gobwas/pool/pbytes # github.com/gobwas/ws v1.1.0-rc.5 +## explicit; go 1.15 github.com/gobwas/ws github.com/gobwas/ws/wsutil # github.com/golang-migrate/migrate/v4 v4.3.1 +## explicit github.com/golang-migrate/migrate/v4 github.com/golang-migrate/migrate/v4/database github.com/golang-migrate/migrate/v4/database/sqlite3 github.com/golang-migrate/migrate/v4/source github.com/golang-migrate/migrate/v4/source/file # github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e +## explicit github.com/golang/groupcache/lru # github.com/gorilla/securecookie v1.1.1 +## explicit github.com/gorilla/securecookie # github.com/gorilla/sessions v1.2.0 +## explicit github.com/gorilla/sessions # github.com/gorilla/websocket v1.4.2 +## explicit; go 1.12 github.com/gorilla/websocket # github.com/h2non/filetype v1.0.8 +## explicit github.com/h2non/filetype github.com/h2non/filetype/matchers github.com/h2non/filetype/matchers/isobmff github.com/h2non/filetype/types # github.com/hashicorp/errwrap v1.0.0 +## explicit github.com/hashicorp/errwrap # github.com/hashicorp/go-multierror v1.0.0 +## explicit github.com/hashicorp/go-multierror # github.com/hashicorp/golang-lru v0.5.1 +## explicit github.com/hashicorp/golang-lru github.com/hashicorp/golang-lru/simplelru # github.com/hashicorp/hcl v1.0.0 +## explicit github.com/hashicorp/hcl github.com/hashicorp/hcl/hcl/ast github.com/hashicorp/hcl/hcl/parser @@ -176,57 +209,81 @@ github.com/hashicorp/hcl/json/parser github.com/hashicorp/hcl/json/scanner github.com/hashicorp/hcl/json/token # github.com/inconshreveable/mousetrap v1.0.0 +## explicit github.com/inconshreveable/mousetrap # github.com/jinzhu/copier v0.0.0-20190924061706-b57f9002281a +## explicit github.com/jinzhu/copier # github.com/jmoiron/sqlx v1.2.0 +## explicit github.com/jmoiron/sqlx github.com/jmoiron/sqlx/reflectx # github.com/josharian/intern v1.0.0 +## explicit; go 1.5 github.com/josharian/intern # github.com/json-iterator/go v1.1.9 +## explicit; go 1.12 github.com/json-iterator/go # github.com/karrick/godirwalk v1.16.1 +## explicit; go 1.13 github.com/karrick/godirwalk # github.com/magiconair/properties v1.8.1 +## explicit github.com/magiconair/properties # github.com/mailru/easyjson v0.7.7 +## explicit; go 1.12 github.com/mailru/easyjson github.com/mailru/easyjson/buffer github.com/mailru/easyjson/jlexer github.com/mailru/easyjson/jwriter # github.com/markbates/errx v1.1.0 +## explicit; go 1.12 github.com/markbates/errx # github.com/markbates/oncer v1.0.0 +## explicit; go 1.12 github.com/markbates/oncer # github.com/markbates/safe v1.0.1 +## explicit github.com/markbates/safe # github.com/matryer/moq v0.0.0-20200106131100-75d0ddfc0007 +## explicit github.com/matryer/moq github.com/matryer/moq/pkg/moq # github.com/mattn/go-sqlite3 v1.14.6 +## explicit; go 1.12 github.com/mattn/go-sqlite3 # github.com/mitchellh/go-homedir v1.1.0 +## explicit github.com/mitchellh/go-homedir # github.com/mitchellh/mapstructure v1.1.2 +## explicit github.com/mitchellh/mapstructure # github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd +## explicit github.com/modern-go/concurrent # github.com/modern-go/reflect2 v1.0.1 +## explicit github.com/modern-go/reflect2 # github.com/natefinch/pie v0.0.0-20170715172608-9a0d72014007 +## explicit github.com/natefinch/pie # github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 +## explicit github.com/nfnt/resize # github.com/pelletier/go-toml v1.2.0 +## explicit github.com/pelletier/go-toml # github.com/pkg/errors v0.9.1 +## explicit github.com/pkg/errors # github.com/pmezard/go-difflib v1.0.0 +## explicit github.com/pmezard/go-difflib/difflib # github.com/remeh/sizedwaitgroup v1.0.0 +## explicit github.com/remeh/sizedwaitgroup # github.com/robertkrimen/otto v0.0.0-20200922221731-ef014fd054ac +## explicit github.com/robertkrimen/otto github.com/robertkrimen/otto/ast github.com/robertkrimen/otto/dbg @@ -235,54 +292,75 @@ github.com/robertkrimen/otto/parser github.com/robertkrimen/otto/registry github.com/robertkrimen/otto/token # github.com/rs/cors v1.6.0 +## explicit github.com/rs/cors # github.com/rs/zerolog v1.18.0 +## explicit github.com/rs/zerolog github.com/rs/zerolog/internal/cbor github.com/rs/zerolog/internal/json github.com/rs/zerolog/log # github.com/russross/blackfriday/v2 v2.0.1 +## explicit github.com/russross/blackfriday/v2 # github.com/shurcooL/graphql v0.0.0-20181231061246-d48a9a75455f +## explicit github.com/shurcooL/graphql github.com/shurcooL/graphql/ident github.com/shurcooL/graphql/internal/jsonutil # github.com/shurcooL/sanitized_anchor_name v1.0.0 +## explicit github.com/shurcooL/sanitized_anchor_name # github.com/sirupsen/logrus v1.8.1 +## explicit; go 1.13 github.com/sirupsen/logrus # github.com/spf13/afero v1.2.0 +## explicit github.com/spf13/afero github.com/spf13/afero/mem # github.com/spf13/cast v1.3.0 +## explicit github.com/spf13/cast # github.com/spf13/cobra v1.0.0 +## explicit; go 1.12 github.com/spf13/cobra # github.com/spf13/jwalterweatherman v1.0.0 +## explicit github.com/spf13/jwalterweatherman # github.com/spf13/pflag v1.0.3 +## explicit github.com/spf13/pflag # github.com/spf13/viper v1.7.0 +## explicit; go 1.12 github.com/spf13/viper # github.com/stretchr/objx v0.1.1 +## explicit github.com/stretchr/objx # github.com/stretchr/testify v1.5.1 +## explicit; go 1.13 github.com/stretchr/testify/assert github.com/stretchr/testify/mock # github.com/subosito/gotenv v1.2.0 +## explicit github.com/subosito/gotenv # github.com/tidwall/gjson v1.8.1 +## explicit; go 1.12 github.com/tidwall/gjson # github.com/tidwall/match v1.0.3 +## explicit; go 1.15 github.com/tidwall/match # github.com/tidwall/pretty v1.2.0 +## explicit; go 1.16 github.com/tidwall/pretty # github.com/urfave/cli/v2 v2.1.1 +## explicit; go 1.11 github.com/urfave/cli/v2 # github.com/vektah/dataloaden v0.2.1-0.20190515034641-a19b9a6e7c9e +## explicit github.com/vektah/dataloaden github.com/vektah/dataloaden/pkg/generator # github.com/vektah/gqlparser/v2 v2.0.1 +## explicit; go 1.12 github.com/vektah/gqlparser/v2 github.com/vektah/gqlparser/v2/ast github.com/vektah/gqlparser/v2/formatter @@ -292,16 +370,19 @@ github.com/vektah/gqlparser/v2/parser github.com/vektah/gqlparser/v2/validator github.com/vektah/gqlparser/v2/validator/rules # github.com/vektra/mockery/v2 v2.2.1 +## explicit; go 1.14 github.com/vektra/mockery/v2 github.com/vektra/mockery/v2/cmd github.com/vektra/mockery/v2/pkg github.com/vektra/mockery/v2/pkg/config github.com/vektra/mockery/v2/pkg/logging # golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97 +## explicit; go 1.17 golang.org/x/crypto/bcrypt golang.org/x/crypto/blowfish golang.org/x/crypto/ssh/terminal # golang.org/x/image v0.0.0-20210220032944-ac19c3e999fb +## explicit; go 1.12 golang.org/x/image/bmp golang.org/x/image/ccitt golang.org/x/image/riff @@ -311,9 +392,11 @@ golang.org/x/image/vp8 golang.org/x/image/vp8l golang.org/x/image/webp # golang.org/x/mod v0.3.0 +## explicit; go 1.12 golang.org/x/mod/module golang.org/x/mod/semver # golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 +## explicit; go 1.11 golang.org/x/net/bpf golang.org/x/net/context/ctxhttp golang.org/x/net/html @@ -324,14 +407,17 @@ golang.org/x/net/internal/socket golang.org/x/net/ipv4 golang.org/x/net/publicsuffix # golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c +## explicit; go 1.17 golang.org/x/sys/cpu golang.org/x/sys/internal/unsafeheader golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows # golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b +## explicit; go 1.17 golang.org/x/term # golang.org/x/text v0.3.3 +## explicit; go 1.11 golang.org/x/text/encoding golang.org/x/text/encoding/charmap golang.org/x/text/encoding/htmlindex @@ -351,6 +437,7 @@ golang.org/x/text/runes golang.org/x/text/transform golang.org/x/text/unicode/norm # golang.org/x/tools v0.0.0-20200915031644-64986481280e +## explicit; go 1.12 golang.org/x/tools/go/ast/astutil golang.org/x/tools/go/gcexportdata golang.org/x/tools/go/internal/gcimporter @@ -368,12 +455,17 @@ golang.org/x/tools/internal/imports golang.org/x/tools/internal/packagesinternal golang.org/x/tools/internal/typesinternal # golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 +## explicit; go 1.11 golang.org/x/xerrors golang.org/x/xerrors/internal # gopkg.in/ini.v1 v1.51.0 +## explicit gopkg.in/ini.v1 # gopkg.in/sourcemap.v1 v1.0.5 +## explicit gopkg.in/sourcemap.v1 gopkg.in/sourcemap.v1/base64vlq # gopkg.in/yaml.v2 v2.3.0 +## explicit gopkg.in/yaml.v2 +# git.apache.org/thrift.git => github.com/apache/thrift v0.0.0-20180902110319-2566ecd5d999 From 4b00d24248c89eef4debdea04ef713c5b0ec33c2 Mon Sep 17 00:00:00 2001 From: SmallCoccinelle <89733524+SmallCoccinelle@users.noreply.github.com> Date: Thu, 9 Sep 2021 06:10:08 +0200 Subject: [PATCH 13/86] Remove unused (#1709) * Remove stuff which isn't being used Some fields, functions and structs aren't in use by the project. Remove them for janitorial reasons. * Remove more unused code All of these functions are currently not in use. Clean up the code by removal, since the version control has the code if need be. * Remove unused functions There's a large set of unused functions and variables in the code base. Remove these, so it clearer what code to support going forward. Dead code has been eliminated. Where applicable, comment const-sections in tests, so reserved identifiers are still known. * Fix use-def of tsURL The first def of tsURL doesn't matter because there's no use before we hit the 2nd def. * Remove dead code assignment Setting logFile = "" is effectively dead code, because there's no use of it later. * Comment out found The variable 'found' is dead in the function (because no post-process action is following it). Comment it for now. * Comment dead code in tests These might provide hints as to what isn't covered at the moment. * Dead code removal In the case of constants where iota is involved, move the iota so it matches the current key values. This avoids problems with persistently stored key IDs. --- pkg/api/context_keys.go | 4 +- pkg/api/resolver_subscription_job.go | 20 ------ pkg/api/routes_scene.go | 3 +- pkg/api/server.go | 10 --- pkg/api/session.go | 5 -- pkg/dlna/dms.go | 1 - pkg/ffmpeg/hls.go | 3 +- pkg/gallery/export_test.go | 30 +-------- pkg/gallery/import_test.go | 4 +- pkg/image/export_test.go | 49 +++----------- pkg/image/import_test.go | 10 ++- pkg/logger/logger.go | 1 - pkg/manager/filename_parser.go | 26 -------- pkg/manager/jsonschema/utils.go | 3 - pkg/manager/task_import.go | 95 ---------------------------- pkg/scene/export_test.go | 26 ++------ pkg/scene/import_test.go | 4 +- pkg/scraper/action.go | 13 ---- pkg/scraper/mapped.go | 2 +- pkg/scraper/url.go | 11 ---- pkg/session/session.go | 13 ---- pkg/sqlite/filter.go | 8 --- pkg/sqlite/gallery.go | 11 ---- pkg/sqlite/scraped_item.go | 8 --- pkg/sqlite/sql.go | 33 ---------- pkg/utils/byterange.go | 8 --- 26 files changed, 28 insertions(+), 373 deletions(-) diff --git a/pkg/api/context_keys.go b/pkg/api/context_keys.go index 839464af9..8731f75c3 100644 --- a/pkg/api/context_keys.go +++ b/pkg/api/context_keys.go @@ -5,8 +5,8 @@ package api type key int const ( - galleryKey key = iota - performerKey + // galleryKey key = 0 + performerKey key = iota + 1 sceneKey studioKey movieKey diff --git a/pkg/api/resolver_subscription_job.go b/pkg/api/resolver_subscription_job.go index c7b3176c0..2ee28df96 100644 --- a/pkg/api/resolver_subscription_job.go +++ b/pkg/api/resolver_subscription_job.go @@ -2,32 +2,12 @@ package api import ( "context" - "time" "github.com/stashapp/stash/pkg/job" "github.com/stashapp/stash/pkg/manager" "github.com/stashapp/stash/pkg/models" ) -type throttledUpdate struct { - id int - pendingUpdate *job.Job - lastUpdate time.Time - broadcastTimer *time.Timer - killTimer *time.Timer -} - -func (tu *throttledUpdate) broadcast(output chan *models.JobStatusUpdate) { - tu.lastUpdate = time.Now() - output <- &models.JobStatusUpdate{ - Type: models.JobStatusUpdateTypeUpdate, - Job: jobToJobModel(*tu.pendingUpdate), - } - - tu.broadcastTimer = nil - tu.pendingUpdate = nil -} - func makeJobStatusUpdate(t models.JobStatusUpdateType, j job.Job) *models.JobStatusUpdate { return &models.JobStatusUpdate{ Type: t, diff --git a/pkg/api/routes_scene.go b/pkg/api/routes_scene.go index d7bb1d888..0bc48f66f 100644 --- a/pkg/api/routes_scene.go +++ b/pkg/api/routes_scene.go @@ -16,8 +16,7 @@ import ( ) type sceneRoutes struct { - txnManager models.TransactionManager - sceneServer manager.SceneServer + txnManager models.TransactionManager } func (rs sceneRoutes) Routes() chi.Router { diff --git a/pkg/api/server.go b/pkg/api/server.go index 823dae457..cb969562c 100644 --- a/pkg/api/server.go +++ b/pkg/api/server.go @@ -98,16 +98,6 @@ func authenticateHandler() func(http.Handler) http.Handler { } } -func visitedPluginHandler() func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - // get the visited plugins and set them in the context - - next.ServeHTTP(w, r) - }) - } -} - const loginEndPoint = "/login" func Start() { diff --git a/pkg/api/session.go b/pkg/api/session.go index 739df916f..cc14d0c28 100644 --- a/pkg/api/session.go +++ b/pkg/api/session.go @@ -10,11 +10,6 @@ import ( "github.com/stashapp/stash/pkg/session" ) -const cookieName = "session" -const usernameFormKey = "username" -const passwordFormKey = "password" -const userIDKey = "userID" - const returnURLParam = "returnURL" type loginTemplateData struct { diff --git a/pkg/dlna/dms.go b/pkg/dlna/dms.go index 85c25c979..599cc7820 100644 --- a/pkg/dlna/dms.go +++ b/pkg/dlna/dms.go @@ -58,7 +58,6 @@ const ( resPath = "/res" iconPath = "/icon" rootDescPath = "/rootDesc.xml" - contentDirectorySCPDURL = "/scpd/ContentDirectory.xml" contentDirectoryEventSubURL = "/evt/ContentDirectory" serviceControlURL = "/ctl" deviceIconPath = "/deviceIcon" diff --git a/pkg/ffmpeg/hls.go b/pkg/ffmpeg/hls.go index 4ac5788ec..f0f6b5205 100644 --- a/pkg/ffmpeg/hls.go +++ b/pkg/ffmpeg/hls.go @@ -21,9 +21,8 @@ func WriteHLSPlaylist(probeResult VideoFile, baseUrl string, w io.Writer) { leftover := duration upTo := 0.0 - tsURL := baseUrl i := strings.LastIndex(baseUrl, ".m3u8") - tsURL = baseUrl[0:i] + ".ts" + tsURL := baseUrl[0:i] + ".ts" for leftover > 0 { thisLength := hlsSegmentLength diff --git a/pkg/gallery/export_test.go b/pkg/gallery/export_test.go index 56b3e540a..2cd04fa17 100644 --- a/pkg/gallery/export_test.go +++ b/pkg/gallery/export_test.go @@ -19,7 +19,7 @@ const ( missingStudioID = 5 errStudioID = 6 - noTagsID = 11 + // noTagsID = 11 errTagsID = 12 ) @@ -39,11 +39,6 @@ const ( studioName = "studioName" ) -var names = []string{ - "name1", - "name2", -} - var createTime time.Time = time.Date(2001, 01, 01, 0, 0, 0, 0, time.UTC) var updateTime time.Time = time.Date(2002, 01, 01, 0, 0, 0, 0, time.UTC) @@ -71,18 +66,6 @@ func createFullGallery(id int) models.Gallery { } } -func createEmptyGallery(id int) models.Gallery { - return models.Gallery{ - ID: id, - CreatedAt: models.SQLiteTimestamp{ - Timestamp: createTime, - }, - UpdatedAt: models.SQLiteTimestamp{ - Timestamp: updateTime, - }, - } -} - func createFullJSONGallery() *jsonschema.Gallery { return &jsonschema.Gallery{ Title: title, @@ -103,17 +86,6 @@ func createFullJSONGallery() *jsonschema.Gallery { } } -func createEmptyJSONGallery() *jsonschema.Gallery { - return &jsonschema.Gallery{ - CreatedAt: models.JSONTime{ - Time: createTime, - }, - UpdatedAt: models.JSONTime{ - Time: updateTime, - }, - } -} - type basicTestScenario struct { input models.Gallery expected *jsonschema.Gallery diff --git a/pkg/gallery/import_test.go b/pkg/gallery/import_test.go index 19de14f35..176c80810 100644 --- a/pkg/gallery/import_test.go +++ b/pkg/gallery/import_test.go @@ -13,8 +13,8 @@ import ( ) const ( - galleryNameErr = "galleryNameErr" - existingGalleryName = "existingGalleryName" + galleryNameErr = "galleryNameErr" + // existingGalleryName = "existingGalleryName" existingGalleryID = 100 existingStudioID = 101 diff --git a/pkg/image/export_test.go b/pkg/image/export_test.go index 9c7ab5ed1..25959aab0 100644 --- a/pkg/image/export_test.go +++ b/pkg/image/export_test.go @@ -13,8 +13,8 @@ import ( ) const ( - imageID = 1 - noImageID = 2 + imageID = 1 + // noImageID = 2 errImageID = 3 studioID = 4 @@ -24,17 +24,17 @@ const ( // noGalleryID = 7 // errGalleryID = 8 - noTagsID = 11 + // noTagsID = 11 errTagsID = 12 - noMoviesID = 13 - errMoviesID = 14 - errFindMovieID = 15 + // noMoviesID = 13 + // errMoviesID = 14 + // errFindMovieID = 15 - noMarkersID = 16 - errMarkersID = 17 - errFindPrimaryTagID = 18 - errFindByMarkerID = 19 + // noMarkersID = 16 + // errMarkersID = 17 + // errFindPrimaryTagID = 18 + // errFindByMarkerID = 19 ) const ( @@ -53,11 +53,6 @@ const ( //galleryChecksum = "galleryChecksum" ) -var names = []string{ - "name1", - "name2", -} - var createTime time.Time = time.Date(2001, 01, 01, 0, 0, 0, 0, time.UTC) var updateTime time.Time = time.Date(2002, 01, 01, 0, 0, 0, 0, time.UTC) @@ -81,18 +76,6 @@ func createFullImage(id int) models.Image { } } -func createEmptyImage(id int) models.Image { - return models.Image{ - ID: id, - CreatedAt: models.SQLiteTimestamp{ - Timestamp: createTime, - }, - UpdatedAt: models.SQLiteTimestamp{ - Timestamp: updateTime, - }, - } -} - func createFullJSONImage() *jsonschema.Image { return &jsonschema.Image{ Title: title, @@ -114,18 +97,6 @@ func createFullJSONImage() *jsonschema.Image { } } -func createEmptyJSONImage() *jsonschema.Image { - return &jsonschema.Image{ - File: &jsonschema.ImageFile{}, - CreatedAt: models.JSONTime{ - Time: createTime, - }, - UpdatedAt: models.JSONTime{ - Time: updateTime, - }, - } -} - type basicTestScenario struct { input models.Image expected *jsonschema.Image diff --git a/pkg/image/import_test.go b/pkg/image/import_test.go index 91978dac8..8b7099823 100644 --- a/pkg/image/import_test.go +++ b/pkg/image/import_test.go @@ -11,20 +11,18 @@ import ( "github.com/stretchr/testify/mock" ) -const invalidImage = "aW1hZ2VCeXRlcw&&" - const ( path = "path" - imageNameErr = "imageNameErr" - existingImageName = "existingImageName" + imageNameErr = "imageNameErr" + // existingImageName = "existingImageName" existingImageID = 100 existingStudioID = 101 existingGalleryID = 102 existingPerformerID = 103 - existingMovieID = 104 - existingTagID = 105 + // existingMovieID = 104 + existingTagID = 105 existingStudioName = "existingStudioName" existingStudioErr = "existingStudioErr" diff --git a/pkg/logger/logger.go b/pkg/logger/logger.go index 8e2de2bbf..fde7e09c2 100644 --- a/pkg/logger/logger.go +++ b/pkg/logger/logger.go @@ -41,7 +41,6 @@ func Init(logFile string, logOut bool, logLevel string) { if err != nil { fmt.Printf("Could not open '%s' for log output due to error: %s\n", logFile, err.Error()) - logFile = "" } } diff --git a/pkg/manager/filename_parser.go b/pkg/manager/filename_parser.go index 991261941..6373c4a2a 100644 --- a/pkg/manager/filename_parser.go +++ b/pkg/manager/filename_parser.go @@ -11,8 +11,6 @@ import ( "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/tag" - - "github.com/jmoiron/sqlx" ) type parserField struct { @@ -52,10 +50,6 @@ func (f parserField) replaceInPattern(pattern string) string { return string(f.fieldRegex.ReplaceAllString(pattern, f.regex)) } -func (f parserField) getFieldPattern() string { - return "{" + f.field + "}" -} - var validFields map[string]parserField var escapeCharRE *regexp.Regexp var capitalizeTitleRE *regexp.Regexp @@ -405,26 +399,6 @@ func (m parseMapper) parse(scene *models.Scene) *sceneHolder { return sceneHolder } -type performerQueryer interface { - FindByNames(names []string, tx *sqlx.Tx, nocase bool) ([]*models.Performer, error) -} - -type sceneQueryer interface { - QueryByPathRegex(findFilter *models.FindFilterType) ([]*models.Scene, int) -} - -type tagQueryer interface { - FindByName(name string, tx *sqlx.Tx, nocase bool) (*models.Tag, error) -} - -type studioQueryer interface { - FindByName(name string, tx *sqlx.Tx, nocase bool) (*models.Studio, error) -} - -type movieQueryer interface { - FindByName(name string, tx *sqlx.Tx, nocase bool) (*models.Movie, error) -} - type SceneFilenameParser struct { Pattern string ParserInput models.SceneParserInput diff --git a/pkg/manager/jsonschema/utils.go b/pkg/manager/jsonschema/utils.go index 2844026f7..13f42c820 100644 --- a/pkg/manager/jsonschema/utils.go +++ b/pkg/manager/jsonschema/utils.go @@ -4,13 +4,10 @@ import ( "bytes" "io/ioutil" - "time" jsoniter "github.com/json-iterator/go" ) -var nilTime = (time.Time{}).UnixNano() - func CompareJSON(a interface{}, b interface{}) bool { aBuf, _ := encode(a) bBuf, _ := encode(b) diff --git a/pkg/manager/task_import.go b/pkg/manager/task_import.go index 84acaaeef..db3d9f76d 100644 --- a/pkg/manager/task_import.go +++ b/pkg/manager/task_import.go @@ -569,101 +569,6 @@ func (t *ImportTask) ImportImages(ctx context.Context) { logger.Info("[images] import complete") } -func (t *ImportTask) getPerformers(names []string, qb models.PerformerReader) ([]*models.Performer, error) { - performers, err := qb.FindByNames(names, false) - if err != nil { - return nil, err - } - - var pluckedNames []string - for _, performer := range performers { - if !performer.Name.Valid { - continue - } - pluckedNames = append(pluckedNames, performer.Name.String) - } - - missingPerformers := utils.StrFilter(names, func(name string) bool { - return !utils.StrInclude(pluckedNames, name) - }) - - for _, missingPerformer := range missingPerformers { - logger.Warnf("[scenes] performer %s does not exist", missingPerformer) - } - - return performers, nil -} - -func (t *ImportTask) getMoviesScenes(input []jsonschema.SceneMovie, sceneID int, mqb models.MovieReader) ([]models.MoviesScenes, error) { - var movies []models.MoviesScenes - for _, inputMovie := range input { - movie, err := mqb.FindByName(inputMovie.MovieName, false) - if err != nil { - return nil, err - } - - if movie == nil { - logger.Warnf("[scenes] movie %s does not exist", inputMovie.MovieName) - } else { - toAdd := models.MoviesScenes{ - MovieID: movie.ID, - SceneID: sceneID, - } - - if inputMovie.SceneIndex != 0 { - toAdd.SceneIndex = sql.NullInt64{ - Int64: int64(inputMovie.SceneIndex), - Valid: true, - } - } - - movies = append(movies, toAdd) - } - } - - return movies, nil -} - -func (t *ImportTask) getTags(sceneChecksum string, names []string, tqb models.TagReader) ([]*models.Tag, error) { - tags, err := tqb.FindByNames(names, false) - if err != nil { - return nil, err - } - - var pluckedNames []string - for _, tag := range tags { - if tag.Name == "" { - continue - } - pluckedNames = append(pluckedNames, tag.Name) - } - - missingTags := utils.StrFilter(names, func(name string) bool { - return !utils.StrInclude(pluckedNames, name) - }) - - for _, missingTag := range missingTags { - logger.Warnf("[scenes] <%s> tag %s does not exist", sceneChecksum, missingTag) - } - - return tags, nil -} - -// https://www.reddit.com/r/golang/comments/5ia523/idiomatic_way_to_remove_duplicates_in_a_slice/db6qa2e -func (t *ImportTask) getUnique(s []string) []string { - seen := make(map[string]struct{}, len(s)) - j := 0 - for _, v := range s { - if _, ok := seen[v]; ok { - continue - } - seen[v] = struct{}{} - s[j] = v - j++ - } - return s[:j] -} - var currentLocation = time.Now().Location() func (t *ImportTask) getTimeFromJSONTime(jsonTime models.JSONTime) time.Time { diff --git a/pkg/scene/export_test.go b/pkg/scene/export_test.go index dc3164f13..422008fa2 100644 --- a/pkg/scene/export_test.go +++ b/pkg/scene/export_test.go @@ -23,8 +23,8 @@ const ( missingStudioID = 5 errStudioID = 6 - noGalleryID = 7 - errGalleryID = 8 + // noGalleryID = 7 + // errGalleryID = 8 noTagsID = 11 errTagsID = 12 @@ -64,8 +64,8 @@ const ( ) const ( - studioName = "studioName" - galleryChecksum = "galleryChecksum" + studioName = "studioName" + // galleryChecksum = "galleryChecksum" validMovie1 = 1 validMovie2 = 2 @@ -293,24 +293,6 @@ func TestGetStudioName(t *testing.T) { mockStudioReader.AssertExpectations(t) } -var getGalleryChecksumScenarios = []stringTestScenario{ - { - createEmptyScene(sceneID), - galleryChecksum, - false, - }, - { - createEmptyScene(noGalleryID), - "", - false, - }, - { - createEmptyScene(errGalleryID), - "", - true, - }, -} - type stringSliceTestScenario struct { input models.Scene expected []string diff --git a/pkg/scene/import_test.go b/pkg/scene/import_test.go index 09f0bf38d..3f59c3cf3 100644 --- a/pkg/scene/import_test.go +++ b/pkg/scene/import_test.go @@ -16,8 +16,8 @@ const invalidImage = "aW1hZ2VCeXRlcw&&" const ( path = "path" - sceneNameErr = "sceneNameErr" - existingSceneName = "existingSceneName" + sceneNameErr = "sceneNameErr" + // existingSceneName = "existingSceneName" existingSceneID = 100 existingStudioID = 101 diff --git a/pkg/scraper/action.go b/pkg/scraper/action.go index fdfa15afa..ac5e163b6 100644 --- a/pkg/scraper/action.go +++ b/pkg/scraper/action.go @@ -11,13 +11,6 @@ const ( scraperActionJson scraperAction = "scrapeJson" ) -var allScraperAction = []scraperAction{ - scraperActionScript, - scraperActionStash, - scraperActionXPath, - scraperActionJson, -} - func (e scraperAction) IsValid() bool { switch e { case scraperActionScript, scraperActionStash, scraperActionXPath, scraperActionJson: @@ -26,12 +19,6 @@ func (e scraperAction) IsValid() bool { return false } -type scrapeOptions struct { - scraper scraperTypeConfig - config config - globalConfig GlobalConfig -} - type scraper interface { scrapePerformersByName(name string) ([]*models.ScrapedPerformer, error) scrapePerformerByFragment(scrapedPerformer models.ScrapedPerformerInput) (*models.ScrapedPerformer, error) diff --git a/pkg/scraper/mapped.go b/pkg/scraper/mapped.go index 52af8556c..8a8e2b847 100644 --- a/pkg/scraper/mapped.go +++ b/pkg/scraper/mapped.go @@ -557,7 +557,7 @@ func (a mappedPostProcessAction) ToPostProcessAction() (postProcessAction, error if found != "" { return nil, fmt.Errorf("post-process actions must have a single field, found %s and %s", found, "subtractDays") } - found = "subtractDays" + // found = "subtractDays" action := postProcessSubtractDays(a.SubtractDays) ret = &action } diff --git a/pkg/scraper/url.go b/pkg/scraper/url.go index e2af1cb97..d2adaa81b 100644 --- a/pkg/scraper/url.go +++ b/pkg/scraper/url.go @@ -235,17 +235,6 @@ func getRemoteCDPWSAddress(address string) (string, error) { return remote, err } -func cdpNetwork(enable bool) chromedp.Action { - return chromedp.ActionFunc(func(ctx context.Context) error { - if enable { - network.Enable().Do(ctx) - } else { - network.Disable().Do(ctx) - } - return nil - }) -} - func cdpHeaders(driverOptions scraperDriverOptions) map[string]interface{} { headers := map[string]interface{}{} if driverOptions.Headers != nil { diff --git a/pkg/session/session.go b/pkg/session/session.go index 55faa4282..9d754e63a 100644 --- a/pkg/session/session.go +++ b/pkg/session/session.go @@ -173,19 +173,6 @@ func setVisitedPlugins(ctx context.Context, visitedPlugins []string) context.Con return context.WithValue(ctx, contextVisitedPlugins, visitedPlugins) } -func (s *Store) createSessionCookie(username string) (*http.Cookie, error) { - session := sessions.NewSession(s.sessionStore, cookieName) - session.Values[userIDKey] = username - - encoded, err := securecookie.EncodeMulti(session.Name(), session.Values, - s.sessionStore.Codecs...) - if err != nil { - return nil, err - } - - return sessions.NewCookie(session.Name(), encoded, session.Options), nil -} - func (s *Store) MakePluginCookie(ctx context.Context) *http.Cookie { currentUser := GetCurrentUserID(ctx) visitedPlugins := GetVisitedPlugins(ctx) diff --git a/pkg/sqlite/filter.go b/pkg/sqlite/filter.go index 1517bf99a..d83ce12b0 100644 --- a/pkg/sqlite/filter.go +++ b/pkg/sqlite/filter.go @@ -389,14 +389,6 @@ func boolCriterionHandler(c *bool, column string) criterionHandlerFunc { } } -func stringLiteralCriterionHandler(v *string, column string) criterionHandlerFunc { - return func(f *filterBuilder) { - if v != nil { - f.addWhere(column+" = ?", v) - } - } -} - // handle for MultiCriterion where there is a join table between the new // objects type joinedMultiCriterionHandlerBuilder struct { diff --git a/pkg/sqlite/gallery.go b/pkg/sqlite/gallery.go index 66bf2032a..30f13c75c 100644 --- a/pkg/sqlite/gallery.go +++ b/pkg/sqlite/gallery.go @@ -311,17 +311,6 @@ func galleryIsMissingCriterionHandler(qb *galleryQueryBuilder, isMissing *string } } -func (qb *galleryQueryBuilder) getMultiCriterionHandlerBuilder(foreignTable, joinTable, foreignFK string, addJoinsFunc func(f *filterBuilder)) multiCriterionHandlerBuilder { - return multiCriterionHandlerBuilder{ - primaryTable: galleryTable, - foreignTable: foreignTable, - joinTable: joinTable, - primaryFK: galleryIDColumn, - foreignFK: foreignFK, - addJoinsFunc: addJoinsFunc, - } -} - func galleryTagsCriterionHandler(qb *galleryQueryBuilder, tags *models.MultiCriterionInput) criterionHandlerFunc { h := joinedMultiCriterionHandlerBuilder{ primaryTable: galleryTable, diff --git a/pkg/sqlite/scraped_item.go b/pkg/sqlite/scraped_item.go index 39e040f89..30f772dc9 100644 --- a/pkg/sqlite/scraped_item.go +++ b/pkg/sqlite/scraped_item.go @@ -72,14 +72,6 @@ func (qb *scrapedItemQueryBuilder) getScrapedItemsSort(findFilter *models.FindFi return getSort(sort, direction, "scraped_items") } -func (qb *scrapedItemQueryBuilder) queryScrapedItem(query string, args []interface{}) (*models.ScrapedItem, error) { - results, err := qb.queryScrapedItems(query, args) - if err != nil || len(results) < 1 { - return nil, err - } - return results[0], nil -} - func (qb *scrapedItemQueryBuilder) queryScrapedItems(query string, args []interface{}) ([]*models.ScrapedItem, error) { var ret models.ScrapedItems if err := qb.query(query, args, &ret); err != nil { diff --git a/pkg/sqlite/sql.go b/pkg/sqlite/sql.go index 7c9477bdc..7101fe489 100644 --- a/pkg/sqlite/sql.go +++ b/pkg/sqlite/sql.go @@ -8,7 +8,6 @@ import ( "strconv" "strings" - "github.com/jmoiron/sqlx" "github.com/stashapp/stash/pkg/logger" "github.com/stashapp/stash/pkg/models" ) @@ -148,32 +147,6 @@ func getInBinding(length int) string { return "(" + bindings + ")" } -func getCriterionModifierBinding(criterionModifier models.CriterionModifier, value interface{}) (string, int) { - var length int - switch x := value.(type) { - case []string: - length = len(x) - case []int: - length = len(x) - default: - length = 1 - } - if modifier := criterionModifier.String(); criterionModifier.IsValid() { - switch modifier { - case "EQUALS", "NOT_EQUALS", "GREATER_THAN", "LESS_THAN", "IS_NULL", "NOT_NULL", "BETWEEN", "NOT_BETWEEN": - return getSimpleCriterionClause(criterionModifier, "?") - case "INCLUDES": - return "IN " + getInBinding(length), length // TODO? - case "EXCLUDES": - return "NOT IN " + getInBinding(length), length // TODO? - default: - logger.Errorf("todo") - return "= ?", 1 // TODO - } - } - return "= ?", 1 // TODO -} - func getSimpleCriterionClause(criterionModifier models.CriterionModifier, rhs string) (string, int) { if modifier := criterionModifier.String(); criterionModifier.IsValid() { switch modifier { @@ -252,12 +225,6 @@ func getCountCriterionClause(primaryTable, joinTable, primaryFK string, criterio return getIntCriterionWhereClause(lhs, criterion) } -func ensureTx(tx *sqlx.Tx) { - if tx == nil { - panic("must use a transaction") - } -} - func getImage(tx dbi, query string, args ...interface{}) ([]byte, error) { rows, err := tx.Queryx(query, args...) diff --git a/pkg/utils/byterange.go b/pkg/utils/byterange.go index 2220b8dc4..5fb031ea7 100644 --- a/pkg/utils/byterange.go +++ b/pkg/utils/byterange.go @@ -30,14 +30,6 @@ func CreateByteRange(s string) ByteRange { return ret } -func (r ByteRange) getBytesToRead() int64 { - if r.End == nil { - return 0 - } - - return *r.End - r.Start + 1 -} - func (r ByteRange) ToHeaderValue(fileLength int64) string { if r.End == nil { return "" From 82a41e17c742cf9b7ea9d362710433f49246bab6 Mon Sep 17 00:00:00 2001 From: SmallCoccinelle <89733524+SmallCoccinelle@users.noreply.github.com> Date: Thu, 9 Sep 2021 06:10:39 +0200 Subject: [PATCH 14/86] Avoid wrapping strings.Replace in Contains (#1710) The strings.Replace function counts the number of replacements. If 0, the original string is returned. Hence, there is no need to check if a replacement will happen before doing the work. --- pkg/scraper/mapped.go | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/pkg/scraper/mapped.go b/pkg/scraper/mapped.go index 8a8e2b847..ca5c7b1b8 100644 --- a/pkg/scraper/mapped.go +++ b/pkg/scraper/mapped.go @@ -32,9 +32,7 @@ func (s mappedConfig) applyCommon(c commonMappedConfig, src string) string { ret := src for commonKey, commonVal := range c { - if strings.Contains(ret, commonKey) { - ret = strings.Replace(ret, commonKey, commonVal, -1) - } + ret = strings.Replace(ret, commonKey, commonVal, -1) } return ret From c91ffe1e58483c6579162becac84369e8f4d0c56 Mon Sep 17 00:00:00 2001 From: gitgiggety <79809426+gitgiggety@users.noreply.github.com> Date: Thu, 9 Sep 2021 06:58:43 +0200 Subject: [PATCH 15/86] Tag hierarchy (#1519) * Add migration script for tag relations table * Expand hierarchical filter features Expand the features of the hierarchical multi input filter with support for using a relations table, which only has parent_id and child_id columns, and support adding an additional intermediate table to join on, for example for scenes and tags which are linked by the scenes_tags table as well. * Add hierarchical filtering for tags * Add hierarchical tags support to scene markers Refactor filtering of scene markers to filterBuilder and in the process add support for hierarchical tags as well. * List parent and child tags on tag details page * Support setting parent and child tags Add support for setting parent and child tags during tag creation and tag updates. * Validate no loops are created in tags hierarchy * Update tag merging to support tag hierarcy * Add unit tests for tags.EnsureUniqueHierarchy * Fix applying recursive to with clause The SQL `RECURSIVE` of a `WITH` clause only needs to be applied once, imediately after the `WITH`. So this fixes the query building to do just that, automatically applying the `RECURSIVE` keyword when any added with clause is added as recursive. * Rename hierarchical root id column * Rewrite hierarchical filtering for performance Completely rewrite the hierarchical filtering to optimize for performance. Doing the recursive query in combination with a complex query seems to break SQLite optimizing some things which means that the recursive part might be 2,5 second slower than adding a static `VALUES()` list. This is mostly noticable in case of the tag hierarchy where setting an exclusion with any depth (or depth: all) being applied has this performance impact of 2,5 second. "Include" also suffered this issue, but some rewritten query by joining in the *_tags table in one pass and applying a `WHERE x IS NOT NULL` filter did seem to optimize that case. But that optimization isn't applied to the `IS NULL` filter of "exclude". Running a simple query beforehand to get all (recursive) items and then applying them to the query doesn't have this performance penalty. * Remove UI references to child studios and tags * Add parents to tag export * Support importing of parent relationship for tags * Assign stable ids to parent / child badges * Silence Apollo warning on parents/children fields on tags Silence warning triggered by Apollo GraphQL by explicitly instructing it to use the incoming parents/children values. By default it already does this, but it triggers a warning as it might be unintended that it uses the incoming values (instead of for example merging both arrays). Setting merge to false still applies the same behaviour (use only incoming values) but silences the warning as it's explicitly configured to work like this. * Rework detecting unique tag hierarchy Completely rework the unique tag hierarchy to detect invalid hierarchies for which a tag is "added in the middle". So when there are tags A <- B and A <- C, you could previously edit tag B and add tag C as a sub tag without it being noticed as parent A being applied twice (to tag C). While afterwards saving tag C would fail as tag A was applied as parent twice. The updated code correctly detects this scenario as well. Furthermore the error messaging has been reworked a bit and the message now mentions both the direct parent / sub tag as well as the tag which would results in the error. So in aboves example it would now show the message that tag C can't be applied because tag A already is a parent. * Update relations on cached tags when needed Update the relations on cached tags when a tag is created / updated / deleted so these always reflect the correct state. Otherwise (re)opening a tag might still show the old relations untill the page is fully reloaded or the list is navigated. But this obviously is strange when you for example have tag A, create or update tag B to have a relation to tag A, and from tags B page click through to tag A and it doesn't show that it is linked to tag B. --- graphql/documents/data/tag.graphql | 8 + graphql/schema/types/filters.graphql | 20 +- graphql/schema/types/tag.graphql | 9 + pkg/api/resolver_model_tag.go | 22 ++ pkg/api/resolver_mutation_tag.go | 71 ++++ pkg/database/database.go | 2 +- .../migrations/26_tag_hierarchy.up.sql | 7 + pkg/dlna/cds.go | 3 +- pkg/gallery/query.go | 3 +- pkg/image/query.go | 3 +- pkg/manager/jsonschema/tag.go | 1 + pkg/manager/task_import.go | 56 +++- pkg/models/mocks/TagReaderWriter.go | 120 +++++++ pkg/models/tag.go | 6 + pkg/sqlite/filter.go | 148 +++++++-- pkg/sqlite/gallery.go | 56 ++-- pkg/sqlite/gallery_test.go | 18 +- pkg/sqlite/image.go | 56 ++-- pkg/sqlite/image_test.go | 18 +- pkg/sqlite/movies.go | 2 + pkg/sqlite/performer.go | 31 +- pkg/sqlite/performer_test.go | 9 +- pkg/sqlite/query.go | 15 +- pkg/sqlite/repository.go | 8 +- pkg/sqlite/scene.go | 52 ++- pkg/sqlite/scene_marker.go | 197 +++++------- pkg/sqlite/scene_test.go | 18 +- pkg/sqlite/tag.go | 134 ++++++++ pkg/tag/export.go | 7 + pkg/tag/export_test.go | 41 ++- pkg/tag/import.go | 69 +++- pkg/tag/import_test.go | 113 +++++++ pkg/tag/update.go | 162 +++++++++- pkg/tag/update_test.go | 302 ++++++++++++++++++ .../components/Changelog/versions/v0100.md | 2 + .../Filters/HierarchicalLabelValueFilter.tsx | 14 +- .../components/Shared/DeleteEntityDialog.tsx | 5 + .../Studios/StudioDetails/Studio.tsx | 2 +- .../src/components/Tags/TagDetails/Tag.tsx | 27 +- .../Tags/TagDetails/TagDetailsPanel.tsx | 51 ++- .../Tags/TagDetails/TagEditPanel.tsx | 62 +++- .../Tags/TagDetails/TagMarkersPanel.tsx | 9 +- ui/v2.5/src/components/Tags/TagList.tsx | 18 ++ ui/v2.5/src/core/createClient.ts | 11 + ui/v2.5/src/core/tags.ts | 70 +++- ui/v2.5/src/locales/de-DE.json | 3 +- ui/v2.5/src/locales/en-GB.json | 7 +- ui/v2.5/src/locales/pt-BR.json | 3 +- ui/v2.5/src/locales/zh-CN.json | 3 +- ui/v2.5/src/locales/zh-TW.json | 3 +- .../src/models/list-filter/criteria/tags.ts | 7 +- ui/v2.5/src/utils/navigation.ts | 25 +- 52 files changed, 1778 insertions(+), 331 deletions(-) create mode 100644 pkg/database/migrations/26_tag_hierarchy.up.sql create mode 100644 pkg/tag/update_test.go diff --git a/graphql/documents/data/tag.graphql b/graphql/documents/data/tag.graphql index cf1e2c050..ab83adf73 100644 --- a/graphql/documents/data/tag.graphql +++ b/graphql/documents/data/tag.graphql @@ -8,4 +8,12 @@ fragment TagData on Tag { image_count gallery_count performer_count + + parents { + ...SlimTagData + } + + children { + ...SlimTagData + } } diff --git a/graphql/schema/types/filters.graphql b/graphql/schema/types/filters.graphql index 76640bb24..04a508f06 100644 --- a/graphql/schema/types/filters.graphql +++ b/graphql/schema/types/filters.graphql @@ -72,7 +72,7 @@ input PerformerFilterType { """Filter to only include performers missing this property""" is_missing: String """Filter to only include performers with these tags""" - tags: MultiCriterionInput + tags: HierarchicalMultiCriterionInput """Filter by tag count""" tag_count: IntCriterionInput """Filter by scene count""" @@ -99,11 +99,11 @@ input PerformerFilterType { input SceneMarkerFilterType { """Filter to only include scene markers with this tag""" - tag_id: ID + tag_id: ID @deprecated(reason: "use tags filter instead") """Filter to only include scene markers with these tags""" - tags: MultiCriterionInput + tags: HierarchicalMultiCriterionInput """Filter to only include scene markers attached to a scene with these tags""" - scene_tags: MultiCriterionInput + scene_tags: HierarchicalMultiCriterionInput """Filter to only include scene markers with these performers""" performers: MultiCriterionInput } @@ -143,11 +143,11 @@ input SceneFilterType { """Filter to only include scenes with this movie""" movies: MultiCriterionInput """Filter to only include scenes with these tags""" - tags: MultiCriterionInput + tags: HierarchicalMultiCriterionInput """Filter by tag count""" tag_count: IntCriterionInput """Filter to only include scenes with performers with these tags""" - performer_tags: MultiCriterionInput + performer_tags: HierarchicalMultiCriterionInput """Filter to only include scenes with these performers""" performers: MultiCriterionInput """Filter by performer count""" @@ -226,11 +226,11 @@ input GalleryFilterType { """Filter to only include galleries with this studio""" studios: HierarchicalMultiCriterionInput """Filter to only include galleries with these tags""" - tags: MultiCriterionInput + tags: HierarchicalMultiCriterionInput """Filter by tag count""" tag_count: IntCriterionInput """Filter to only include galleries with performers with these tags""" - performer_tags: MultiCriterionInput + performer_tags: HierarchicalMultiCriterionInput """Filter to only include galleries with these performers""" performers: MultiCriterionInput """Filter by performer count""" @@ -295,11 +295,11 @@ input ImageFilterType { """Filter to only include images with this studio""" studios: HierarchicalMultiCriterionInput """Filter to only include images with these tags""" - tags: MultiCriterionInput + tags: HierarchicalMultiCriterionInput """Filter by tag count""" tag_count: IntCriterionInput """Filter to only include images with performers with these tags""" - performer_tags: MultiCriterionInput + performer_tags: HierarchicalMultiCriterionInput """Filter to only include images with these performers""" performers: MultiCriterionInput """Filter by performer count""" diff --git a/graphql/schema/types/tag.graphql b/graphql/schema/types/tag.graphql index 46f8c25cd..fea9f40fe 100644 --- a/graphql/schema/types/tag.graphql +++ b/graphql/schema/types/tag.graphql @@ -11,6 +11,9 @@ type Tag { image_count: Int # Resolver gallery_count: Int # Resolver performer_count: Int + + parents: [Tag!]! + children: [Tag!]! } input TagCreateInput { @@ -19,6 +22,9 @@ input TagCreateInput { """This should be a URL or a base64 encoded data URL""" image: String + + parent_ids: [ID!] + child_ids: [ID!] } input TagUpdateInput { @@ -28,6 +34,9 @@ input TagUpdateInput { """This should be a URL or a base64 encoded data URL""" image: String + + parent_ids: [ID!] + child_ids: [ID!] } input TagDestroyInput { diff --git a/pkg/api/resolver_model_tag.go b/pkg/api/resolver_model_tag.go index a863cd233..3b90463ca 100644 --- a/pkg/api/resolver_model_tag.go +++ b/pkg/api/resolver_model_tag.go @@ -10,6 +10,28 @@ import ( "github.com/stashapp/stash/pkg/models" ) +func (r *tagResolver) Parents(ctx context.Context, obj *models.Tag) (ret []*models.Tag, err error) { + if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error { + ret, err = repo.Tag().FindByChildTagID(obj.ID) + return err + }); err != nil { + return nil, err + } + + return ret, nil +} + +func (r *tagResolver) Children(ctx context.Context, obj *models.Tag) (ret []*models.Tag, err error) { + if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error { + ret, err = repo.Tag().FindByParentTagID(obj.ID) + return err + }); err != nil { + return nil, err + } + + return ret, nil +} + func (r *tagResolver) Aliases(ctx context.Context, obj *models.Tag) (ret []string, err error) { if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error { ret, err = repo.Tag().GetAliases(obj.ID) diff --git a/pkg/api/resolver_mutation_tag.go b/pkg/api/resolver_mutation_tag.go index 94292a7ba..d1dc230e4 100644 --- a/pkg/api/resolver_mutation_tag.go +++ b/pkg/api/resolver_mutation_tag.go @@ -75,6 +75,28 @@ func (r *mutationResolver) TagCreate(ctx context.Context, input models.TagCreate } } + if input.ParentIds != nil && len(input.ParentIds) > 0 { + ids, err := utils.StringSliceToIntSlice(input.ParentIds) + if err != nil { + return err + } + + if err := qb.UpdateParentTags(t.ID, ids); err != nil { + return err + } + } + + if input.ChildIds != nil && len(input.ChildIds) > 0 { + ids, err := utils.StringSliceToIntSlice(input.ChildIds) + if err != nil { + return err + } + + if err := qb.UpdateChildTags(t.ID, ids); err != nil { + return err + } + } + return nil }); err != nil { return nil, err @@ -161,6 +183,41 @@ func (r *mutationResolver) TagUpdate(ctx context.Context, input models.TagUpdate } } + var parentIDs []int + var childIDs []int + + if translator.hasField("parent_ids") { + parentIDs, err = utils.StringSliceToIntSlice(input.ParentIds) + if err != nil { + return err + } + } + + if translator.hasField("child_ids") { + childIDs, err = utils.StringSliceToIntSlice(input.ChildIds) + if err != nil { + return err + } + } + + if parentIDs != nil || childIDs != nil { + if err := tag.EnsureUniqueHierarchy(tagID, parentIDs, childIDs, qb); err != nil { + return err + } + } + + if parentIDs != nil { + if err := qb.UpdateParentTags(tagID, parentIDs); err != nil { + return err + } + } + + if childIDs != nil { + if err := qb.UpdateChildTags(tagID, childIDs); err != nil { + return err + } + } + return nil }); err != nil { return nil, err @@ -242,10 +299,24 @@ func (r *mutationResolver) TagsMerge(ctx context.Context, input models.TagsMerge return fmt.Errorf("Tag with ID %d not found", destination) } + parents, children, err := tag.MergeHierarchy(destination, source, qb) + if err != nil { + return err + } + if err = qb.Merge(source, destination); err != nil { return err } + err = qb.UpdateParentTags(destination, parents) + if err != nil { + return err + } + err = qb.UpdateChildTags(destination, children) + if err != nil { + return err + } + return nil }); err != nil { return nil, err diff --git a/pkg/database/database.go b/pkg/database/database.go index cedcc9428..b4a6a8c29 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -23,7 +23,7 @@ import ( var DB *sqlx.DB var WriteMu *sync.Mutex var dbPath string -var appSchemaVersion uint = 25 +var appSchemaVersion uint = 26 var databaseSchemaVersion uint var ( diff --git a/pkg/database/migrations/26_tag_hierarchy.up.sql b/pkg/database/migrations/26_tag_hierarchy.up.sql new file mode 100644 index 000000000..6e9855fc2 --- /dev/null +++ b/pkg/database/migrations/26_tag_hierarchy.up.sql @@ -0,0 +1,7 @@ +CREATE TABLE tags_relations ( + parent_id integer, + child_id integer, + primary key (parent_id, child_id), + foreign key (parent_id) references tags(id) on delete cascade, + foreign key (child_id) references tags(id) on delete cascade +); diff --git a/pkg/dlna/cds.go b/pkg/dlna/cds.go index e99f63be8..79a80ab51 100644 --- a/pkg/dlna/cds.go +++ b/pkg/dlna/cds.go @@ -570,9 +570,10 @@ func (me *contentDirectoryService) getTags() []interface{} { func (me *contentDirectoryService) getTagScenes(paths []string, host string) []interface{} { sceneFilter := &models.SceneFilterType{ - Tags: &models.MultiCriterionInput{ + Tags: &models.HierarchicalMultiCriterionInput{ Modifier: models.CriterionModifierIncludes, Value: []string{paths[0]}, + Depth: 0, }, } diff --git a/pkg/gallery/query.go b/pkg/gallery/query.go index a354f5aa5..a7fbed856 100644 --- a/pkg/gallery/query.go +++ b/pkg/gallery/query.go @@ -31,9 +31,10 @@ func CountByStudioID(r models.GalleryReader, id int) (int, error) { func CountByTagID(r models.GalleryReader, id int) (int, error) { filter := &models.GalleryFilterType{ - Tags: &models.MultiCriterionInput{ + Tags: &models.HierarchicalMultiCriterionInput{ Value: []string{strconv.Itoa(id)}, Modifier: models.CriterionModifierIncludes, + Depth: 0, }, } diff --git a/pkg/image/query.go b/pkg/image/query.go index b5c70738c..eca6d49a4 100644 --- a/pkg/image/query.go +++ b/pkg/image/query.go @@ -31,9 +31,10 @@ func CountByStudioID(r models.ImageReader, id int) (int, error) { func CountByTagID(r models.ImageReader, id int) (int, error) { filter := &models.ImageFilterType{ - Tags: &models.MultiCriterionInput{ + Tags: &models.HierarchicalMultiCriterionInput{ Value: []string{strconv.Itoa(id)}, Modifier: models.CriterionModifierIncludes, + Depth: 0, }, } diff --git a/pkg/manager/jsonschema/tag.go b/pkg/manager/jsonschema/tag.go index 66fa910ab..fbb85a835 100644 --- a/pkg/manager/jsonschema/tag.go +++ b/pkg/manager/jsonschema/tag.go @@ -12,6 +12,7 @@ type Tag struct { Name string `json:"name,omitempty"` Aliases []string `json:"aliases,omitempty"` Image string `json:"image,omitempty"` + Parents []string `json:"parents,omitempty"` CreatedAt models.JSONTime `json:"created_at,omitempty"` UpdatedAt models.JSONTime `json:"updated_at,omitempty"` } diff --git a/pkg/manager/task_import.go b/pkg/manager/task_import.go index db3d9f76d..1bf2f3476 100644 --- a/pkg/manager/task_import.go +++ b/pkg/manager/task_import.go @@ -376,6 +376,7 @@ func (t *ImportTask) ImportGalleries(ctx context.Context) { } func (t *ImportTask) ImportTags(ctx context.Context) { + pendingParent := make(map[string][]*jsonschema.Tag) logger.Info("[tags] importing") for i, mappingJSON := range t.mappings.Tags { @@ -389,23 +390,64 @@ func (t *ImportTask) ImportTags(ctx context.Context) { logger.Progressf("[tags] %d of %d", index, len(t.mappings.Tags)) if err := t.txnManager.WithTxn(ctx, func(r models.Repository) error { - readerWriter := r.Tag() - - tagImporter := &tag.Importer{ - ReaderWriter: readerWriter, - Input: *tagJSON, + return t.ImportTag(tagJSON, pendingParent, false, r.Tag()) + }); err != nil { + if parentError, ok := err.(tag.ParentTagNotExistError); ok { + pendingParent[parentError.MissingParent()] = append(pendingParent[parentError.MissingParent()], tagJSON) + continue } - return performImport(tagImporter, t.DuplicateBehaviour) - }); err != nil { logger.Errorf("[tags] <%s> failed to import: %s", mappingJSON.Checksum, err.Error()) continue } } + for _, s := range pendingParent { + for _, orphanTagJSON := range s { + if err := t.txnManager.WithTxn(ctx, func(r models.Repository) error { + return t.ImportTag(orphanTagJSON, nil, true, r.Tag()) + }); err != nil { + logger.Errorf("[tags] <%s> failed to create: %s", orphanTagJSON.Name, err.Error()) + continue + } + } + } + logger.Info("[tags] import complete") } +func (t *ImportTask) ImportTag(tagJSON *jsonschema.Tag, pendingParent map[string][]*jsonschema.Tag, fail bool, readerWriter models.TagReaderWriter) error { + importer := &tag.Importer{ + ReaderWriter: readerWriter, + Input: *tagJSON, + MissingRefBehaviour: t.MissingRefBehaviour, + } + + // first phase: return error if parent does not exist + if !fail { + importer.MissingRefBehaviour = models.ImportMissingRefEnumFail + } + + if err := performImport(importer, t.DuplicateBehaviour); err != nil { + return err + } + + for _, childTagJSON := range pendingParent[tagJSON.Name] { + if err := t.ImportTag(childTagJSON, pendingParent, fail, readerWriter); err != nil { + if parentError, ok := err.(tag.ParentTagNotExistError); ok { + pendingParent[parentError.MissingParent()] = append(pendingParent[parentError.MissingParent()], tagJSON) + continue + } + + return fmt.Errorf("failed to create child tag <%s>: %s", childTagJSON.Name, err.Error()) + } + } + + delete(pendingParent, tagJSON.Name) + + return nil +} + func (t *ImportTask) ImportScrapedItems(ctx context.Context) { if err := t.txnManager.WithTxn(ctx, func(r models.Repository) error { logger.Info("[scraped sites] importing") diff --git a/pkg/models/mocks/TagReaderWriter.go b/pkg/models/mocks/TagReaderWriter.go index f7cabb8ce..f6b257ed2 100644 --- a/pkg/models/mocks/TagReaderWriter.go +++ b/pkg/models/mocks/TagReaderWriter.go @@ -130,6 +130,75 @@ func (_m *TagReaderWriter) Find(id int) (*models.Tag, error) { return r0, r1 } +// FindAllAncestors provides a mock function with given fields: tagID, excludeIDs +func (_m *TagReaderWriter) FindAllAncestors(tagID int, excludeIDs []int) ([]*models.Tag, error) { + ret := _m.Called(tagID, excludeIDs) + + var r0 []*models.Tag + if rf, ok := ret.Get(0).(func(int, []int) []*models.Tag); ok { + r0 = rf(tagID, excludeIDs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*models.Tag) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(int, []int) error); ok { + r1 = rf(tagID, excludeIDs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindAllDescendants provides a mock function with given fields: tagID, excludeIDs +func (_m *TagReaderWriter) FindAllDescendants(tagID int, excludeIDs []int) ([]*models.Tag, error) { + ret := _m.Called(tagID, excludeIDs) + + var r0 []*models.Tag + if rf, ok := ret.Get(0).(func(int, []int) []*models.Tag); ok { + r0 = rf(tagID, excludeIDs) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*models.Tag) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(int, []int) error); ok { + r1 = rf(tagID, excludeIDs) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// FindByChildTagID provides a mock function with given fields: childID +func (_m *TagReaderWriter) FindByChildTagID(childID int) ([]*models.Tag, error) { + ret := _m.Called(childID) + + var r0 []*models.Tag + if rf, ok := ret.Get(0).(func(int) []*models.Tag); ok { + r0 = rf(childID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*models.Tag) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(int) error); ok { + r1 = rf(childID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // FindByGalleryID provides a mock function with given fields: galleryID func (_m *TagReaderWriter) FindByGalleryID(galleryID int) ([]*models.Tag, error) { ret := _m.Called(galleryID) @@ -222,6 +291,29 @@ func (_m *TagReaderWriter) FindByNames(names []string, nocase bool) ([]*models.T return r0, r1 } +// FindByParentTagID provides a mock function with given fields: parentID +func (_m *TagReaderWriter) FindByParentTagID(parentID int) ([]*models.Tag, error) { + ret := _m.Called(parentID) + + var r0 []*models.Tag + if rf, ok := ret.Get(0).(func(int) []*models.Tag); ok { + r0 = rf(parentID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*models.Tag) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(int) error); ok { + r1 = rf(parentID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // FindByPerformerID provides a mock function with given fields: performerID func (_m *TagReaderWriter) FindByPerformerID(performerID int) ([]*models.Tag, error) { ret := _m.Called(performerID) @@ -464,6 +556,20 @@ func (_m *TagReaderWriter) UpdateAliases(tagID int, aliases []string) error { return r0 } +// UpdateChildTags provides a mock function with given fields: tagID, parentIDs +func (_m *TagReaderWriter) UpdateChildTags(tagID int, parentIDs []int) error { + ret := _m.Called(tagID, parentIDs) + + var r0 error + if rf, ok := ret.Get(0).(func(int, []int) error); ok { + r0 = rf(tagID, parentIDs) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // UpdateFull provides a mock function with given fields: updatedTag func (_m *TagReaderWriter) UpdateFull(updatedTag models.Tag) (*models.Tag, error) { ret := _m.Called(updatedTag) @@ -500,3 +606,17 @@ func (_m *TagReaderWriter) UpdateImage(tagID int, image []byte) error { return r0 } + +// UpdateParentTags provides a mock function with given fields: tagID, parentIDs +func (_m *TagReaderWriter) UpdateParentTags(tagID int, parentIDs []int) error { + ret := _m.Called(tagID, parentIDs) + + var r0 error + if rf, ok := ret.Get(0).(func(int, []int) error); ok { + r0 = rf(tagID, parentIDs) + } else { + r0 = ret.Error(0) + } + + return r0 +} diff --git a/pkg/models/tag.go b/pkg/models/tag.go index 4d3e0d84e..bcb7096f0 100644 --- a/pkg/models/tag.go +++ b/pkg/models/tag.go @@ -10,6 +10,8 @@ type TagReader interface { FindByGalleryID(galleryID int) ([]*Tag, error) FindByName(name string, nocase bool) (*Tag, error) FindByNames(names []string, nocase bool) ([]*Tag, error) + FindByParentTagID(parentID int) ([]*Tag, error) + FindByChildTagID(childID int) ([]*Tag, error) Count() (int, error) All() ([]*Tag, error) // TODO - this interface is temporary until the filter schema can fully @@ -18,6 +20,8 @@ type TagReader interface { Query(tagFilter *TagFilterType, findFilter *FindFilterType) ([]*Tag, int, error) GetImage(tagID int) ([]byte, error) GetAliases(tagID int) ([]string, error) + FindAllAncestors(tagID int, excludeIDs []int) ([]*Tag, error) + FindAllDescendants(tagID int, excludeIDs []int) ([]*Tag, error) } type TagWriter interface { @@ -29,6 +33,8 @@ type TagWriter interface { DestroyImage(tagID int) error UpdateAliases(tagID int, aliases []string) error Merge(source []int, destination int) error + UpdateParentTags(tagID int, parentIDs []int) error + UpdateChildTags(tagID int, parentIDs []int) error } type TagReaderWriter interface { diff --git a/pkg/sqlite/filter.go b/pkg/sqlite/filter.go index d83ce12b0..0d47c2fb2 100644 --- a/pkg/sqlite/filter.go +++ b/pkg/sqlite/filter.go @@ -3,7 +3,9 @@ package sqlite import ( "errors" "fmt" + "github.com/stashapp/stash/pkg/logger" "regexp" + "strconv" "strings" "github.com/stashapp/stash/pkg/models" @@ -97,6 +99,7 @@ type filterBuilder struct { whereClauses []sqlClause havingClauses []sqlClause withClauses []sqlClause + recursiveWith bool err error } @@ -188,6 +191,16 @@ func (f *filterBuilder) addWith(sql string, args ...interface{}) { f.withClauses = append(f.withClauses, makeClause(sql, args...)) } +// addRecursiveWith adds a with clause and arguments to the filter, and sets it to recursive +func (f *filterBuilder) addRecursiveWith(sql string, args ...interface{}) { + if sql == "" { + return + } + + f.addWith(sql, args...) + f.recursiveWith = true +} + func (f *filterBuilder) getSubFilterClause(clause, subFilterClause string) string { ret := clause @@ -510,18 +523,41 @@ func (m *stringListCriterionHandlerBuilder) handler(criterion *models.StringCrit } type hierarchicalMultiCriterionHandlerBuilder struct { + tx dbi + primaryTable string foreignTable string foreignFK string - derivedTable string - parentFK string + derivedTable string + parentFK string + relationsTable string } -func addHierarchicalWithClause(f *filterBuilder, value []string, derivedTable, table, parentFK string, depth int) { +func getHierarchicalValues(tx dbi, values []string, table, relationsTable, parentFK string, depth int) string { var args []interface{} - for _, value := range value { + if depth == 0 { + valid := true + var valuesClauses []string + for _, value := range values { + id, err := strconv.Atoi(value) + // In case of invalid value just run the query. + // Building VALUES() based on provided values just saves a query when depth is 0. + if err != nil { + valid = false + break + } + + valuesClauses = append(valuesClauses, fmt.Sprintf("(%d,%d)", id, id)) + } + + if valid { + return "VALUES" + strings.Join(valuesClauses, ",") + } + } + + for _, value := range values { args = append(args, value) } inCount := len(args) @@ -532,45 +568,107 @@ func addHierarchicalWithClause(f *filterBuilder, value []string, derivedTable, t } withClauseMap := utils.StrFormatMap{ - "derivedTable": derivedTable, - "table": table, - "inBinding": getInBinding(inCount), - "parentFK": parentFK, - "depthCondition": depthCondition, - "unionClause": "", + "table": table, + "relationsTable": relationsTable, + "inBinding": getInBinding(inCount), + "recursiveSelect": "", + "parentFK": parentFK, + "depthCondition": depthCondition, + "unionClause": "", + } + + if relationsTable != "" { + withClauseMap["recursiveSelect"] = utils.StrFormat(`SELECT p.root_id, c.child_id, depth + 1 FROM {relationsTable} AS c +INNER JOIN items as p ON c.parent_id = p.item_id +`, withClauseMap) + } else { + withClauseMap["recursiveSelect"] = utils.StrFormat(`SELECT p.root_id, c.id, depth + 1 FROM {table} as c +INNER JOIN items as p ON c.{parentFK} = p.item_id +`, withClauseMap) } if depth != 0 { withClauseMap["unionClause"] = utils.StrFormat(` -UNION SELECT p.id, c.id, depth + 1 FROM {table} as c -INNER JOIN {derivedTable} as p ON c.{parentFK} = p.child_id {depthCondition} +UNION {recursiveSelect} {depthCondition} `, withClauseMap) } - withClause := utils.StrFormat(`RECURSIVE {derivedTable} AS ( -SELECT id as id, id as child_id, 0 as depth FROM {table} + withClause := utils.StrFormat(`items AS ( +SELECT id as root_id, id as item_id, 0 as depth FROM {table} WHERE id in {inBinding} {unionClause}) `, withClauseMap) - f.addWith(withClause, args...) + query := fmt.Sprintf("WITH RECURSIVE %s SELECT 'VALUES' || GROUP_CONCAT('(' || root_id || ', ' || item_id || ')') AS val FROM items", withClause) + + var valuesClause string + err := tx.Get(&valuesClause, query, args...) + if err != nil { + logger.Error(err) + // return record which never matches so we don't have to handle error here + return "VALUES(NULL, NULL)" + } + + return valuesClause +} + +func addHierarchicalConditionClauses(f *filterBuilder, criterion *models.HierarchicalMultiCriterionInput, table, idColumn string) { + if criterion.Modifier == models.CriterionModifierIncludes { + f.addWhere(fmt.Sprintf("%s.%s IS NOT NULL", table, idColumn)) + } else if criterion.Modifier == models.CriterionModifierIncludesAll { + f.addWhere(fmt.Sprintf("%s.%s IS NOT NULL", table, idColumn)) + f.addHaving(fmt.Sprintf("count(distinct %s.%s) IS %d", table, idColumn, len(criterion.Value))) + } else if criterion.Modifier == models.CriterionModifierExcludes { + f.addWhere(fmt.Sprintf("%s.%s IS NULL", table, idColumn)) + } } func (m *hierarchicalMultiCriterionHandlerBuilder) handler(criterion *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { return func(f *filterBuilder) { if criterion != nil && len(criterion.Value) > 0 { - addHierarchicalWithClause(f, criterion.Value, m.derivedTable, m.foreignTable, m.parentFK, criterion.Depth) + valuesClause := getHierarchicalValues(m.tx, criterion.Value, m.foreignTable, m.relationsTable, m.parentFK, criterion.Depth) - f.addJoin(m.derivedTable, "", fmt.Sprintf("%s.child_id = %s.%s", m.derivedTable, m.primaryTable, m.foreignFK)) + f.addJoin("(SELECT column1 AS root_id, column2 AS item_id FROM ("+valuesClause+"))", m.derivedTable, fmt.Sprintf("%s.item_id = %s.%s", m.derivedTable, m.primaryTable, m.foreignFK)) - if criterion.Modifier == models.CriterionModifierIncludes { - f.addWhere(fmt.Sprintf("%s.id IS NOT NULL", m.derivedTable)) - } else if criterion.Modifier == models.CriterionModifierIncludesAll { - f.addWhere(fmt.Sprintf("%s.id IS NOT NULL", m.derivedTable)) - f.addHaving(fmt.Sprintf("count(distinct %s.id) IS %d", m.derivedTable, len(criterion.Value))) - } else if criterion.Modifier == models.CriterionModifierExcludes { - f.addWhere(fmt.Sprintf("%s.id IS NULL", m.derivedTable)) - } + addHierarchicalConditionClauses(f, criterion, m.derivedTable, "root_id") + } + } +} + +type joinedHierarchicalMultiCriterionHandlerBuilder struct { + tx dbi + + primaryTable string + foreignTable string + foreignFK string + + parentFK string + relationsTable string + + joinAs string + joinTable string + primaryFK string +} + +func (m *joinedHierarchicalMultiCriterionHandlerBuilder) handler(criterion *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { + return func(f *filterBuilder) { + if criterion != nil && len(criterion.Value) > 0 { + valuesClause := getHierarchicalValues(m.tx, criterion.Value, m.foreignTable, m.relationsTable, m.parentFK, criterion.Depth) + + joinAlias := m.joinAs + joinTable := utils.StrFormat(`( + SELECT j.*, d.column1 AS root_id, d.column2 AS item_id FROM {joinTable} AS j + INNER JOIN ({valuesClause}) AS d ON j.{foreignFK} = d.column2 +) +`, utils.StrFormatMap{ + "joinTable": m.joinTable, + "foreignFK": m.foreignFK, + "valuesClause": valuesClause, + }) + + f.addJoin(joinTable, joinAlias, fmt.Sprintf("%s.%s = %s.id", joinAlias, m.primaryFK, m.primaryTable)) + + addHierarchicalConditionClauses(f, criterion, joinAlias, "root_id") } } } diff --git a/pkg/sqlite/gallery.go b/pkg/sqlite/gallery.go index 30f13c75c..2564c068b 100644 --- a/pkg/sqlite/gallery.go +++ b/pkg/sqlite/gallery.go @@ -311,17 +311,18 @@ func galleryIsMissingCriterionHandler(qb *galleryQueryBuilder, isMissing *string } } -func galleryTagsCriterionHandler(qb *galleryQueryBuilder, tags *models.MultiCriterionInput) criterionHandlerFunc { - h := joinedMultiCriterionHandlerBuilder{ - primaryTable: galleryTable, - joinTable: galleriesTagsTable, - joinAs: "tags_join", - primaryFK: galleryIDColumn, - foreignFK: tagIDColumn, +func galleryTagsCriterionHandler(qb *galleryQueryBuilder, tags *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { + h := joinedHierarchicalMultiCriterionHandlerBuilder{ + tx: qb.tx, - addJoinTable: func(f *filterBuilder) { - qb.tagsRepository().join(f, "tags_join", "galleries.id") - }, + primaryTable: galleryTable, + foreignTable: tagTable, + foreignFK: "tag_id", + + relationsTable: "tags_relations", + joinAs: "image_tag", + joinTable: galleriesTagsTable, + primaryFK: galleryIDColumn, } return h.handler(tags) @@ -375,6 +376,8 @@ func galleryImageCountCriterionHandler(qb *galleryQueryBuilder, imageCount *mode func galleryStudioCriterionHandler(qb *galleryQueryBuilder, studios *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { h := hierarchicalMultiCriterionHandlerBuilder{ + tx: qb.tx, + primaryTable: galleryTable, foreignTable: studioTable, foreignFK: studioIDColumn, @@ -385,31 +388,20 @@ func galleryStudioCriterionHandler(qb *galleryQueryBuilder, studios *models.Hier return h.handler(studios) } -func galleryPerformerTagsCriterionHandler(qb *galleryQueryBuilder, performerTagsFilter *models.MultiCriterionInput) criterionHandlerFunc { +func galleryPerformerTagsCriterionHandler(qb *galleryQueryBuilder, tags *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { return func(f *filterBuilder) { - if performerTagsFilter != nil && len(performerTagsFilter.Value) > 0 { - qb.performersRepository().join(f, "performers_join", "galleries.id") - f.addJoin("performers_tags", "performer_tags_join", "performers_join.performer_id = performer_tags_join.performer_id") + if tags != nil && len(tags.Value) > 0 { + valuesClause := getHierarchicalValues(qb.tx, tags.Value, tagTable, "tags_relations", "", tags.Depth) - var args []interface{} - for _, tagID := range performerTagsFilter.Value { - args = append(args, tagID) - } + f.addWith(`performer_tags AS ( +SELECT pg.gallery_id, t.column1 AS root_tag_id FROM performers_galleries pg +INNER JOIN performers_tags pt ON pt.performer_id = pg.performer_id +INNER JOIN (` + valuesClause + `) t ON t.column2 = pt.tag_id +)`) - if performerTagsFilter.Modifier == models.CriterionModifierIncludes { - // includes any of the provided ids - f.addWhere("performer_tags_join.tag_id IN "+getInBinding(len(performerTagsFilter.Value)), args...) - } else if performerTagsFilter.Modifier == models.CriterionModifierIncludesAll { - // includes all of the provided ids - f.addWhere("performer_tags_join.tag_id IN "+getInBinding(len(performerTagsFilter.Value)), args...) - f.addHaving(fmt.Sprintf("count(distinct performer_tags_join.tag_id) IS %d", len(performerTagsFilter.Value))) - } else if performerTagsFilter.Modifier == models.CriterionModifierExcludes { - f.addWhere(fmt.Sprintf(`not exists - (select performers_galleries.performer_id from performers_galleries - left join performers_tags on performers_tags.performer_id = performers_galleries.performer_id where - performers_galleries.gallery_id = galleries.id AND - performers_tags.tag_id in %s)`, getInBinding(len(performerTagsFilter.Value))), args...) - } + f.addJoin("performer_tags", "", "performer_tags.gallery_id = galleries.id") + + addHierarchicalConditionClauses(f, tags, "performer_tags", "root_tag_id") } } } diff --git a/pkg/sqlite/gallery_test.go b/pkg/sqlite/gallery_test.go index 03f03a544..4ba755b00 100644 --- a/pkg/sqlite/gallery_test.go +++ b/pkg/sqlite/gallery_test.go @@ -621,12 +621,13 @@ func TestGalleryQueryPerformers(t *testing.T) { func TestGalleryQueryTags(t *testing.T) { withTxn(func(r models.Repository) error { sqb := r.Gallery() - tagCriterion := models.MultiCriterionInput{ + tagCriterion := models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdxWithGallery]), strconv.Itoa(tagIDs[tagIdx1WithGallery]), }, Modifier: models.CriterionModifierIncludes, + Depth: 0, } galleryFilter := models.GalleryFilterType{ @@ -641,12 +642,13 @@ func TestGalleryQueryTags(t *testing.T) { assert.True(t, gallery.ID == galleryIDs[galleryIdxWithTag] || gallery.ID == galleryIDs[galleryIdxWithTwoTags]) } - tagCriterion = models.MultiCriterionInput{ + tagCriterion = models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdx1WithGallery]), strconv.Itoa(tagIDs[tagIdx2WithGallery]), }, Modifier: models.CriterionModifierIncludesAll, + Depth: 0, } galleries = queryGallery(t, sqb, &galleryFilter, nil) @@ -654,11 +656,12 @@ func TestGalleryQueryTags(t *testing.T) { assert.Len(t, galleries, 1) assert.Equal(t, galleryIDs[galleryIdxWithTwoTags], galleries[0].ID) - tagCriterion = models.MultiCriterionInput{ + tagCriterion = models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdx1WithGallery]), }, Modifier: models.CriterionModifierExcludes, + Depth: 0, } q := getGalleryStringValue(galleryIdxWithTwoTags, titleField) @@ -776,12 +779,13 @@ func TestGalleryQueryStudioDepth(t *testing.T) { func TestGalleryQueryPerformerTags(t *testing.T) { withTxn(func(r models.Repository) error { sqb := r.Gallery() - tagCriterion := models.MultiCriterionInput{ + tagCriterion := models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdxWithPerformer]), strconv.Itoa(tagIDs[tagIdx1WithPerformer]), }, Modifier: models.CriterionModifierIncludes, + Depth: 0, } galleryFilter := models.GalleryFilterType{ @@ -796,12 +800,13 @@ func TestGalleryQueryPerformerTags(t *testing.T) { assert.True(t, gallery.ID == galleryIDs[galleryIdxWithPerformerTag] || gallery.ID == galleryIDs[galleryIdxWithPerformerTwoTags]) } - tagCriterion = models.MultiCriterionInput{ + tagCriterion = models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdx1WithPerformer]), strconv.Itoa(tagIDs[tagIdx2WithPerformer]), }, Modifier: models.CriterionModifierIncludesAll, + Depth: 0, } galleries = queryGallery(t, sqb, &galleryFilter, nil) @@ -809,11 +814,12 @@ func TestGalleryQueryPerformerTags(t *testing.T) { assert.Len(t, galleries, 1) assert.Equal(t, galleryIDs[galleryIdxWithPerformerTwoTags], galleries[0].ID) - tagCriterion = models.MultiCriterionInput{ + tagCriterion = models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdx1WithPerformer]), }, Modifier: models.CriterionModifierExcludes, + Depth: 0, } q := getGalleryStringValue(galleryIdxWithPerformerTwoTags, titleField) diff --git a/pkg/sqlite/image.go b/pkg/sqlite/image.go index d0b6f16f8..01598c51a 100644 --- a/pkg/sqlite/image.go +++ b/pkg/sqlite/image.go @@ -348,17 +348,18 @@ func (qb *imageQueryBuilder) getMultiCriterionHandlerBuilder(foreignTable, joinT } } -func imageTagsCriterionHandler(qb *imageQueryBuilder, tags *models.MultiCriterionInput) criterionHandlerFunc { - h := joinedMultiCriterionHandlerBuilder{ - primaryTable: imageTable, - joinTable: imagesTagsTable, - joinAs: "tags_join", - primaryFK: imageIDColumn, - foreignFK: tagIDColumn, +func imageTagsCriterionHandler(qb *imageQueryBuilder, tags *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { + h := joinedHierarchicalMultiCriterionHandlerBuilder{ + tx: qb.tx, - addJoinTable: func(f *filterBuilder) { - qb.tagsRepository().join(f, "tags_join", "images.id") - }, + primaryTable: imageTable, + foreignTable: tagTable, + foreignFK: "tag_id", + + relationsTable: "tags_relations", + joinAs: "image_tag", + joinTable: imagesTagsTable, + primaryFK: imageIDColumn, } return h.handler(tags) @@ -412,6 +413,8 @@ func imagePerformerCountCriterionHandler(qb *imageQueryBuilder, performerCount * func imageStudioCriterionHandler(qb *imageQueryBuilder, studios *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { h := hierarchicalMultiCriterionHandlerBuilder{ + tx: qb.tx, + primaryTable: imageTable, foreignTable: studioTable, foreignFK: studioIDColumn, @@ -422,31 +425,20 @@ func imageStudioCriterionHandler(qb *imageQueryBuilder, studios *models.Hierarch return h.handler(studios) } -func imagePerformerTagsCriterionHandler(qb *imageQueryBuilder, performerTagsFilter *models.MultiCriterionInput) criterionHandlerFunc { +func imagePerformerTagsCriterionHandler(qb *imageQueryBuilder, tags *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { return func(f *filterBuilder) { - if performerTagsFilter != nil && len(performerTagsFilter.Value) > 0 { - qb.performersRepository().join(f, "performers_join", "images.id") - f.addJoin("performers_tags", "performer_tags_join", "performers_join.performer_id = performer_tags_join.performer_id") + if tags != nil && len(tags.Value) > 0 { + valuesClause := getHierarchicalValues(qb.tx, tags.Value, tagTable, "tags_relations", "", tags.Depth) - var args []interface{} - for _, tagID := range performerTagsFilter.Value { - args = append(args, tagID) - } + f.addWith(`performer_tags AS ( +SELECT pi.image_id, t.column1 AS root_tag_id FROM performers_images pi +INNER JOIN performers_tags pt ON pt.performer_id = pi.performer_id +INNER JOIN (` + valuesClause + `) t ON t.column2 = pt.tag_id +)`) - if performerTagsFilter.Modifier == models.CriterionModifierIncludes { - // includes any of the provided ids - f.addWhere("performer_tags_join.tag_id IN "+getInBinding(len(performerTagsFilter.Value)), args...) - } else if performerTagsFilter.Modifier == models.CriterionModifierIncludesAll { - // includes all of the provided ids - f.addWhere("performer_tags_join.tag_id IN "+getInBinding(len(performerTagsFilter.Value)), args...) - f.addHaving(fmt.Sprintf("count(distinct performer_tags_join.tag_id) IS %d", len(performerTagsFilter.Value))) - } else if performerTagsFilter.Modifier == models.CriterionModifierExcludes { - f.addWhere(fmt.Sprintf(`not exists - (select performers_images.performer_id from performers_images - left join performers_tags on performers_tags.performer_id = performers_images.performer_id where - performers_images.image_id = images.id AND - performers_tags.tag_id in %s)`, getInBinding(len(performerTagsFilter.Value))), args...) - } + f.addJoin("performer_tags", "", "performer_tags.image_id = images.id") + + addHierarchicalConditionClauses(f, tags, "performer_tags", "root_tag_id") } } } diff --git a/pkg/sqlite/image_test.go b/pkg/sqlite/image_test.go index 6d866041f..02e077c8d 100644 --- a/pkg/sqlite/image_test.go +++ b/pkg/sqlite/image_test.go @@ -722,12 +722,13 @@ func TestImageQueryPerformers(t *testing.T) { func TestImageQueryTags(t *testing.T) { withTxn(func(r models.Repository) error { sqb := r.Image() - tagCriterion := models.MultiCriterionInput{ + tagCriterion := models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdxWithImage]), strconv.Itoa(tagIDs[tagIdx1WithImage]), }, Modifier: models.CriterionModifierIncludes, + Depth: 0, } imageFilter := models.ImageFilterType{ @@ -746,12 +747,13 @@ func TestImageQueryTags(t *testing.T) { assert.True(t, image.ID == imageIDs[imageIdxWithTag] || image.ID == imageIDs[imageIdxWithTwoTags]) } - tagCriterion = models.MultiCriterionInput{ + tagCriterion = models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdx1WithImage]), strconv.Itoa(tagIDs[tagIdx2WithImage]), }, Modifier: models.CriterionModifierIncludesAll, + Depth: 0, } images, _, err = sqb.Query(&imageFilter, nil) @@ -762,11 +764,12 @@ func TestImageQueryTags(t *testing.T) { assert.Len(t, images, 1) assert.Equal(t, imageIDs[imageIdxWithTwoTags], images[0].ID) - tagCriterion = models.MultiCriterionInput{ + tagCriterion = models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdx1WithImage]), }, Modifier: models.CriterionModifierExcludes, + Depth: 0, } q := getImageStringValue(imageIdxWithTwoTags, titleField) @@ -902,12 +905,13 @@ func queryImages(t *testing.T, sqb models.ImageReader, imageFilter *models.Image func TestImageQueryPerformerTags(t *testing.T) { withTxn(func(r models.Repository) error { sqb := r.Image() - tagCriterion := models.MultiCriterionInput{ + tagCriterion := models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdxWithPerformer]), strconv.Itoa(tagIDs[tagIdx1WithPerformer]), }, Modifier: models.CriterionModifierIncludes, + Depth: 0, } imageFilter := models.ImageFilterType{ @@ -922,12 +926,13 @@ func TestImageQueryPerformerTags(t *testing.T) { assert.True(t, image.ID == imageIDs[imageIdxWithPerformerTag] || image.ID == imageIDs[imageIdxWithPerformerTwoTags]) } - tagCriterion = models.MultiCriterionInput{ + tagCriterion = models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdx1WithPerformer]), strconv.Itoa(tagIDs[tagIdx2WithPerformer]), }, Modifier: models.CriterionModifierIncludesAll, + Depth: 0, } images = queryImages(t, sqb, &imageFilter, nil) @@ -935,11 +940,12 @@ func TestImageQueryPerformerTags(t *testing.T) { assert.Len(t, images, 1) assert.Equal(t, imageIDs[imageIdxWithPerformerTwoTags], images[0].ID) - tagCriterion = models.MultiCriterionInput{ + tagCriterion = models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdx1WithPerformer]), }, Modifier: models.CriterionModifierExcludes, + Depth: 0, } q := getImageStringValue(imageIdxWithPerformerTwoTags, titleField) diff --git a/pkg/sqlite/movies.go b/pkg/sqlite/movies.go index 9e69b8451..92c3636e3 100644 --- a/pkg/sqlite/movies.go +++ b/pkg/sqlite/movies.go @@ -195,6 +195,8 @@ func movieIsMissingCriterionHandler(qb *movieQueryBuilder, isMissing *string) cr func movieStudioCriterionHandler(qb *movieQueryBuilder, studios *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { h := hierarchicalMultiCriterionHandlerBuilder{ + tx: qb.tx, + primaryTable: movieTable, foreignTable: studioTable, foreignFK: studioIDColumn, diff --git a/pkg/sqlite/performer.go b/pkg/sqlite/performer.go index 526a6e360..c8b3f86de 100644 --- a/pkg/sqlite/performer.go +++ b/pkg/sqlite/performer.go @@ -280,7 +280,7 @@ func (qb *performerQueryBuilder) makeFilter(filter *models.PerformerFilterType) query.handleCriterion(performerTagsCriterionHandler(qb, filter.Tags)) - query.handleCriterion(performerStudiosCriterionHandler(filter.Studios)) + query.handleCriterion(performerStudiosCriterionHandler(qb, filter.Studios)) query.handleCriterion(performerTagCountCriterionHandler(qb, filter.TagCount)) query.handleCriterion(performerSceneCountCriterionHandler(qb, filter.SceneCount)) @@ -376,17 +376,18 @@ func performerAgeFilterCriterionHandler(age *models.IntCriterionInput) criterion } } -func performerTagsCriterionHandler(qb *performerQueryBuilder, tags *models.MultiCriterionInput) criterionHandlerFunc { - h := joinedMultiCriterionHandlerBuilder{ - primaryTable: performerTable, - joinTable: performersTagsTable, - joinAs: "tags_join", - primaryFK: performerIDColumn, - foreignFK: tagIDColumn, +func performerTagsCriterionHandler(qb *performerQueryBuilder, tags *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { + h := joinedHierarchicalMultiCriterionHandlerBuilder{ + tx: qb.tx, - addJoinTable: func(f *filterBuilder) { - qb.tagsRepository().join(f, "tags_join", "performers.id") - }, + primaryTable: performerTable, + foreignTable: tagTable, + foreignFK: "tag_id", + + relationsTable: "tags_relations", + joinAs: "image_tag", + joinTable: performersTagsTable, + primaryFK: performerIDColumn, } return h.handler(tags) @@ -432,7 +433,7 @@ func performerGalleryCountCriterionHandler(qb *performerQueryBuilder, count *mod return h.handler(count) } -func performerStudiosCriterionHandler(studios *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { +func performerStudiosCriterionHandler(qb *performerQueryBuilder, studios *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { return func(f *filterBuilder) { if studios != nil { var clauseCondition string @@ -465,13 +466,13 @@ func performerStudiosCriterionHandler(studios *models.HierarchicalMultiCriterion }, } - const derivedStudioTable = "studio" const derivedPerformerStudioTable = "performer_studio" - addHierarchicalWithClause(f, studios.Value, derivedStudioTable, studioTable, "parent_id", studios.Depth) + valuesClause := getHierarchicalValues(qb.tx, studios.Value, studioTable, "", "parent_id", studios.Depth) + f.addWith("studio(root_id, item_id) AS (" + valuesClause + ")") templStr := `SELECT performer_id FROM {primaryTable} INNER JOIN {joinTable} ON {primaryTable}.id = {joinTable}.{primaryFK} - INNER JOIN studio ON {primaryTable}.studio_id = studio.child_id` + INNER JOIN studio ON {primaryTable}.studio_id = studio.item_id` var unions []string for _, c := range formatMaps { diff --git a/pkg/sqlite/performer_test.go b/pkg/sqlite/performer_test.go index a68e09e03..4ce990c35 100644 --- a/pkg/sqlite/performer_test.go +++ b/pkg/sqlite/performer_test.go @@ -500,12 +500,13 @@ func queryPerformers(t *testing.T, qb models.PerformerReader, performerFilter *m func TestPerformerQueryTags(t *testing.T) { withTxn(func(r models.Repository) error { sqb := r.Performer() - tagCriterion := models.MultiCriterionInput{ + tagCriterion := models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdxWithPerformer]), strconv.Itoa(tagIDs[tagIdx1WithPerformer]), }, Modifier: models.CriterionModifierIncludes, + Depth: 0, } performerFilter := models.PerformerFilterType{ @@ -519,12 +520,13 @@ func TestPerformerQueryTags(t *testing.T) { assert.True(t, performer.ID == performerIDs[performerIdxWithTag] || performer.ID == performerIDs[performerIdxWithTwoTags]) } - tagCriterion = models.MultiCriterionInput{ + tagCriterion = models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdx1WithPerformer]), strconv.Itoa(tagIDs[tagIdx2WithPerformer]), }, Modifier: models.CriterionModifierIncludesAll, + Depth: 0, } performers = queryPerformers(t, sqb, &performerFilter, nil) @@ -532,11 +534,12 @@ func TestPerformerQueryTags(t *testing.T) { assert.Len(t, performers, 1) assert.Equal(t, sceneIDs[performerIdxWithTwoTags], performers[0].ID) - tagCriterion = models.MultiCriterionInput{ + tagCriterion = models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdx1WithPerformer]), }, Modifier: models.CriterionModifierExcludes, + Depth: 0, } q := getSceneStringValue(performerIdxWithTwoTags, titleField) diff --git a/pkg/sqlite/query.go b/pkg/sqlite/query.go index 2d31ee583..158e7f971 100644 --- a/pkg/sqlite/query.go +++ b/pkg/sqlite/query.go @@ -18,6 +18,7 @@ type queryBuilder struct { havingClauses []string args []interface{} withClauses []string + recursiveWith bool sortAndPagination string @@ -32,7 +33,7 @@ func (qb queryBuilder) executeFind() ([]int, int, error) { body := qb.body body += qb.joins.toSQL() - return qb.repository.executeFindQuery(body, qb.args, qb.sortAndPagination, qb.whereClauses, qb.havingClauses, qb.withClauses) + return qb.repository.executeFindQuery(body, qb.args, qb.sortAndPagination, qb.whereClauses, qb.havingClauses, qb.withClauses, qb.recursiveWith) } func (qb queryBuilder) executeCount() (int, error) { @@ -45,7 +46,11 @@ func (qb queryBuilder) executeCount() (int, error) { withClause := "" if len(qb.withClauses) > 0 { - withClause = "WITH " + strings.Join(qb.withClauses, ", ") + " " + var recursive string + if qb.recursiveWith { + recursive = " RECURSIVE " + } + withClause = "WITH " + recursive + strings.Join(qb.withClauses, ", ") + " " } body = qb.repository.buildQueryBody(body, qb.whereClauses, qb.havingClauses) @@ -69,12 +74,14 @@ func (qb *queryBuilder) addHaving(clauses ...string) { } } -func (qb *queryBuilder) addWith(clauses ...string) { +func (qb *queryBuilder) addWith(recursive bool, clauses ...string) { for _, clause := range clauses { if len(clause) > 0 { qb.withClauses = append(qb.withClauses, clause) } } + + qb.recursiveWith = qb.recursiveWith || recursive } func (qb *queryBuilder) addArg(args ...interface{}) { @@ -104,7 +111,7 @@ func (qb *queryBuilder) addFilter(f *filterBuilder) { clause, args := f.generateWithClauses() if len(clause) > 0 { - qb.addWith(clause) + qb.addWith(f.recursiveWith, clause) } if len(args) > 0 { diff --git a/pkg/sqlite/repository.go b/pkg/sqlite/repository.go index 6550d0d67..ffe5b78ad 100644 --- a/pkg/sqlite/repository.go +++ b/pkg/sqlite/repository.go @@ -246,12 +246,16 @@ func (r *repository) buildQueryBody(body string, whereClauses []string, havingCl return body } -func (r *repository) executeFindQuery(body string, args []interface{}, sortAndPagination string, whereClauses []string, havingClauses []string, withClauses []string) ([]int, int, error) { +func (r *repository) executeFindQuery(body string, args []interface{}, sortAndPagination string, whereClauses []string, havingClauses []string, withClauses []string, recursiveWith bool) ([]int, int, error) { body = r.buildQueryBody(body, whereClauses, havingClauses) withClause := "" if len(withClauses) > 0 { - withClause = "WITH " + strings.Join(withClauses, ", ") + " " + var recursive string + if recursiveWith { + recursive = " RECURSIVE " + } + withClause = "WITH " + recursive + strings.Join(withClauses, ", ") + " " } countQuery := withClause + r.buildCountQuery(body) diff --git a/pkg/sqlite/scene.go b/pkg/sqlite/scene.go index 92cdd5c2c..5aa0d4722 100644 --- a/pkg/sqlite/scene.go +++ b/pkg/sqlite/scene.go @@ -548,12 +548,19 @@ func (qb *sceneQueryBuilder) getMultiCriterionHandlerBuilder(foreignTable, joinT } } -func sceneTagsCriterionHandler(qb *sceneQueryBuilder, tags *models.MultiCriterionInput) criterionHandlerFunc { - addJoinsFunc := func(f *filterBuilder) { - qb.tagsRepository().join(f, "tags_join", "scenes.id") - f.addJoin("tags", "", "tags_join.tag_id = tags.id") +func sceneTagsCriterionHandler(qb *sceneQueryBuilder, tags *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { + h := joinedHierarchicalMultiCriterionHandlerBuilder{ + tx: qb.tx, + + primaryTable: sceneTable, + foreignTable: tagTable, + foreignFK: "tag_id", + + relationsTable: "tags_relations", + joinAs: "scene_tag", + joinTable: scenesTagsTable, + primaryFK: sceneIDColumn, } - h := qb.getMultiCriterionHandlerBuilder(tagTable, scenesTagsTable, tagIDColumn, addJoinsFunc) return h.handler(tags) } @@ -596,6 +603,8 @@ func scenePerformerCountCriterionHandler(qb *sceneQueryBuilder, performerCount * func sceneStudioCriterionHandler(qb *sceneQueryBuilder, studios *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { h := hierarchicalMultiCriterionHandlerBuilder{ + tx: qb.tx, + primaryTable: sceneTable, foreignTable: studioTable, foreignFK: studioIDColumn, @@ -615,31 +624,20 @@ func sceneMoviesCriterionHandler(qb *sceneQueryBuilder, movies *models.MultiCrit return h.handler(movies) } -func scenePerformerTagsCriterionHandler(qb *sceneQueryBuilder, performerTagsFilter *models.MultiCriterionInput) criterionHandlerFunc { +func scenePerformerTagsCriterionHandler(qb *sceneQueryBuilder, tags *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { return func(f *filterBuilder) { - if performerTagsFilter != nil && len(performerTagsFilter.Value) > 0 { - qb.performersRepository().join(f, "performers_join", "scenes.id") - f.addJoin("performers_tags", "performer_tags_join", "performers_join.performer_id = performer_tags_join.performer_id") + if tags != nil && len(tags.Value) > 0 { + valuesClause := getHierarchicalValues(qb.tx, tags.Value, tagTable, "tags_relations", "", tags.Depth) - var args []interface{} - for _, tagID := range performerTagsFilter.Value { - args = append(args, tagID) - } + f.addWith(`performer_tags AS ( +SELECT ps.scene_id, t.column1 AS root_tag_id FROM performers_scenes ps +INNER JOIN performers_tags pt ON pt.performer_id = ps.performer_id +INNER JOIN (` + valuesClause + `) t ON t.column2 = pt.tag_id +)`) - if performerTagsFilter.Modifier == models.CriterionModifierIncludes { - // includes any of the provided ids - f.addWhere("performer_tags_join.tag_id IN "+getInBinding(len(performerTagsFilter.Value)), args...) - } else if performerTagsFilter.Modifier == models.CriterionModifierIncludesAll { - // includes all of the provided ids - f.addWhere("performer_tags_join.tag_id IN "+getInBinding(len(performerTagsFilter.Value)), args...) - f.addHaving(fmt.Sprintf("count(distinct performer_tags_join.tag_id) IS %d", len(performerTagsFilter.Value))) - } else if performerTagsFilter.Modifier == models.CriterionModifierExcludes { - f.addWhere(fmt.Sprintf(`not exists - (select performers_scenes.performer_id from performers_scenes - left join performers_tags on performers_tags.performer_id = performers_scenes.performer_id where - performers_scenes.scene_id = scenes.id AND - performers_tags.tag_id in %s)`, getInBinding(len(performerTagsFilter.Value))), args...) - } + f.addJoin("performer_tags", "", "performer_tags.scene_id = scenes.id") + + addHierarchicalConditionClauses(f, tags, "performer_tags", "root_tag_id") } } } diff --git a/pkg/sqlite/scene_marker.go b/pkg/sqlite/scene_marker.go index 0ad3cda43..392bfc75d 100644 --- a/pkg/sqlite/scene_marker.go +++ b/pkg/sqlite/scene_marker.go @@ -3,8 +3,6 @@ package sqlite import ( "database/sql" "fmt" - "strconv" - "github.com/stashapp/stash/pkg/database" "github.com/stashapp/stash/pkg/models" ) @@ -127,6 +125,17 @@ func (qb *sceneMarkerQueryBuilder) Wall(q *string) ([]*models.SceneMarker, error return qb.querySceneMarkers(query, nil) } +func (qb *sceneMarkerQueryBuilder) makeFilter(sceneMarkerFilter *models.SceneMarkerFilterType) *filterBuilder { + query := &filterBuilder{} + + query.handleCriterion(sceneMarkerTagIDCriterionHandler(qb, sceneMarkerFilter.TagID)) + query.handleCriterion(sceneMarkerTagsCriterionHandler(qb, sceneMarkerFilter.Tags)) + query.handleCriterion(sceneMarkerSceneTagsCriterionHandler(qb, sceneMarkerFilter.SceneTags)) + query.handleCriterion(sceneMarkerPerformersCriterionHandler(qb, sceneMarkerFilter.Performers)) + + return query +} + func (qb *sceneMarkerQueryBuilder) Query(sceneMarkerFilter *models.SceneMarkerFilterType, findFilter *models.FindFilterType) ([]*models.SceneMarker, int, error) { if sceneMarkerFilter == nil { sceneMarkerFilter = &models.SceneMarkerFilterType{} @@ -135,121 +144,23 @@ func (qb *sceneMarkerQueryBuilder) Query(sceneMarkerFilter *models.SceneMarkerFi findFilter = &models.FindFilterType{} } - var whereClauses []string - var havingClauses []string - var args []interface{} - body := selectDistinctIDs("scene_markers") - body = body + ` - left join tags as primary_tag on primary_tag.id = scene_markers.primary_tag_id - left join scenes as scene on scene.id = scene_markers.scene_id - left join scene_markers_tags as tags_join on tags_join.scene_marker_id = scene_markers.id - left join tags on tags_join.tag_id = tags.id - ` + query := qb.newQuery() - if tagsFilter := sceneMarkerFilter.Tags; tagsFilter != nil && len(tagsFilter.Value) > 0 { - //select `scene_markers`.* from `scene_markers` - //left join `tags` as `primary_tags_join` - // on `primary_tags_join`.`id` = `scene_markers`.`primary_tag_id` - // and `primary_tags_join`.`id` in ('3', '37', '9', '89') - //left join `scene_markers_tags` as `tags_join` - // on `tags_join`.`scene_marker_id` = `scene_markers`.`id` - // and `tags_join`.`tag_id` in ('3', '37', '9', '89') - //group by `scene_markers`.`id` - //having ((count(distinct `primary_tags_join`.`id`) + count(distinct `tags_join`.`tag_id`)) = 4) - - length := len(tagsFilter.Value) - - if tagsFilter.Modifier == models.CriterionModifierIncludes || tagsFilter.Modifier == models.CriterionModifierIncludesAll { - body += " LEFT JOIN tags AS ptj ON ptj.id = scene_markers.primary_tag_id AND ptj.id IN " + getInBinding(length) - body += " LEFT JOIN scene_markers_tags AS tj ON tj.scene_marker_id = scene_markers.id AND tj.tag_id IN " + getInBinding(length) - - // only one required for include any - requiredCount := 1 - - // all required for include all - if tagsFilter.Modifier == models.CriterionModifierIncludesAll { - requiredCount = length - } - - havingClauses = append(havingClauses, "((COUNT(DISTINCT ptj.id) + COUNT(DISTINCT tj.tag_id)) >= "+strconv.Itoa(requiredCount)+")") - } else if tagsFilter.Modifier == models.CriterionModifierExcludes { - // excludes all of the provided ids - whereClauses = append(whereClauses, "scene_markers.primary_tag_id not in "+getInBinding(length)) - whereClauses = append(whereClauses, "not exists (select smt.scene_marker_id from scene_markers_tags as smt where smt.scene_marker_id = scene_markers.id and smt.tag_id in "+getInBinding(length)+")") - } - - for _, tagID := range tagsFilter.Value { - args = append(args, tagID) - } - for _, tagID := range tagsFilter.Value { - args = append(args, tagID) - } - } - - if sceneTagsFilter := sceneMarkerFilter.SceneTags; sceneTagsFilter != nil && len(sceneTagsFilter.Value) > 0 { - length := len(sceneTagsFilter.Value) - - if sceneTagsFilter.Modifier == models.CriterionModifierIncludes || sceneTagsFilter.Modifier == models.CriterionModifierIncludesAll { - body += " LEFT JOIN scenes_tags AS scene_tags_join ON scene_tags_join.scene_id = scene.id AND scene_tags_join.tag_id IN " + getInBinding(length) - - // only one required for include any - requiredCount := 1 - - // all required for include all - if sceneTagsFilter.Modifier == models.CriterionModifierIncludesAll { - requiredCount = length - } - - havingClauses = append(havingClauses, "COUNT(DISTINCT scene_tags_join.tag_id) >= "+strconv.Itoa(requiredCount)) - } else if sceneTagsFilter.Modifier == models.CriterionModifierExcludes { - // excludes all of the provided ids - whereClauses = append(whereClauses, "not exists (select st.scene_id from scenes_tags as st where st.scene_id = scene.id AND st.tag_id IN "+getInBinding(length)+")") - } - - for _, tagID := range sceneTagsFilter.Value { - args = append(args, tagID) - } - } - - if performersFilter := sceneMarkerFilter.Performers; performersFilter != nil && len(performersFilter.Value) > 0 { - length := len(performersFilter.Value) - - if performersFilter.Modifier == models.CriterionModifierIncludes || performersFilter.Modifier == models.CriterionModifierIncludesAll { - body += " LEFT JOIN performers_scenes as scene_performers ON scene.id = scene_performers.scene_id" - whereClauses = append(whereClauses, "scene_performers.performer_id IN "+getInBinding(length)) - - // only one required for include any - requiredCount := 1 - - // all required for include all - if performersFilter.Modifier == models.CriterionModifierIncludesAll { - requiredCount = length - } - - havingClauses = append(havingClauses, "COUNT(DISTINCT scene_performers.performer_id) >= "+strconv.Itoa(requiredCount)) - } else if performersFilter.Modifier == models.CriterionModifierExcludes { - // excludes all of the provided ids - whereClauses = append(whereClauses, "not exists (select sp.scene_id from performers_scenes as sp where sp.scene_id = scene.id AND sp.performer_id IN "+getInBinding(length)+")") - } - - for _, performerID := range performersFilter.Value { - args = append(args, performerID) - } - } + query.body = selectDistinctIDs("scene_markers") if q := findFilter.Q; q != nil && *q != "" { searchColumns := []string{"scene_markers.title", "scene.title"} clause, thisArgs := getSearchBinding(searchColumns, *q, false) - whereClauses = append(whereClauses, clause) - args = append(args, thisArgs...) + query.addWhere(clause) + query.addArg(thisArgs...) } - if tagID := sceneMarkerFilter.TagID; tagID != nil { - whereClauses = append(whereClauses, "(scene_markers.primary_tag_id = "+*tagID+" OR tags.id = "+*tagID+")") - } + filter := qb.makeFilter(sceneMarkerFilter) - sortAndPagination := qb.getSceneMarkerSort(findFilter) + getPagination(findFilter) - idsResult, countResult, err := qb.executeFindQuery(body, args, sortAndPagination, whereClauses, havingClauses, []string{}) + query.addFilter(filter) + + query.sortAndPagination = qb.getSceneMarkerSort(findFilter) + getPagination(findFilter) + idsResult, countResult, err := query.executeFind() if err != nil { return nil, 0, err } @@ -267,6 +178,74 @@ func (qb *sceneMarkerQueryBuilder) Query(sceneMarkerFilter *models.SceneMarkerFi return sceneMarkers, countResult, nil } +func sceneMarkerTagIDCriterionHandler(qb *sceneMarkerQueryBuilder, tagID *string) criterionHandlerFunc { + return func(f *filterBuilder) { + if tagID != nil { + f.addJoin("scene_markers_tags", "", "scene_markers_tags.scene_marker_id = scene_markers.id") + + f.addWhere("(scene_markers.primary_tag_id = ? OR scene_markers_tags.tag_id = ?)", *tagID, *tagID) + } + } +} + +func sceneMarkerTagsCriterionHandler(qb *sceneMarkerQueryBuilder, tags *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { + return func(f *filterBuilder) { + if tags != nil && len(tags.Value) > 0 { + valuesClause := getHierarchicalValues(qb.tx, tags.Value, tagTable, "tags_relations", "", tags.Depth) + + f.addWith(`marker_tags AS ( +SELECT mt.scene_marker_id, t.column1 AS root_tag_id FROM scene_markers_tags mt +INNER JOIN (` + valuesClause + `) t ON t.column2 = mt.tag_id +UNION +SELECT m.id, t.column1 FROM scene_markers m +INNER JOIN (` + valuesClause + `) t ON t.column2 = m.primary_tag_id +)`) + + f.addJoin("marker_tags", "", "marker_tags.scene_marker_id = scene_markers.id") + + addHierarchicalConditionClauses(f, tags, "marker_tags", "root_tag_id") + } + } +} + +func sceneMarkerSceneTagsCriterionHandler(qb *sceneMarkerQueryBuilder, tags *models.HierarchicalMultiCriterionInput) criterionHandlerFunc { + return func(f *filterBuilder) { + if tags != nil && len(tags.Value) > 0 { + valuesClause := getHierarchicalValues(qb.tx, tags.Value, tagTable, "tags_relations", "", tags.Depth) + + f.addWith(`scene_tags AS ( +SELECT st.scene_id, t.column1 AS root_tag_id FROM scenes_tags st +INNER JOIN (` + valuesClause + `) t ON t.column2 = st.tag_id +)`) + + f.addJoin("scene_tags", "", "scene_tags.scene_id = scene_markers.scene_id") + + addHierarchicalConditionClauses(f, tags, "scene_tags", "root_tag_id") + } + } +} + +func sceneMarkerPerformersCriterionHandler(qb *sceneMarkerQueryBuilder, performers *models.MultiCriterionInput) criterionHandlerFunc { + h := joinedMultiCriterionHandlerBuilder{ + primaryTable: sceneTable, + joinTable: performersScenesTable, + joinAs: "performers_join", + primaryFK: sceneIDColumn, + foreignFK: performerIDColumn, + + addJoinTable: func(f *filterBuilder) { + f.addJoin(performersScenesTable, "performers_join", "performers_join.scene_id = scene_markers.scene_id") + }, + } + + handler := h.handler(performers) + return func(f *filterBuilder) { + // Make sure scenes is included, otherwise excludes filter fails + f.addJoin(sceneTable, "", "scenes.id = scene_markers.scene_id") + handler(f) + } +} + func (qb *sceneMarkerQueryBuilder) getSceneMarkerSort(findFilter *models.FindFilterType) string { sort := findFilter.GetSort("title") direction := findFilter.GetDirection() diff --git a/pkg/sqlite/scene_test.go b/pkg/sqlite/scene_test.go index 2be8e4d45..aa895b8b7 100644 --- a/pkg/sqlite/scene_test.go +++ b/pkg/sqlite/scene_test.go @@ -1034,12 +1034,13 @@ func TestSceneQueryPerformers(t *testing.T) { func TestSceneQueryTags(t *testing.T) { withTxn(func(r models.Repository) error { sqb := r.Scene() - tagCriterion := models.MultiCriterionInput{ + tagCriterion := models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdxWithScene]), strconv.Itoa(tagIDs[tagIdx1WithScene]), }, Modifier: models.CriterionModifierIncludes, + Depth: 0, } sceneFilter := models.SceneFilterType{ @@ -1054,12 +1055,13 @@ func TestSceneQueryTags(t *testing.T) { assert.True(t, scene.ID == sceneIDs[sceneIdxWithTag] || scene.ID == sceneIDs[sceneIdxWithTwoTags]) } - tagCriterion = models.MultiCriterionInput{ + tagCriterion = models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdx1WithScene]), strconv.Itoa(tagIDs[tagIdx2WithScene]), }, Modifier: models.CriterionModifierIncludesAll, + Depth: 0, } scenes = queryScene(t, sqb, &sceneFilter, nil) @@ -1067,11 +1069,12 @@ func TestSceneQueryTags(t *testing.T) { assert.Len(t, scenes, 1) assert.Equal(t, sceneIDs[sceneIdxWithTwoTags], scenes[0].ID) - tagCriterion = models.MultiCriterionInput{ + tagCriterion = models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdx1WithScene]), }, Modifier: models.CriterionModifierExcludes, + Depth: 0, } q := getSceneStringValue(sceneIdxWithTwoTags, titleField) @@ -1089,12 +1092,13 @@ func TestSceneQueryTags(t *testing.T) { func TestSceneQueryPerformerTags(t *testing.T) { withTxn(func(r models.Repository) error { sqb := r.Scene() - tagCriterion := models.MultiCriterionInput{ + tagCriterion := models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdxWithPerformer]), strconv.Itoa(tagIDs[tagIdx1WithPerformer]), }, Modifier: models.CriterionModifierIncludes, + Depth: 0, } sceneFilter := models.SceneFilterType{ @@ -1109,12 +1113,13 @@ func TestSceneQueryPerformerTags(t *testing.T) { assert.True(t, scene.ID == sceneIDs[sceneIdxWithPerformerTag] || scene.ID == sceneIDs[sceneIdxWithPerformerTwoTags]) } - tagCriterion = models.MultiCriterionInput{ + tagCriterion = models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdx1WithPerformer]), strconv.Itoa(tagIDs[tagIdx2WithPerformer]), }, Modifier: models.CriterionModifierIncludesAll, + Depth: 0, } scenes = queryScene(t, sqb, &sceneFilter, nil) @@ -1122,11 +1127,12 @@ func TestSceneQueryPerformerTags(t *testing.T) { assert.Len(t, scenes, 1) assert.Equal(t, sceneIDs[sceneIdxWithPerformerTwoTags], scenes[0].ID) - tagCriterion = models.MultiCriterionInput{ + tagCriterion = models.HierarchicalMultiCriterionInput{ Value: []string{ strconv.Itoa(tagIDs[tagIdx1WithPerformer]), }, Modifier: models.CriterionModifierExcludes, + Depth: 0, } q := getSceneStringValue(sceneIdxWithPerformerTwoTags, titleField) diff --git a/pkg/sqlite/tag.go b/pkg/sqlite/tag.go index c67a8dea5..28237867c 100644 --- a/pkg/sqlite/tag.go +++ b/pkg/sqlite/tag.go @@ -196,6 +196,28 @@ func (qb *tagQueryBuilder) FindByNames(names []string, nocase bool) ([]*models.T return qb.queryTags(query, args) } +func (qb *tagQueryBuilder) FindByParentTagID(parentID int) ([]*models.Tag, error) { + query := ` + SELECT tags.* FROM tags + INNER JOIN tags_relations ON tags_relations.child_id = tags.id + WHERE tags_relations.parent_id = ? + ` + query += qb.getDefaultTagSort() + args := []interface{}{parentID} + return qb.queryTags(query, args) +} + +func (qb *tagQueryBuilder) FindByChildTagID(parentID int) ([]*models.Tag, error) { + query := ` + SELECT tags.* FROM tags + INNER JOIN tags_relations ON tags_relations.parent_id = tags.id + WHERE tags_relations.child_id = ? + ` + query += qb.getDefaultTagSort() + args := []interface{}{parentID} + return qb.queryTags(query, args) +} + func (qb *tagQueryBuilder) Count() (int, error) { return qb.runCountQuery(qb.buildCountQuery("SELECT tags.id FROM tags"), nil) } @@ -572,3 +594,115 @@ AND NOT EXISTS(SELECT 1 FROM `+table+` o WHERE o.`+idColumn+` = `+table+`.`+idCo return nil } + +func (qb *tagQueryBuilder) UpdateParentTags(tagID int, parentIDs []int) error { + tx := qb.tx + if _, err := tx.Exec("DELETE FROM tags_relations WHERE child_id = ?", tagID); err != nil { + return err + } + + if len(parentIDs) > 0 { + var args []interface{} + var values []string + for _, parentID := range parentIDs { + values = append(values, "(? , ?)") + args = append(args, parentID, tagID) + } + + query := "INSERT INTO tags_relations (parent_id, child_id) VALUES " + strings.Join(values, ", ") + if _, err := tx.Exec(query, args...); err != nil { + return err + } + } + + return nil +} + +func (qb *tagQueryBuilder) UpdateChildTags(tagID int, childIDs []int) error { + tx := qb.tx + if _, err := tx.Exec("DELETE FROM tags_relations WHERE parent_id = ?", tagID); err != nil { + return err + } + + if len(childIDs) > 0 { + var args []interface{} + var values []string + for _, childID := range childIDs { + values = append(values, "(? , ?)") + args = append(args, tagID, childID) + } + + query := "INSERT INTO tags_relations (parent_id, child_id) VALUES " + strings.Join(values, ", ") + if _, err := tx.Exec(query, args...); err != nil { + return err + } + } + + return nil +} + +func (qb *tagQueryBuilder) FindAllAncestors(tagID int, excludeIDs []int) ([]*models.Tag, error) { + inBinding := getInBinding(len(excludeIDs) + 1) + + query := `WITH RECURSIVE +parents AS ( + SELECT t.id AS parent_id, t.id AS child_id FROM tags t WHERE t.id = ? + UNION + SELECT tr.parent_id, tr.child_id FROM tags_relations tr INNER JOIN parents p ON p.parent_id = tr.child_id WHERE tr.parent_id NOT IN` + inBinding + ` +), +children AS ( + SELECT tr.parent_id, tr.child_id FROM tags_relations tr INNER JOIN parents p ON p.parent_id = tr.parent_id WHERE tr.child_id NOT IN` + inBinding + ` + UNION + SELECT tr.parent_id, tr.child_id FROM tags_relations tr INNER JOIN children c ON c.child_id = tr.parent_id WHERE tr.child_id NOT IN` + inBinding + ` +) +SELECT t.* FROM tags t INNER JOIN parents p ON t.id = p.parent_id +UNION +SELECT t.* FROM tags t INNER JOIN children c ON t.id = c.child_id +` + + var ret models.Tags + excludeArgs := []interface{}{tagID} + for _, excludeID := range excludeIDs { + excludeArgs = append(excludeArgs, excludeID) + } + args := []interface{}{tagID} + args = append(args, append(append(excludeArgs, excludeArgs...), excludeArgs...)...) + if err := qb.query(query, args, &ret); err != nil { + return nil, err + } + + return ret, nil +} + +func (qb *tagQueryBuilder) FindAllDescendants(tagID int, excludeIDs []int) ([]*models.Tag, error) { + inBinding := getInBinding(len(excludeIDs) + 1) + + query := `WITH RECURSIVE +children AS ( + SELECT t.id AS parent_id, t.id AS child_id FROM tags t WHERE t.id = ? + UNION + SELECT tr.parent_id, tr.child_id FROM tags_relations tr INNER JOIN children c ON c.child_id = tr.parent_id WHERE tr.child_id NOT IN` + inBinding + ` +), +parents AS ( + SELECT tr.parent_id, tr.child_id FROM tags_relations tr INNER JOIN children c ON c.child_id = tr.child_id WHERE tr.parent_id NOT IN` + inBinding + ` + UNION + SELECT tr.parent_id, tr.child_id FROM tags_relations tr INNER JOIN parents p ON p.parent_id = tr.child_id WHERE tr.parent_id NOT IN` + inBinding + ` +) +SELECT t.* FROM tags t INNER JOIN children c ON t.id = c.child_id +UNION +SELECT t.* FROM tags t INNER JOIN parents p ON t.id = p.parent_id +` + + var ret models.Tags + excludeArgs := []interface{}{tagID} + for _, excludeID := range excludeIDs { + excludeArgs = append(excludeArgs, excludeID) + } + args := []interface{}{tagID} + args = append(args, append(append(excludeArgs, excludeArgs...), excludeArgs...)...) + if err := qb.query(query, args, &ret); err != nil { + return nil, err + } + + return ret, nil +} diff --git a/pkg/tag/export.go b/pkg/tag/export.go index ba9d6da82..54c64990e 100644 --- a/pkg/tag/export.go +++ b/pkg/tag/export.go @@ -32,6 +32,13 @@ func ToJSON(reader models.TagReader, tag *models.Tag) (*jsonschema.Tag, error) { newTagJSON.Image = utils.GetBase64StringFromData(image) } + parents, err := reader.FindByChildTagID(tag.ID) + if err != nil { + return nil, fmt.Errorf("error getting parents: %s", err.Error()) + } + + newTagJSON.Parents = GetNames(parents) + return &newTagJSON, nil } diff --git a/pkg/tag/export_test.go b/pkg/tag/export_test.go index 16e85292d..36b7a0855 100644 --- a/pkg/tag/export_test.go +++ b/pkg/tag/export_test.go @@ -13,10 +13,12 @@ import ( ) const ( - tagID = 1 - noImageID = 2 - errImageID = 3 - errAliasID = 4 + tagID = 1 + noImageID = 2 + errImageID = 3 + errAliasID = 4 + withParentsID = 5 + errParentsID = 6 ) const tagName = "testTag" @@ -37,7 +39,7 @@ func createTag(id int) models.Tag { } } -func createJSONTag(aliases []string, image string) *jsonschema.Tag { +func createJSONTag(aliases []string, image string, parents []string) *jsonschema.Tag { return &jsonschema.Tag{ Name: tagName, Aliases: aliases, @@ -47,7 +49,8 @@ func createJSONTag(aliases []string, image string) *jsonschema.Tag { UpdatedAt: models.JSONTime{ Time: updateTime, }, - Image: image, + Image: image, + Parents: parents, } } @@ -63,12 +66,12 @@ func initTestTable() { scenarios = []testScenario{ { createTag(tagID), - createJSONTag([]string{"alias"}, "PHN2ZwogICB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iCiAgIHhtbG5zOmNjPSJodHRwOi8vY3JlYXRpdmVjb21tb25zLm9yZy9ucyMiCiAgIHhtbG5zOnJkZj0iaHR0cDovL3d3dy53My5vcmcvMTk5OS8wMi8yMi1yZGYtc3ludGF4LW5zIyIKICAgeG1sbnM6c3ZnPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIKICAgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIgogICB4bWxuczpzb2RpcG9kaT0iaHR0cDovL3NvZGlwb2RpLnNvdXJjZWZvcmdlLm5ldC9EVEQvc29kaXBvZGktMC5kdGQiCiAgIHhtbG5zOmlua3NjYXBlPSJodHRwOi8vd3d3Lmlua3NjYXBlLm9yZy9uYW1lc3BhY2VzL2lua3NjYXBlIgogICB3aWR0aD0iMjAwIgogICBoZWlnaHQ9IjIwMCIKICAgaWQ9InN2ZzIiCiAgIHZlcnNpb249IjEuMSIKICAgaW5rc2NhcGU6dmVyc2lvbj0iMC40OC40IHI5OTM5IgogICBzb2RpcG9kaTpkb2NuYW1lPSJ0YWcuc3ZnIj4KICA8ZGVmcwogICAgIGlkPSJkZWZzNCIgLz4KICA8c29kaXBvZGk6bmFtZWR2aWV3CiAgICAgaWQ9ImJhc2UiCiAgICAgcGFnZWNvbG9yPSIjMDAwMDAwIgogICAgIGJvcmRlcmNvbG9yPSIjNjY2NjY2IgogICAgIGJvcmRlcm9wYWNpdHk9IjEuMCIKICAgICBpbmtzY2FwZTpwYWdlb3BhY2l0eT0iMSIKICAgICBpbmtzY2FwZTpwYWdlc2hhZG93PSIyIgogICAgIGlua3NjYXBlOnpvb209IjEiCiAgICAgaW5rc2NhcGU6Y3g9IjE4MS43Nzc3MSIKICAgICBpbmtzY2FwZTpjeT0iMjc5LjcyMzc2IgogICAgIGlua3NjYXBlOmRvY3VtZW50LXVuaXRzPSJweCIKICAgICBpbmtzY2FwZTpjdXJyZW50LWxheWVyPSJsYXllcjEiCiAgICAgc2hvd2dyaWQ9ImZhbHNlIgogICAgIGZpdC1tYXJnaW4tdG9wPSIwIgogICAgIGZpdC1tYXJnaW4tbGVmdD0iMCIKICAgICBmaXQtbWFyZ2luLXJpZ2h0PSIwIgogICAgIGZpdC1tYXJnaW4tYm90dG9tPSIwIgogICAgIGlua3NjYXBlOndpbmRvdy13aWR0aD0iMTkyMCIKICAgICBpbmtzY2FwZTp3aW5kb3ctaGVpZ2h0PSIxMDE3IgogICAgIGlua3NjYXBlOndpbmRvdy14PSItOCIKICAgICBpbmtzY2FwZTp3aW5kb3cteT0iLTgiCiAgICAgaW5rc2NhcGU6d2luZG93LW1heGltaXplZD0iMSIgLz4KICA8bWV0YWRhdGEKICAgICBpZD0ibWV0YWRhdGE3Ij4KICAgIDxyZGY6UkRGPgogICAgICA8Y2M6V29yawogICAgICAgICByZGY6YWJvdXQ9IiI+CiAgICAgICAgPGRjOmZvcm1hdD5pbWFnZS9zdmcreG1sPC9kYzpmb3JtYXQ+CiAgICAgICAgPGRjOnR5cGUKICAgICAgICAgICByZGY6cmVzb3VyY2U9Imh0dHA6Ly9wdXJsLm9yZy9kYy9kY21pdHlwZS9TdGlsbEltYWdlIiAvPgogICAgICAgIDxkYzp0aXRsZT48L2RjOnRpdGxlPgogICAgICA8L2NjOldvcms+CiAgICA8L3JkZjpSREY+CiAgPC9tZXRhZGF0YT4KICA8ZwogICAgIGlua3NjYXBlOmxhYmVsPSJMYXllciAxIgogICAgIGlua3NjYXBlOmdyb3VwbW9kZT0ibGF5ZXIiCiAgICAgaWQ9ImxheWVyMSIKICAgICB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTU3Ljg0MzU4LC01MjQuNjk1MjIpIj4KICAgIDxwYXRoCiAgICAgICBpZD0icGF0aDI5ODciCiAgICAgICBkPSJtIDIyOS45NDMxNCw2NjkuMjY1NDkgLTM2LjA4NDY2LC0zNi4wODQ2NiBjIC00LjY4NjUzLC00LjY4NjUzIC00LjY4NjUzLC0xMi4yODQ2OCAwLC0xNi45NzEyMSBsIDM2LjA4NDY2LC0zNi4wODQ2NyBhIDEyLjAwMDQ1MywxMi4wMDA0NTMgMCAwIDEgOC40ODU2LC0zLjUxNDggbCA3NC45MTQ0MywwIGMgNi42Mjc2MSwwIDEyLjAwMDQxLDUuMzcyOCAxMi4wMDA0MSwxMi4wMDA0MSBsIDAsNzIuMTY5MzMgYyAwLDYuNjI3NjEgLTUuMzcyOCwxMi4wMDA0MSAtMTIuMDAwNDEsMTIuMDAwNDEgbCAtNzQuOTE0NDMsMCBhIDEyLjAwMDQ1MywxMi4wMDA0NTMgMCAwIDEgLTguNDg1NiwtMy41MTQ4MSB6IG0gLTEzLjQ1NjM5LC01My4wNTU4NyBjIC00LjY4NjUzLDQuNjg2NTMgLTQuNjg2NTMsMTIuMjg0NjggMCwxNi45NzEyMSA0LjY4NjUyLDQuNjg2NTIgMTIuMjg0NjcsNC42ODY1MiAxNi45NzEyLDAgNC42ODY1MywtNC42ODY1MyA0LjY4NjUzLC0xMi4yODQ2OCAwLC0xNi45NzEyMSAtNC42ODY1MywtNC42ODY1MiAtMTIuMjg0NjgsLTQuNjg2NTIgLTE2Ljk3MTIsMCB6IgogICAgICAgaW5rc2NhcGU6Y29ubmVjdG9yLWN1cnZhdHVyZT0iMCIKICAgICAgIHN0eWxlPSJmaWxsOiNmZmZmZmY7ZmlsbC1vcGFjaXR5OjEiIC8+CiAgPC9nPgo8L3N2Zz4="), + createJSONTag([]string{"alias"}, image, nil), false, }, { createTag(noImageID), - createJSONTag(nil, ""), + createJSONTag(nil, "", nil), false, }, { @@ -81,6 +84,16 @@ func initTestTable() { nil, true, }, + { + createTag(withParentsID), + createJSONTag(nil, image, []string{"parent"}), + false, + }, + { + createTag(errParentsID), + nil, + true, + }, } } @@ -91,15 +104,25 @@ func TestToJSON(t *testing.T) { imageErr := errors.New("error getting image") aliasErr := errors.New("error getting aliases") + parentsErr := errors.New("error getting parents") mockTagReader.On("GetAliases", tagID).Return([]string{"alias"}, nil).Once() mockTagReader.On("GetAliases", noImageID).Return(nil, nil).Once() mockTagReader.On("GetAliases", errImageID).Return(nil, nil).Once() mockTagReader.On("GetAliases", errAliasID).Return(nil, aliasErr).Once() + mockTagReader.On("GetAliases", withParentsID).Return(nil, nil).Once() + mockTagReader.On("GetAliases", errParentsID).Return(nil, nil).Once() - mockTagReader.On("GetImage", tagID).Return(models.DefaultTagImage, nil).Once() + mockTagReader.On("GetImage", tagID).Return(imageBytes, nil).Once() mockTagReader.On("GetImage", noImageID).Return(nil, nil).Once() mockTagReader.On("GetImage", errImageID).Return(nil, imageErr).Once() + mockTagReader.On("GetImage", withParentsID).Return(imageBytes, nil).Once() + mockTagReader.On("GetImage", errParentsID).Return(nil, nil).Once() + + mockTagReader.On("FindByChildTagID", tagID).Return(nil, nil).Once() + mockTagReader.On("FindByChildTagID", noImageID).Return(nil, nil).Once() + mockTagReader.On("FindByChildTagID", withParentsID).Return([]*models.Tag{{Name: "parent"}}, nil).Once() + mockTagReader.On("FindByChildTagID", errParentsID).Return(nil, parentsErr).Once() for i, s := range scenarios { tag := s.tag diff --git a/pkg/tag/import.go b/pkg/tag/import.go index 54de4bd0e..8fe0410d2 100644 --- a/pkg/tag/import.go +++ b/pkg/tag/import.go @@ -8,9 +8,22 @@ import ( "github.com/stashapp/stash/pkg/utils" ) +type ParentTagNotExistError struct { + missingParent string +} + +func (e ParentTagNotExistError) Error() string { + return fmt.Sprintf("parent tag <%s> does not exist", e.missingParent) +} + +func (e ParentTagNotExistError) MissingParent() string { + return e.missingParent +} + type Importer struct { - ReaderWriter models.TagReaderWriter - Input jsonschema.Tag + ReaderWriter models.TagReaderWriter + Input jsonschema.Tag + MissingRefBehaviour models.ImportMissingRefEnum tag models.Tag imageData []byte @@ -45,6 +58,15 @@ func (i *Importer) PostImport(id int) error { return fmt.Errorf("error setting tag aliases: %s", err.Error()) } + parents, err := i.getParents() + if err != nil { + return err + } + + if err := i.ReaderWriter.UpdateParentTags(id, parents); err != nil { + return fmt.Errorf("error setting parents: %s", err.Error()) + } + return nil } @@ -87,3 +109,46 @@ func (i *Importer) Update(id int) error { return nil } + +func (i *Importer) getParents() ([]int, error) { + var parents []int + for _, parent := range i.Input.Parents { + tag, err := i.ReaderWriter.FindByName(parent, false) + if err != nil { + return nil, fmt.Errorf("error finding parent by name: %s", err.Error()) + } + + if tag == nil { + if i.MissingRefBehaviour == models.ImportMissingRefEnumFail { + return nil, ParentTagNotExistError{missingParent: parent} + } + + if i.MissingRefBehaviour == models.ImportMissingRefEnumIgnore { + continue + } + + if i.MissingRefBehaviour == models.ImportMissingRefEnumCreate { + parentID, err := i.createParent(parent) + if err != nil { + return nil, err + } + parents = append(parents, parentID) + } + } else { + parents = append(parents, tag.ID) + } + } + + return parents, nil +} + +func (i *Importer) createParent(name string) (int, error) { + newTag := *models.NewTag(name) + + created, err := i.ReaderWriter.Create(newTag) + if err != nil { + return 0, err + } + + return created.ID, nil +} diff --git a/pkg/tag/import_test.go b/pkg/tag/import_test.go index ea29e47c3..c2f29d8e5 100644 --- a/pkg/tag/import_test.go +++ b/pkg/tag/import_test.go @@ -8,6 +8,7 @@ import ( "github.com/stashapp/stash/pkg/models" "github.com/stashapp/stash/pkg/models/mocks" "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" ) const image = "aW1hZ2VCeXRlcw==" @@ -64,13 +65,25 @@ func TestImporterPostImport(t *testing.T) { updateTagImageErr := errors.New("UpdateImage error") updateTagAliasErr := errors.New("UpdateAlias error") + updateTagParentsErr := errors.New("UpdateParentTags error") readerWriter.On("UpdateAliases", tagID, i.Input.Aliases).Return(nil).Once() readerWriter.On("UpdateAliases", errAliasID, i.Input.Aliases).Return(updateTagAliasErr).Once() + readerWriter.On("UpdateAliases", withParentsID, i.Input.Aliases).Return(nil).Once() + readerWriter.On("UpdateAliases", errParentsID, i.Input.Aliases).Return(nil).Once() readerWriter.On("UpdateImage", tagID, imageBytes).Return(nil).Once() readerWriter.On("UpdateImage", errAliasID, imageBytes).Return(nil).Once() readerWriter.On("UpdateImage", errImageID, imageBytes).Return(updateTagImageErr).Once() + readerWriter.On("UpdateImage", withParentsID, imageBytes).Return(nil).Once() + readerWriter.On("UpdateImage", errParentsID, imageBytes).Return(nil).Once() + + var parentTags []int + readerWriter.On("UpdateParentTags", tagID, parentTags).Return(nil).Once() + readerWriter.On("UpdateParentTags", withParentsID, []int{100}).Return(nil).Once() + readerWriter.On("UpdateParentTags", errParentsID, []int{100}).Return(updateTagParentsErr).Once() + + readerWriter.On("FindByName", "Parent", false).Return(&models.Tag{ID: 100}, nil) err := i.PostImport(tagID) assert.Nil(t, err) @@ -81,6 +94,106 @@ func TestImporterPostImport(t *testing.T) { err = i.PostImport(errAliasID) assert.NotNil(t, err) + i.Input.Parents = []string{"Parent"} + err = i.PostImport(withParentsID) + assert.Nil(t, err) + + err = i.PostImport(errParentsID) + assert.NotNil(t, err) + + readerWriter.AssertExpectations(t) +} + +func TestImporterPostImportParentMissing(t *testing.T) { + readerWriter := &mocks.TagReaderWriter{} + + i := Importer{ + ReaderWriter: readerWriter, + Input: jsonschema.Tag{}, + imageData: imageBytes, + } + + createID := 1 + createErrorID := 2 + createFindErrorID := 3 + createFoundID := 4 + failID := 5 + failFindErrorID := 6 + failFoundID := 7 + ignoreID := 8 + ignoreFindErrorID := 9 + ignoreFoundID := 10 + + findError := errors.New("failed finding parent") + + var emptyParents []int + + readerWriter.On("UpdateImage", mock.Anything, mock.Anything).Return(nil) + readerWriter.On("UpdateAliases", mock.Anything, mock.Anything).Return(nil) + + readerWriter.On("FindByName", "Create", false).Return(nil, nil).Once() + readerWriter.On("FindByName", "CreateError", false).Return(nil, nil).Once() + readerWriter.On("FindByName", "CreateFindError", false).Return(nil, findError).Once() + readerWriter.On("FindByName", "CreateFound", false).Return(&models.Tag{ID: 101}, nil).Once() + readerWriter.On("FindByName", "Fail", false).Return(nil, nil).Once() + readerWriter.On("FindByName", "FailFindError", false).Return(nil, findError) + readerWriter.On("FindByName", "FailFound", false).Return(&models.Tag{ID: 102}, nil).Once() + readerWriter.On("FindByName", "Ignore", false).Return(nil, nil).Once() + readerWriter.On("FindByName", "IgnoreFindError", false).Return(nil, findError) + readerWriter.On("FindByName", "IgnoreFound", false).Return(&models.Tag{ID: 103}, nil).Once() + + readerWriter.On("UpdateParentTags", createID, []int{100}).Return(nil).Once() + readerWriter.On("UpdateParentTags", createFoundID, []int{101}).Return(nil).Once() + readerWriter.On("UpdateParentTags", failFoundID, []int{102}).Return(nil).Once() + readerWriter.On("UpdateParentTags", ignoreID, emptyParents).Return(nil).Once() + readerWriter.On("UpdateParentTags", ignoreFoundID, []int{103}).Return(nil).Once() + + readerWriter.On("Create", mock.MatchedBy(func(t models.Tag) bool { return t.Name == "Create" })).Return(&models.Tag{ID: 100}, nil).Once() + readerWriter.On("Create", mock.MatchedBy(func(t models.Tag) bool { return t.Name == "CreateError" })).Return(nil, errors.New("failed creating parent")).Once() + + i.MissingRefBehaviour = models.ImportMissingRefEnumCreate + i.Input.Parents = []string{"Create"} + err := i.PostImport(createID) + assert.Nil(t, err) + + i.Input.Parents = []string{"CreateError"} + err = i.PostImport(createErrorID) + assert.NotNil(t, err) + + i.Input.Parents = []string{"CreateFindError"} + err = i.PostImport(createFindErrorID) + assert.NotNil(t, err) + + i.Input.Parents = []string{"CreateFound"} + err = i.PostImport(createFoundID) + assert.Nil(t, err) + + i.MissingRefBehaviour = models.ImportMissingRefEnumFail + i.Input.Parents = []string{"Fail"} + err = i.PostImport(failID) + assert.NotNil(t, err) + + i.Input.Parents = []string{"FailFindError"} + err = i.PostImport(failFindErrorID) + assert.NotNil(t, err) + + i.Input.Parents = []string{"FailFound"} + err = i.PostImport(failFoundID) + assert.Nil(t, err) + + i.MissingRefBehaviour = models.ImportMissingRefEnumIgnore + i.Input.Parents = []string{"Ignore"} + err = i.PostImport(ignoreID) + assert.Nil(t, err) + + i.Input.Parents = []string{"IgnoreFindError"} + err = i.PostImport(ignoreFindErrorID) + assert.NotNil(t, err) + + i.Input.Parents = []string{"IgnoreFound"} + err = i.PostImport(ignoreFoundID) + assert.Nil(t, err) + readerWriter.AssertExpectations(t) } diff --git a/pkg/tag/update.go b/pkg/tag/update.go index 4f5b9b18b..d4a03f8f5 100644 --- a/pkg/tag/update.go +++ b/pkg/tag/update.go @@ -2,7 +2,6 @@ package tag import ( "fmt" - "github.com/stashapp/stash/pkg/models" ) @@ -23,6 +22,20 @@ func (e *NameUsedByAliasError) Error() string { return fmt.Sprintf("name '%s' is used as alias for '%s'", e.Name, e.OtherTag) } +type InvalidTagHierarchyError struct { + Direction string + InvalidTag string + ApplyingTag string +} + +func (e *InvalidTagHierarchyError) Error() string { + if e.InvalidTag == e.ApplyingTag { + return fmt.Sprintf("Cannot apply tag \"%s\" as it already is a %s", e.InvalidTag, e.Direction) + } else { + return fmt.Sprintf("Cannot apply tag \"%s\" as it is linked to \"%s\" which already is a %s", e.ApplyingTag, e.InvalidTag, e.Direction) + } +} + // EnsureTagNameUnique returns an error if the tag name provided // is used as a name or alias of another existing tag. func EnsureTagNameUnique(id int, name string, qb models.TagReader) error { @@ -63,3 +76,150 @@ func EnsureAliasesUnique(id int, aliases []string, qb models.TagReader) error { return nil } + +func EnsureUniqueHierarchy(id int, parentIDs, childIDs []int, qb models.TagReader) error { + allAncestors := make(map[int]*models.Tag) + allDescendants := make(map[int]*models.Tag) + excludeIDs := []int{id} + + validateParent := func(testID, applyingID int) error { + if parentTag, exists := allAncestors[testID]; exists { + applyingTag, err := qb.Find(applyingID) + + if err != nil { + return nil + } + + return &InvalidTagHierarchyError{ + Direction: "parent", + InvalidTag: parentTag.Name, + ApplyingTag: applyingTag.Name, + } + } + + return nil + } + + validateChild := func(testID, applyingID int) error { + if childTag, exists := allDescendants[testID]; exists { + applyingTag, err := qb.Find(applyingID) + + if err != nil { + return nil + } + + return &InvalidTagHierarchyError{ + Direction: "child", + InvalidTag: childTag.Name, + ApplyingTag: applyingTag.Name, + } + } + + return validateParent(testID, applyingID) + } + + if parentIDs == nil { + parentTags, err := qb.FindByChildTagID(id) + if err != nil { + return err + } + + for _, parentTag := range parentTags { + parentIDs = append(parentIDs, parentTag.ID) + } + } + + if childIDs == nil { + childTags, err := qb.FindByParentTagID(id) + if err != nil { + return err + } + + for _, childTag := range childTags { + childIDs = append(childIDs, childTag.ID) + } + } + + for _, parentID := range parentIDs { + parentsAncestors, err := qb.FindAllAncestors(parentID, excludeIDs) + if err != nil { + return err + } + + for _, ancestorTag := range parentsAncestors { + if err := validateParent(ancestorTag.ID, parentID); err != nil { + return err + } + + allAncestors[ancestorTag.ID] = ancestorTag + } + } + + for _, childID := range childIDs { + childsDescendants, err := qb.FindAllDescendants(childID, excludeIDs) + if err != nil { + return err + } + + for _, descendentTag := range childsDescendants { + if err := validateChild(descendentTag.ID, childID); err != nil { + return err + } + + allDescendants[descendentTag.ID] = descendentTag + } + } + + return nil +} + +func MergeHierarchy(destination int, sources []int, qb models.TagReader) ([]int, []int, error) { + var mergedParents, mergedChildren []int + allIds := append([]int{destination}, sources...) + + addTo := func(mergedItems []int, tags []*models.Tag) []int { + Tags: + for _, tag := range tags { + // Ignore tags which are already set + for _, existingItem := range mergedItems { + if tag.ID == existingItem { + continue Tags + } + } + + // Ignore tags which are being merged, as these are rolled up anyway (if A is merged into B any direct link between them can be ignored) + for _, id := range allIds { + if tag.ID == id { + continue Tags + } + } + + mergedItems = append(mergedItems, tag.ID) + } + + return mergedItems + } + + for _, id := range allIds { + parents, err := qb.FindByChildTagID(id) + if err != nil { + return nil, nil, err + } + + mergedParents = addTo(mergedParents, parents) + + children, err := qb.FindByParentTagID(id) + if err != nil { + return nil, nil, err + } + + mergedChildren = addTo(mergedChildren, children) + } + + err := EnsureUniqueHierarchy(destination, mergedParents, mergedChildren, qb) + if err != nil { + return nil, nil, err + } + + return mergedParents, mergedChildren, nil +} diff --git a/pkg/tag/update_test.go b/pkg/tag/update_test.go new file mode 100644 index 000000000..5a9cddf9d --- /dev/null +++ b/pkg/tag/update_test.go @@ -0,0 +1,302 @@ +package tag + +import ( + "fmt" + "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/models/mocks" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "testing" +) + +var testUniqueHierarchyTags = map[int]*models.Tag{ + 1: { + ID: 1, + Name: "one", + }, + 2: { + ID: 2, + Name: "two", + }, + 3: { + ID: 3, + Name: "three", + }, + 4: { + ID: 4, + Name: "four", + }, +} + +type testUniqueHierarchyCase struct { + id int + parents []*models.Tag + children []*models.Tag + + onFindAllAncestors map[int][]*models.Tag + onFindAllDescendants map[int][]*models.Tag + + expectedError string +} + +var testUniqueHierarchyCases = []testUniqueHierarchyCase{ + { + id: 1, + parents: []*models.Tag{}, + children: []*models.Tag{}, + onFindAllAncestors: map[int][]*models.Tag{ + 1: {}, + }, + onFindAllDescendants: map[int][]*models.Tag{ + 1: {}, + }, + expectedError: "", + }, + { + id: 1, + parents: []*models.Tag{testUniqueHierarchyTags[2]}, + children: []*models.Tag{testUniqueHierarchyTags[3]}, + onFindAllAncestors: map[int][]*models.Tag{ + 2: {testUniqueHierarchyTags[2]}, + }, + onFindAllDescendants: map[int][]*models.Tag{ + 3: {testUniqueHierarchyTags[3]}, + }, + expectedError: "", + }, + { + id: 2, + parents: []*models.Tag{testUniqueHierarchyTags[3]}, + children: make([]*models.Tag, 0), + onFindAllAncestors: map[int][]*models.Tag{ + 3: {testUniqueHierarchyTags[3]}, + }, + onFindAllDescendants: map[int][]*models.Tag{ + 2: {testUniqueHierarchyTags[2]}, + }, + expectedError: "", + }, + { + id: 2, + parents: []*models.Tag{ + testUniqueHierarchyTags[3], + testUniqueHierarchyTags[4], + }, + children: []*models.Tag{}, + onFindAllAncestors: map[int][]*models.Tag{ + 3: {testUniqueHierarchyTags[3], testUniqueHierarchyTags[4]}, + 4: {testUniqueHierarchyTags[4]}, + }, + onFindAllDescendants: map[int][]*models.Tag{ + 2: {testUniqueHierarchyTags[2]}, + }, + expectedError: "Cannot apply tag \"four\" as it already is a parent", + }, + { + id: 2, + parents: []*models.Tag{}, + children: []*models.Tag{testUniqueHierarchyTags[3]}, + onFindAllAncestors: map[int][]*models.Tag{ + 2: {testUniqueHierarchyTags[2]}, + }, + onFindAllDescendants: map[int][]*models.Tag{ + 3: {testUniqueHierarchyTags[3]}, + }, + expectedError: "", + }, + { + id: 2, + parents: []*models.Tag{}, + children: []*models.Tag{ + testUniqueHierarchyTags[3], + testUniqueHierarchyTags[4], + }, + onFindAllAncestors: map[int][]*models.Tag{ + 2: {testUniqueHierarchyTags[2]}, + }, + onFindAllDescendants: map[int][]*models.Tag{ + 3: {testUniqueHierarchyTags[3], testUniqueHierarchyTags[4]}, + 4: {testUniqueHierarchyTags[4]}, + }, + expectedError: "Cannot apply tag \"four\" as it already is a child", + }, + { + id: 1, + parents: []*models.Tag{testUniqueHierarchyTags[2]}, + children: []*models.Tag{testUniqueHierarchyTags[3]}, + onFindAllAncestors: map[int][]*models.Tag{ + 2: {testUniqueHierarchyTags[2], testUniqueHierarchyTags[3]}, + }, + onFindAllDescendants: map[int][]*models.Tag{ + 3: {testUniqueHierarchyTags[3]}, + }, + expectedError: "Cannot apply tag \"three\" as it already is a parent", + }, + { + id: 1, + parents: []*models.Tag{testUniqueHierarchyTags[2]}, + children: []*models.Tag{testUniqueHierarchyTags[3]}, + onFindAllAncestors: map[int][]*models.Tag{ + 2: {testUniqueHierarchyTags[2]}, + }, + onFindAllDescendants: map[int][]*models.Tag{ + 3: {testUniqueHierarchyTags[3], testUniqueHierarchyTags[2]}, + }, + expectedError: "Cannot apply tag \"three\" as it is linked to \"two\" which already is a parent", + }, + { + id: 1, + parents: []*models.Tag{testUniqueHierarchyTags[3]}, + children: []*models.Tag{testUniqueHierarchyTags[3]}, + onFindAllAncestors: map[int][]*models.Tag{ + 3: {testUniqueHierarchyTags[3]}, + }, + onFindAllDescendants: map[int][]*models.Tag{ + 3: {testUniqueHierarchyTags[3]}, + }, + expectedError: "Cannot apply tag \"three\" as it already is a parent", + }, + { + id: 1, + parents: []*models.Tag{ + testUniqueHierarchyTags[2], + }, + children: []*models.Tag{ + testUniqueHierarchyTags[3], + }, + onFindAllAncestors: map[int][]*models.Tag{ + 2: {testUniqueHierarchyTags[2]}, + }, + onFindAllDescendants: map[int][]*models.Tag{ + 3: {testUniqueHierarchyTags[3], testUniqueHierarchyTags[2]}, + }, + expectedError: "Cannot apply tag \"three\" as it is linked to \"two\" which already is a parent", + }, + { + id: 1, + parents: []*models.Tag{testUniqueHierarchyTags[2]}, + children: []*models.Tag{testUniqueHierarchyTags[2]}, + onFindAllAncestors: map[int][]*models.Tag{ + 2: {testUniqueHierarchyTags[2]}, + }, + onFindAllDescendants: map[int][]*models.Tag{ + 2: {testUniqueHierarchyTags[2]}, + }, + expectedError: "Cannot apply tag \"two\" as it already is a parent", + }, + { + id: 2, + parents: []*models.Tag{testUniqueHierarchyTags[1]}, + children: []*models.Tag{testUniqueHierarchyTags[3]}, + onFindAllAncestors: map[int][]*models.Tag{ + 1: {testUniqueHierarchyTags[1]}, + }, + onFindAllDescendants: map[int][]*models.Tag{ + 3: {testUniqueHierarchyTags[3], testUniqueHierarchyTags[1]}, + }, + expectedError: "Cannot apply tag \"three\" as it is linked to \"one\" which already is a parent", + }, +} + +func TestEnsureUniqueHierarchy(t *testing.T) { + for _, tc := range testUniqueHierarchyCases { + testEnsureUniqueHierarchy(t, tc, false, false) + testEnsureUniqueHierarchy(t, tc, true, false) + testEnsureUniqueHierarchy(t, tc, false, true) + testEnsureUniqueHierarchy(t, tc, true, true) + } +} + +func testEnsureUniqueHierarchy(t *testing.T, tc testUniqueHierarchyCase, queryParents, queryChildren bool) { + mockTagReader := &mocks.TagReaderWriter{} + + var parentIDs, childIDs []int + var find map[int]*models.Tag + find = make(map[int]*models.Tag) + if tc.parents != nil { + parentIDs = make([]int, 0) + for _, parent := range tc.parents { + if parent.ID != tc.id { + find[parent.ID] = parent + parentIDs = append(parentIDs, parent.ID) + } + } + } + + if tc.children != nil { + childIDs = make([]int, 0) + for _, child := range tc.children { + if child.ID != tc.id { + find[child.ID] = child + childIDs = append(childIDs, child.ID) + } + } + } + + if queryParents { + parentIDs = nil + mockTagReader.On("FindByChildTagID", tc.id).Return(tc.parents, nil).Once() + } + + if queryChildren { + childIDs = nil + mockTagReader.On("FindByParentTagID", tc.id).Return(tc.children, nil).Once() + } + + mockTagReader.On("Find", mock.AnythingOfType("int")).Return(func(tagID int) *models.Tag { + for id, tag := range find { + if id == tagID { + return tag + } + } + return nil + }, func(tagID int) error { + return nil + }).Maybe() + + mockTagReader.On("FindAllAncestors", mock.AnythingOfType("int"), []int{tc.id}).Return(func(tagID int, excludeIDs []int) []*models.Tag { + for id, tags := range tc.onFindAllAncestors { + if id == tagID { + return tags + } + } + return nil + }, func(tagID int, excludeIDs []int) error { + for id, _ := range tc.onFindAllAncestors { + if id == tagID { + return nil + } + } + return fmt.Errorf("undefined ancestors for: %d", tagID) + }).Maybe() + + mockTagReader.On("FindAllDescendants", mock.AnythingOfType("int"), []int{tc.id}).Return(func(tagID int, excludeIDs []int) []*models.Tag { + for id, tags := range tc.onFindAllDescendants { + if id == tagID { + return tags + } + } + return nil + }, func(tagID int, excludeIDs []int) error { + for id, _ := range tc.onFindAllDescendants { + if id == tagID { + return nil + } + } + return fmt.Errorf("undefined descendants for: %d", tagID) + }).Maybe() + + res := EnsureUniqueHierarchy(tc.id, parentIDs, childIDs, mockTagReader) + + assert := assert.New(t) + + if tc.expectedError != "" { + if assert.NotNil(res) { + assert.Equal(tc.expectedError, res.Error()) + } + } else { + assert.Nil(res) + } + + mockTagReader.AssertExpectations(t) +} diff --git a/ui/v2.5/src/components/Changelog/versions/v0100.md b/ui/v2.5/src/components/Changelog/versions/v0100.md index 5cb28b991..6fec97614 100644 --- a/ui/v2.5/src/components/Changelog/versions/v0100.md +++ b/ui/v2.5/src/components/Changelog/versions/v0100.md @@ -1,4 +1,6 @@ ### ✨ New Features +* Added support for Tag hierarchies. ([#1519](https://github.com/stashapp/stash/pull/1519)) +* Added native support for Apple Silicon / M1 Macs. ([#1646] https://github.com/stashapp/stash/pull/1646) * Added Movies to Scene bulk edit dialog. ([#1676](https://github.com/stashapp/stash/pull/1676)) * Added Movies tab to Studio and Performer pages. ([#1675](https://github.com/stashapp/stash/pull/1675)) * Support filtering Movies by Performers. ([#1675](https://github.com/stashapp/stash/pull/1675)) diff --git a/ui/v2.5/src/components/List/Filters/HierarchicalLabelValueFilter.tsx b/ui/v2.5/src/components/List/Filters/HierarchicalLabelValueFilter.tsx index 151a813c6..9105beefc 100644 --- a/ui/v2.5/src/components/List/Filters/HierarchicalLabelValueFilter.tsx +++ b/ui/v2.5/src/components/List/Filters/HierarchicalLabelValueFilter.tsx @@ -1,6 +1,6 @@ import React from "react"; import { Form } from "react-bootstrap"; -import { defineMessages, useIntl } from "react-intl"; +import { defineMessages, MessageDescriptor, useIntl } from "react-intl"; import { FilterSelect, ValidTypes } from "../../Shared"; import { Criterion } from "../../../models/list-filter/criteria/criterion"; import { IHierarchicalLabelValue } from "../../../models/list-filter/types"; @@ -49,6 +49,16 @@ export const HierarchicalLabelValueFilter: React.FC @@ -63,7 +73,7 @@ export const HierarchicalLabelValueFilter: React.FC onDepthChanged(criterion.value.depth !== 0 ? 0 : -1)} /> diff --git a/ui/v2.5/src/components/Shared/DeleteEntityDialog.tsx b/ui/v2.5/src/components/Shared/DeleteEntityDialog.tsx index 1ab0432f4..2ba92061c 100644 --- a/ui/v2.5/src/components/Shared/DeleteEntityDialog.tsx +++ b/ui/v2.5/src/components/Shared/DeleteEntityDialog.tsx @@ -20,6 +20,7 @@ interface IDeleteEntityDialogProps { singularEntity: string; pluralEntity: string; destroyMutation: DestroyMutation; + onDeleted?: () => void; } const messages = defineMessages({ @@ -43,6 +44,7 @@ const DeleteEntityDialog: React.FC = ({ singularEntity, pluralEntity, destroyMutation, + onDeleted, }) => { const intl = useIntl(); const Toast = useToast(); @@ -56,6 +58,9 @@ const DeleteEntityDialog: React.FC = ({ setIsDeleting(true); try { await deleteEntities(); + if (onDeleted) { + onDeleted(); + } Toast.success({ content: intl.formatMessage(messages.deleteToast, { count, diff --git a/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx b/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx index 52323bc87..c0481ec2c 100644 --- a/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx +++ b/ui/v2.5/src/components/Studios/StudioDetails/Studio.tsx @@ -283,7 +283,7 @@ export const Studio: React.FC = () => { diff --git a/ui/v2.5/src/components/Tags/TagDetails/Tag.tsx b/ui/v2.5/src/components/Tags/TagDetails/Tag.tsx index 094dc8f91..fe4f47673 100644 --- a/ui/v2.5/src/components/Tags/TagDetails/Tag.tsx +++ b/ui/v2.5/src/components/Tags/TagDetails/Tag.tsx @@ -21,6 +21,7 @@ import { Icon, } from "src/components/Shared"; import { useToast } from "src/hooks"; +import { tagRelationHook } from "src/core/tags"; import { TagScenesPanel } from "./TagScenesPanel"; import { TagMarkersPanel } from "./TagMarkersPanel"; import { TagImagesPanel } from "./TagImagesPanel"; @@ -123,6 +124,10 @@ export const Tag: React.FC = () => { input: Partial ) { try { + const oldRelations = { + parents: tag?.parents ?? [], + children: tag?.children ?? [], + }; if (!isNew) { const result = await updateTag({ variables: { @@ -131,7 +136,12 @@ export const Tag: React.FC = () => { }); if (result.data?.tagUpdate) { setIsEditing(false); - return result.data.tagUpdate.id; + const updated = result.data.tagUpdate; + tagRelationHook(updated, oldRelations, { + parents: updated.parents, + children: updated.children, + }); + return updated.id; } } else { const result = await createTag({ @@ -141,7 +151,12 @@ export const Tag: React.FC = () => { }); if (result.data?.tagCreate?.id) { setIsEditing(false); - return result.data.tagCreate.id; + const created = result.data.tagCreate; + tagRelationHook(created, oldRelations, { + parents: created.parents, + children: created.children, + }); + return created.id; } } } catch (e) { @@ -161,7 +176,15 @@ export const Tag: React.FC = () => { async function onDelete() { try { + const oldRelations = { + parents: tag?.parents ?? [], + children: tag?.children ?? [], + }; await deleteTag(); + tagRelationHook(tag as GQL.TagDataFragment, oldRelations, { + parents: [], + children: [], + }); } catch (e) { Toast.error(e); } diff --git a/ui/v2.5/src/components/Tags/TagDetails/TagDetailsPanel.tsx b/ui/v2.5/src/components/Tags/TagDetails/TagDetailsPanel.tsx index 92d007ebb..c02570b85 100644 --- a/ui/v2.5/src/components/Tags/TagDetails/TagDetailsPanel.tsx +++ b/ui/v2.5/src/components/Tags/TagDetails/TagDetailsPanel.tsx @@ -1,6 +1,7 @@ import React from "react"; import { Badge } from "react-bootstrap"; import { FormattedMessage } from "react-intl"; +import { Link } from "react-router-dom"; import * as GQL from "src/core/generated-graphql"; interface ITagDetails { @@ -29,5 +30,53 @@ export const TagDetailsPanel: React.FC = ({ tag }) => { ); } - return <>{renderAliasesField()}; + function renderParentsField() { + if (!tag.parents?.length) { + return; + } + + return ( +
+
+ +
+
+ {tag.parents.map((p) => ( + + {p.name} + + ))} +
+
+ ); + } + + function renderChildrenField() { + if (!tag.children?.length) { + return; + } + + return ( +
+
+ +
+
+ {tag.children.map((c) => ( + + {c.name} + + ))} +
+
+ ); + } + + return ( + <> + {renderAliasesField()} + {renderParentsField()} + {renderChildrenField()} + + ); }; diff --git a/ui/v2.5/src/components/Tags/TagDetails/TagEditPanel.tsx b/ui/v2.5/src/components/Tags/TagDetails/TagEditPanel.tsx index 9eb5321c0..86b53558b 100644 --- a/ui/v2.5/src/components/Tags/TagDetails/TagEditPanel.tsx +++ b/ui/v2.5/src/components/Tags/TagDetails/TagEditPanel.tsx @@ -2,9 +2,9 @@ import React, { useEffect } from "react"; import { FormattedMessage, useIntl } from "react-intl"; import * as GQL from "src/core/generated-graphql"; import * as yup from "yup"; -import { DetailsEditNavbar } from "src/components/Shared"; +import { DetailsEditNavbar, TagSelect } from "src/components/Shared"; import { Form, Col, Row } from "react-bootstrap"; -import { ImageUtils } from "src/utils"; +import { FormUtils, ImageUtils } from "src/utils"; import { useFormik } from "formik"; import { Prompt, useHistory } from "react-router-dom"; import Mousetrap from "mousetrap"; @@ -51,11 +51,15 @@ export const TagEditPanel: React.FC = ({ }, message: "aliases must be unique", }), + parent_ids: yup.array(yup.string().required()).optional().nullable(), + child_ids: yup.array(yup.string().required()).optional().nullable(), }); const initialValues = { name: tag?.name, aliases: tag?.aliases, + parent_ids: (tag?.parents ?? []).map((t) => t.id), + child_ids: (tag?.children ?? []).map((t) => t.id), }; type InputValues = typeof initialValues; @@ -153,6 +157,60 @@ export const TagEditPanel: React.FC = ({ /> + + + {FormUtils.renderLabel({ + title: intl.formatMessage({ id: "parent_tags" }), + labelProps: { + column: true, + sm: 3, + xl: 12, + }, + })} + + + formik.setFieldValue( + "parent_ids", + items.map((item) => item.id) + ) + } + ids={formik.values.parent_ids} + excludeIds={(tag?.id ? [tag.id] : []).concat( + ...formik.values.child_ids + )} + creatable={false} + /> + + + + + {FormUtils.renderLabel({ + title: intl.formatMessage({ id: "sub_tags" }), + labelProps: { + column: true, + sm: 3, + xl: 12, + }, + })} + + + formik.setFieldValue( + "child_ids", + items.map((item) => item.id) + ) + } + ids={formik.values.child_ids} + excludeIds={(tag?.id ? [tag.id] : []).concat( + ...formik.values.parent_ids + )} + creatable={false} + /> + + = ({ tag }) => { ) { // add the tag if not present if ( - !tagCriterion.value.find((p) => { + !tagCriterion.value.items.find((p) => { return p.id === tag.id; }) ) { - tagCriterion.value.push(tagValue); + tagCriterion.value.items.push(tagValue); } tagCriterion.modifier = GQL.CriterionModifier.IncludesAll; } else { // overwrite tagCriterion = new TagsCriterion(TagsCriterionOption); - tagCriterion.value = [tagValue]; + tagCriterion.value = { + items: [tagValue], + depth: 0, + }; filter.criteria.push(tagCriterion); } diff --git a/ui/v2.5/src/components/Tags/TagList.tsx b/ui/v2.5/src/components/Tags/TagList.tsx index d96320e85..88e804a82 100644 --- a/ui/v2.5/src/components/Tags/TagList.tsx +++ b/ui/v2.5/src/components/Tags/TagList.tsx @@ -24,6 +24,7 @@ import { NavUtils } from "src/utils"; import { Icon, Modal, DeleteEntityDialog } from "src/components/Shared"; import { TagCard } from "./TagCard"; import { ExportDialog } from "../Shared/ExportDialog"; +import { tagRelationHook } from "../../core/tags"; interface ITagList { filterHook?: (filter: ListFilterModel) => ListFilterModel; @@ -138,6 +139,15 @@ export const TagList: React.FC = ({ filterHook }) => { singularEntity={intl.formatMessage({ id: "tag" })} pluralEntity={intl.formatMessage({ id: "tags" })} destroyMutation={useTagsDestroy} + onDeleted={() => { + selectedTags.forEach((t) => + tagRelationHook( + t, + { parents: t.parents ?? [], children: t.children ?? [] }, + { parents: [], children: [] } + ) + ); + }} /> ); @@ -175,7 +185,15 @@ export const TagList: React.FC = ({ filterHook }) => { async function onDelete() { try { + const oldRelations = { + parents: deletingTag?.parents ?? [], + children: deletingTag?.children ?? [], + }; await deleteTag(); + tagRelationHook(deletingTag as GQL.TagDataFragment, oldRelations, { + parents: [], + children: [], + }); Toast.success({ content: intl.formatMessage( { id: "toast.delete_past_tense" }, diff --git a/ui/v2.5/src/core/createClient.ts b/ui/v2.5/src/core/createClient.ts index ce976be77..ec4766f6c 100644 --- a/ui/v2.5/src/core/createClient.ts +++ b/ui/v2.5/src/core/createClient.ts @@ -68,6 +68,17 @@ const typePolicies: TypePolicies = { }, }, }, + + Tag: { + fields: { + parents: { + merge: false, + }, + children: { + merge: false, + }, + }, + }, }; export const getPlatformURL = (ws?: boolean) => { diff --git a/ui/v2.5/src/core/tags.ts b/ui/v2.5/src/core/tags.ts index 0c0afed61..d7365b8ac 100644 --- a/ui/v2.5/src/core/tags.ts +++ b/ui/v2.5/src/core/tags.ts @@ -1,4 +1,6 @@ +import { gql } from "@apollo/client"; import * as GQL from "src/core/generated-graphql"; +import { getClient } from "src/core/StashService"; import { TagsCriterion, TagsCriterionOption, @@ -20,21 +22,83 @@ export const tagFilterHook = (tag: GQL.TagDataFragment) => { ) { // add the tag if not present if ( - !tagCriterion.value.find((p) => { + !tagCriterion.value.items.find((p) => { return p.id === tag.id; }) ) { - tagCriterion.value.push(tagValue); + tagCriterion.value.items.push(tagValue); } tagCriterion.modifier = GQL.CriterionModifier.IncludesAll; } else { // overwrite tagCriterion = new TagsCriterion(TagsCriterionOption); - tagCriterion.value = [tagValue]; + tagCriterion.value = { + items: [tagValue], + depth: 0, + }; filter.criteria.push(tagCriterion); } return filter; }; }; + +interface ITagRelationTuple { + parents: GQL.SlimTagDataFragment[]; + children: GQL.SlimTagDataFragment[]; +} + +export const tagRelationHook = ( + tag: GQL.SlimTagDataFragment | GQL.TagDataFragment, + old: ITagRelationTuple, + updated: ITagRelationTuple +) => { + const { cache } = getClient(); + + const tagRef = cache.writeFragment({ + data: tag, + fragment: gql` + fragment Tag on Tag { + id + } + `, + }); + + function updater( + property: "parents" | "children", + oldTags: GQL.SlimTagDataFragment[], + updatedTags: GQL.SlimTagDataFragment[] + ) { + oldTags.forEach((o) => { + if (!updatedTags.some((u) => u.id === o.id)) { + cache.modify({ + id: cache.identify(o), + fields: { + [property](value, { readField }) { + return value.filter( + (t: GQL.SlimTagDataFragment) => readField("id", t) !== tag.id + ); + }, + }, + }); + } + }); + + updatedTags.forEach((u) => { + if (!oldTags.some((o) => o.id === u.id)) { + cache.modify({ + id: cache.identify(u), + fields: { + [property](value) { + return [...value, tagRef]; + }, + }, + }); + } + }); + } + + updater("children", old.parents, updated.parents); + updater("parents", old.children, updated.children); +}; diff --git a/ui/v2.5/src/locales/de-DE.json b/ui/v2.5/src/locales/de-DE.json index 32bd74623..312206a08 100644 --- a/ui/v2.5/src/locales/de-DE.json +++ b/ui/v2.5/src/locales/de-DE.json @@ -91,7 +91,7 @@ "birthdate": "Geburtsdatum", "bitrate": "Bitrate", "career_length": "Länge der Karriere", - "child_studios": "Tochterstudios", + "subsidiary_studios": "Tochterstudios", "component_tagger": { "config": { "active_instance": "Aktive stash-box Instanz:", @@ -501,7 +501,6 @@ "image_count": "Bilderanzahl", "images": "Bilder", "images-size": "Bildgröße", - "include_child_studios": "Tochterstudios einbeziehen", "instagram": "Instagram", "interactive": "Interaktiv", "isMissing": "Wird vermisst", diff --git a/ui/v2.5/src/locales/en-GB.json b/ui/v2.5/src/locales/en-GB.json index 060032cdc..0251ff1be 100644 --- a/ui/v2.5/src/locales/en-GB.json +++ b/ui/v2.5/src/locales/en-GB.json @@ -95,7 +95,8 @@ "birthdate": "Birthdate", "bitrate": "Bit Rate", "career_length": "Career Length", - "child_studios": "Child Studios", + "subsidiary_studios": "Subsidiary Studios", + "sub_tags": "Sub-Tags", "component_tagger": { "config": { "active_instance": "Active stash-box instance:", @@ -529,7 +530,8 @@ "image_count": "Image Count", "images": "Images", "images-size": "Images size", - "include_child_studios": "Include child studios", + "include_sub_studios": "Include subsidiary studios", + "include_sub_tags": "Include sub-tags", "instagram": "Instagram", "interactive": "Interactive", "isMissing": "Is Missing", @@ -571,6 +573,7 @@ "previous": "Previous" }, "parent_studios": "Parent Studios", + "parent_tags": "Parent Tags", "path": "Path", "performer": "Performer", "performer_count": "Performer Count", diff --git a/ui/v2.5/src/locales/pt-BR.json b/ui/v2.5/src/locales/pt-BR.json index 64ce8e204..75d3af9ea 100644 --- a/ui/v2.5/src/locales/pt-BR.json +++ b/ui/v2.5/src/locales/pt-BR.json @@ -91,7 +91,7 @@ "birthdate": "Data de nascimento", "bitrate": "Taxa de bits", "career_length": "Duração da carreira", - "child_studios": "Estúdios filhos", + "subsidiary_studios": "Estúdios filhos", "component_tagger": { "config": { "active_instance": "Ativar stash-box:", @@ -501,7 +501,6 @@ "image_count": "Contagem de imagem", "images": "Imagens", "images-size": "Tamanho das imagens", - "include_child_studios": "Incluem estúdios filho", "instagram": "Instagram", "interactive": "Interativo", "isMissing": "Está faltando", diff --git a/ui/v2.5/src/locales/zh-CN.json b/ui/v2.5/src/locales/zh-CN.json index 3e12dd8d9..4f39bdc1f 100644 --- a/ui/v2.5/src/locales/zh-CN.json +++ b/ui/v2.5/src/locales/zh-CN.json @@ -95,7 +95,7 @@ "birthdate": "出生日期", "bitrate": "比特率", "career_length": "活跃年代", - "child_studios": "子工作室", + "subsidiary_studios": "子工作室", "component_tagger": { "config": { "active_instance": "目前使用的 Stash-box:", @@ -529,7 +529,6 @@ "image_count": "图片数量", "images": "图片", "images-size": "图片大小", - "include_child_studios": "包含子工作室", "instagram": "Instagram", "interactive": "互动", "isMissing": "缺失", diff --git a/ui/v2.5/src/locales/zh-TW.json b/ui/v2.5/src/locales/zh-TW.json index 07f443c52..999ddbaa3 100644 --- a/ui/v2.5/src/locales/zh-TW.json +++ b/ui/v2.5/src/locales/zh-TW.json @@ -95,7 +95,7 @@ "birthdate": "出生日期", "bitrate": "位元率", "career_length": "活躍年代", - "child_studios": "子工作室", + "subsidiary_studios": "子工作室", "component_tagger": { "config": { "active_instance": "目前使用的 Stash-box:", @@ -614,7 +614,6 @@ "filter": "過濾", "filter_name": "過濾條件名稱", "detail": "詳情", - "include_child_studios": "包含子工作室", "criterion": { "greater_than": "大於", "less_than": "小於", diff --git a/ui/v2.5/src/models/list-filter/criteria/tags.ts b/ui/v2.5/src/models/list-filter/criteria/tags.ts index 04545440e..50646a52a 100644 --- a/ui/v2.5/src/models/list-filter/criteria/tags.ts +++ b/ui/v2.5/src/models/list-filter/criteria/tags.ts @@ -1,6 +1,9 @@ -import { ILabeledIdCriterion, ILabeledIdCriterionOption } from "./criterion"; +import { + IHierarchicalLabeledIdCriterion, + ILabeledIdCriterionOption, +} from "./criterion"; -export class TagsCriterion extends ILabeledIdCriterion {} +export class TagsCriterion extends IHierarchicalLabeledIdCriterion {} export const TagsCriterionOption = new ILabeledIdCriterionOption( "tags", diff --git a/ui/v2.5/src/utils/navigation.ts b/ui/v2.5/src/utils/navigation.ts index f4af524cb..929b76654 100644 --- a/ui/v2.5/src/utils/navigation.ts +++ b/ui/v2.5/src/utils/navigation.ts @@ -143,7 +143,10 @@ const makeTagScenesUrl = (tag: Partial) => { if (!tag.id) return "#"; const filter = new ListFilterModel(GQL.FilterMode.Scenes); const criterion = new TagsCriterion(TagsCriterionOption); - criterion.value = [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }]; + criterion.value = { + items: [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }], + depth: 0, + }; filter.criteria.push(criterion); return `/scenes?${filter.makeQueryParameters()}`; }; @@ -152,7 +155,10 @@ const makeTagPerformersUrl = (tag: Partial) => { if (!tag.id) return "#"; const filter = new ListFilterModel(GQL.FilterMode.Performers); const criterion = new TagsCriterion(TagsCriterionOption); - criterion.value = [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }]; + criterion.value = { + items: [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }], + depth: 0, + }; filter.criteria.push(criterion); return `/performers?${filter.makeQueryParameters()}`; }; @@ -161,7 +167,10 @@ const makeTagSceneMarkersUrl = (tag: Partial) => { if (!tag.id) return "#"; const filter = new ListFilterModel(GQL.FilterMode.SceneMarkers); const criterion = new TagsCriterion(TagsCriterionOption); - criterion.value = [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }]; + criterion.value = { + items: [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }], + depth: 0, + }; filter.criteria.push(criterion); return `/scenes/markers?${filter.makeQueryParameters()}`; }; @@ -170,7 +179,10 @@ const makeTagGalleriesUrl = (tag: Partial) => { if (!tag.id) return "#"; const filter = new ListFilterModel(GQL.FilterMode.Galleries); const criterion = new TagsCriterion(TagsCriterionOption); - criterion.value = [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }]; + criterion.value = { + items: [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }], + depth: 0, + }; filter.criteria.push(criterion); return `/galleries?${filter.makeQueryParameters()}`; }; @@ -179,7 +191,10 @@ const makeTagImagesUrl = (tag: Partial) => { if (!tag.id) return "#"; const filter = new ListFilterModel(GQL.FilterMode.Images); const criterion = new TagsCriterion(TagsCriterionOption); - criterion.value = [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }]; + criterion.value = { + items: [{ id: tag.id, label: tag.name || `Tag ${tag.id}` }], + depth: 0, + }; filter.criteria.push(criterion); return `/images?${filter.makeQueryParameters()}`; }; From 04e5ac9c2f54d3af1d0757ba8ae332ce2495f416 Mon Sep 17 00:00:00 2001 From: gitgiggety <79809426+gitgiggety@users.noreply.github.com> Date: Thu, 9 Sep 2021 10:13:42 +0200 Subject: [PATCH 16/86] Studio aliases (#1660) * Add migration to create studio aliases table * Refactor studioQueryBuilder.Query to use filterBuilder * Expand GraphQL API with aliases support for studio * Add aliases support for studios to the UI * List aliases in details panel * Allow editing aliases in edit panel * Add 'aliases' filter when searching * Find studios by alias in filter / select * Add auto-tagging based on studio aliases * Support studio aliases for filename parsing * Support importing and exporting of studio aliases * Search for studio alias as well during scraping --- graphql/documents/data/studio-slim.graphql | 1 + graphql/documents/data/studio.graphql | 1 + graphql/schema/types/filters.graphql | 2 + graphql/schema/types/studio.graphql | 3 + pkg/api/resolver_model_studio.go | 11 + pkg/api/resolver_mutation_studio.go | 45 +++- pkg/autotag/gallery_test.go | 1 + pkg/autotag/image_test.go | 1 + pkg/autotag/integration_test.go | 21 +- pkg/autotag/scene_test.go | 1 + pkg/autotag/studio.go | 82 +++++-- pkg/autotag/studio_test.go | 205 ++++++++++++------ pkg/database/database.go | 2 +- .../migrations/27_studio_aliases.up.sql | 7 + pkg/manager/filename_parser.go | 8 +- pkg/manager/jsonschema/studio.go | 1 + pkg/manager/task_autotag.go | 11 +- pkg/models/mocks/StudioReaderWriter.go | 37 ++++ pkg/models/studio.go | 2 + pkg/scraper/matchers.go | 15 +- pkg/sqlite/setup_test.go | 6 + pkg/sqlite/studio.go | 179 +++++++++++---- pkg/sqlite/studio_test.go | 117 +++++++++- pkg/studio/export.go | 7 + pkg/studio/export_test.go | 22 +- pkg/studio/import.go | 4 + pkg/studio/import_test.go | 16 +- pkg/studio/query.go | 51 +++++ pkg/studio/update.go | 65 ++++++ .../components/Changelog/versions/v0100.md | 1 + ui/v2.5/src/components/Shared/Select.tsx | 94 +++++++- .../StudioDetails/StudioDetailsPanel.tsx | 25 ++- .../Studios/StudioDetails/StudioEditPanel.tsx | 28 ++- ui/v2.5/src/models/list-filter/studios.ts | 1 + 34 files changed, 909 insertions(+), 164 deletions(-) create mode 100644 pkg/database/migrations/27_studio_aliases.up.sql create mode 100644 pkg/studio/query.go create mode 100644 pkg/studio/update.go diff --git a/graphql/documents/data/studio-slim.graphql b/graphql/documents/data/studio-slim.graphql index f840ad2fb..36b0fd287 100644 --- a/graphql/documents/data/studio-slim.graphql +++ b/graphql/documents/data/studio-slim.graphql @@ -11,4 +11,5 @@ fragment SlimStudioData on Studio { } details rating + aliases } diff --git a/graphql/documents/data/studio.graphql b/graphql/documents/data/studio.graphql index 68ec86f82..38de6dfce 100644 --- a/graphql/documents/data/studio.graphql +++ b/graphql/documents/data/studio.graphql @@ -24,4 +24,5 @@ fragment StudioData on Studio { } details rating + aliases } diff --git a/graphql/schema/types/filters.graphql b/graphql/schema/types/filters.graphql index 04a508f06..4ea425f93 100644 --- a/graphql/schema/types/filters.graphql +++ b/graphql/schema/types/filters.graphql @@ -199,6 +199,8 @@ input StudioFilterType { gallery_count: IntCriterionInput """Filter by url""" url: StringCriterionInput + """Filter by studio aliases""" + aliases: StringCriterionInput } input GalleryFilterType { diff --git a/graphql/schema/types/studio.graphql b/graphql/schema/types/studio.graphql index 364b7ad42..ac62f0671 100644 --- a/graphql/schema/types/studio.graphql +++ b/graphql/schema/types/studio.graphql @@ -5,6 +5,7 @@ type Studio { url: String parent_studio: Studio child_studios: [Studio!]! + aliases: [String!]! image_path: String # Resolver scene_count: Int # Resolver @@ -26,6 +27,7 @@ input StudioCreateInput { stash_ids: [StashIDInput!] rating: Int details: String + aliases: [String!] } input StudioUpdateInput { @@ -38,6 +40,7 @@ input StudioUpdateInput { stash_ids: [StashIDInput!] rating: Int details: String + aliases: [String!] } input StudioDestroyInput { diff --git a/pkg/api/resolver_model_studio.go b/pkg/api/resolver_model_studio.go index 5123fe8b1..c44e610c4 100644 --- a/pkg/api/resolver_model_studio.go +++ b/pkg/api/resolver_model_studio.go @@ -45,6 +45,17 @@ func (r *studioResolver) ImagePath(ctx context.Context, obj *models.Studio) (*st return &imagePath, nil } +func (r *studioResolver) Aliases(ctx context.Context, obj *models.Studio) (ret []string, err error) { + if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error { + ret, err = repo.Studio().GetAliases(obj.ID) + return err + }); err != nil { + return nil, err + } + + return ret, err +} + func (r *studioResolver) SceneCount(ctx context.Context, obj *models.Studio) (ret *int, err error) { var res int if err := r.withReadTxn(ctx, func(repo models.ReaderRepository) error { diff --git a/pkg/api/resolver_mutation_studio.go b/pkg/api/resolver_mutation_studio.go index 108d952bc..bdca6059f 100644 --- a/pkg/api/resolver_mutation_studio.go +++ b/pkg/api/resolver_mutation_studio.go @@ -3,6 +3,7 @@ package api import ( "context" "database/sql" + "github.com/stashapp/stash/pkg/studio" "strconv" "time" @@ -64,19 +65,19 @@ func (r *mutationResolver) StudioCreate(ctx context.Context, input models.Studio } // Start the transaction and save the studio - var studio *models.Studio + var s *models.Studio if err := r.withTxn(ctx, func(repo models.Repository) error { qb := repo.Studio() var err error - studio, err = qb.Create(newStudio) + s, err = qb.Create(newStudio) if err != nil { return err } // update image table if len(imageData) > 0 { - if err := qb.UpdateImage(studio.ID, imageData); err != nil { + if err := qb.UpdateImage(s.ID, imageData); err != nil { return err } } @@ -84,7 +85,17 @@ func (r *mutationResolver) StudioCreate(ctx context.Context, input models.Studio // Save the stash_ids if input.StashIds != nil { stashIDJoins := models.StashIDsFromInput(input.StashIds) - if err := qb.UpdateStashIDs(studio.ID, stashIDJoins); err != nil { + if err := qb.UpdateStashIDs(s.ID, stashIDJoins); err != nil { + return err + } + } + + if len(input.Aliases) > 0 { + if err := studio.EnsureAliasesUnique(s.ID, input.Aliases, qb); err != nil { + return err + } + + if err := qb.UpdateAliases(s.ID, input.Aliases); err != nil { return err } } @@ -94,8 +105,8 @@ func (r *mutationResolver) StudioCreate(ctx context.Context, input models.Studio return nil, err } - r.hookExecutor.ExecutePostHooks(ctx, studio.ID, plugin.StudioCreatePost, input, nil) - return r.getStudio(ctx, studio.ID) + r.hookExecutor.ExecutePostHooks(ctx, s.ID, plugin.StudioCreatePost, input, nil) + return r.getStudio(ctx, s.ID) } func (r *mutationResolver) StudioUpdate(ctx context.Context, input models.StudioUpdateInput) (*models.Studio, error) { @@ -136,7 +147,7 @@ func (r *mutationResolver) StudioUpdate(ctx context.Context, input models.Studio updatedStudio.Rating = translator.nullInt64(input.Rating, "rating") // Start the transaction and save the studio - var studio *models.Studio + var s *models.Studio if err := r.withTxn(ctx, func(repo models.Repository) error { qb := repo.Studio() @@ -145,19 +156,19 @@ func (r *mutationResolver) StudioUpdate(ctx context.Context, input models.Studio } var err error - studio, err = qb.Update(updatedStudio) + s, err = qb.Update(updatedStudio) if err != nil { return err } // update image table if len(imageData) > 0 { - if err := qb.UpdateImage(studio.ID, imageData); err != nil { + if err := qb.UpdateImage(s.ID, imageData); err != nil { return err } } else if imageIncluded { // must be unsetting - if err := qb.DestroyImage(studio.ID); err != nil { + if err := qb.DestroyImage(s.ID); err != nil { return err } } @@ -170,13 +181,23 @@ func (r *mutationResolver) StudioUpdate(ctx context.Context, input models.Studio } } + if translator.hasField("aliases") { + if err := studio.EnsureAliasesUnique(studioID, input.Aliases, qb); err != nil { + return err + } + + if err := qb.UpdateAliases(studioID, input.Aliases); err != nil { + return err + } + } + return nil }); err != nil { return nil, err } - r.hookExecutor.ExecutePostHooks(ctx, studio.ID, plugin.StudioUpdatePost, input, translator.getFields()) - return r.getStudio(ctx, studio.ID) + r.hookExecutor.ExecutePostHooks(ctx, s.ID, plugin.StudioUpdatePost, input, translator.getFields()) + return r.getStudio(ctx, s.ID) } func (r *mutationResolver) StudioDestroy(ctx context.Context, input models.StudioDestroyInput) (bool, error) { diff --git a/pkg/autotag/gallery_test.go b/pkg/autotag/gallery_test.go index ff47f20c1..53513fb9d 100644 --- a/pkg/autotag/gallery_test.go +++ b/pkg/autotag/gallery_test.go @@ -79,6 +79,7 @@ func TestGalleryStudios(t *testing.T) { mockGalleryReader := &mocks.GalleryReaderWriter{} mockStudioReader.On("QueryForAutoTag", mock.Anything).Return([]*models.Studio{&studio, &reversedStudio}, nil).Once() + mockStudioReader.On("GetAliases", mock.Anything).Return([]string{}, nil).Maybe() if test.Matches { mockGalleryReader.On("Find", galleryID).Return(&models.Gallery{}, nil).Once() diff --git a/pkg/autotag/image_test.go b/pkg/autotag/image_test.go index 8dba6b6e2..69e067b9a 100644 --- a/pkg/autotag/image_test.go +++ b/pkg/autotag/image_test.go @@ -79,6 +79,7 @@ func TestImageStudios(t *testing.T) { mockImageReader := &mocks.ImageReaderWriter{} mockStudioReader.On("QueryForAutoTag", mock.Anything).Return([]*models.Studio{&studio, &reversedStudio}, nil).Once() + mockStudioReader.On("GetAliases", mock.Anything).Return([]string{}, nil).Maybe() if test.Matches { mockImageReader.On("Find", imageID).Return(&models.Image{}, nil).Once() diff --git a/pkg/autotag/integration_test.go b/pkg/autotag/integration_test.go index 264e82ea3..003e4ee86 100644 --- a/pkg/autotag/integration_test.go +++ b/pkg/autotag/integration_test.go @@ -409,7 +409,12 @@ func TestParseStudioScenes(t *testing.T) { for _, s := range studios { if err := withTxn(func(r models.Repository) error { - return StudioScenes(s, nil, r.Scene()) + aliases, err := r.Studio().GetAliases(s.ID) + if err != nil { + return err + } + + return StudioScenes(s, nil, aliases, r.Scene()) }); err != nil { t.Errorf("Error auto-tagging performers: %s", err) } @@ -559,7 +564,12 @@ func TestParseStudioImages(t *testing.T) { for _, s := range studios { if err := withTxn(func(r models.Repository) error { - return StudioImages(s, nil, r.Image()) + aliases, err := r.Studio().GetAliases(s.ID) + if err != nil { + return err + } + + return StudioImages(s, nil, aliases, r.Image()) }); err != nil { t.Errorf("Error auto-tagging performers: %s", err) } @@ -709,7 +719,12 @@ func TestParseStudioGalleries(t *testing.T) { for _, s := range studios { if err := withTxn(func(r models.Repository) error { - return StudioGalleries(s, nil, r.Gallery()) + aliases, err := r.Studio().GetAliases(s.ID) + if err != nil { + return err + } + + return StudioGalleries(s, nil, aliases, r.Gallery()) }); err != nil { t.Errorf("Error auto-tagging performers: %s", err) } diff --git a/pkg/autotag/scene_test.go b/pkg/autotag/scene_test.go index d2326522c..6a76a2bbe 100644 --- a/pkg/autotag/scene_test.go +++ b/pkg/autotag/scene_test.go @@ -212,6 +212,7 @@ func TestSceneStudios(t *testing.T) { mockSceneReader := &mocks.SceneReaderWriter{} mockStudioReader.On("QueryForAutoTag", mock.Anything).Return([]*models.Studio{&studio, &reversedStudio}, nil).Once() + mockStudioReader.On("GetAliases", mock.Anything).Return([]string{}, nil).Maybe() if test.Matches { mockSceneReader.On("Find", sceneID).Return(&models.Scene{}, nil).Once() diff --git a/pkg/autotag/studio.go b/pkg/autotag/studio.go index ba6309c5a..1634a0fed 100644 --- a/pkg/autotag/studio.go +++ b/pkg/autotag/studio.go @@ -2,7 +2,6 @@ package autotag import ( "database/sql" - "github.com/stashapp/stash/pkg/models" ) @@ -16,7 +15,26 @@ func getMatchingStudios(path string, reader models.StudioReader) ([]*models.Stud var ret []*models.Studio for _, c := range candidates { + matches := false if nameMatchesPath(c.Name.String, path) { + matches = true + } + + if !matches { + aliases, err := reader.GetAliases(c.ID) + if err != nil { + return nil, err + } + + for _, alias := range aliases { + if nameMatchesPath(alias, path) { + matches = true + break + } + } + } + + if matches { ret = append(ret, c) } } @@ -96,37 +114,65 @@ func addGalleryStudio(galleryWriter models.GalleryReaderWriter, galleryID, studi return true, nil } -func getStudioTagger(p *models.Studio) tagger { - return tagger{ +func getStudioTagger(p *models.Studio, aliases []string) []tagger { + ret := []tagger{{ ID: p.ID, Type: "studio", Name: p.Name.String, + }} + + for _, a := range aliases { + ret = append(ret, tagger{ + ID: p.ID, + Type: "studio", + Name: a, + }) } + + return ret } // StudioScenes searches for scenes whose path matches the provided studio name and tags the scene with the studio, if studio is not already set on the scene. -func StudioScenes(p *models.Studio, paths []string, rw models.SceneReaderWriter) error { - t := getStudioTagger(p) +func StudioScenes(p *models.Studio, paths []string, aliases []string, rw models.SceneReaderWriter) error { + t := getStudioTagger(p, aliases) - return t.tagScenes(paths, rw, func(subjectID, otherID int) (bool, error) { - return addSceneStudio(rw, otherID, subjectID) - }) + for _, tt := range t { + if err := tt.tagScenes(paths, rw, func(subjectID, otherID int) (bool, error) { + return addSceneStudio(rw, otherID, subjectID) + }); err != nil { + return err + } + } + + return nil } // StudioImages searches for images whose path matches the provided studio name and tags the image with the studio, if studio is not already set on the image. -func StudioImages(p *models.Studio, paths []string, rw models.ImageReaderWriter) error { - t := getStudioTagger(p) +func StudioImages(p *models.Studio, paths []string, aliases []string, rw models.ImageReaderWriter) error { + t := getStudioTagger(p, aliases) - return t.tagImages(paths, rw, func(subjectID, otherID int) (bool, error) { - return addImageStudio(rw, otherID, subjectID) - }) + for _, tt := range t { + if err := tt.tagImages(paths, rw, func(subjectID, otherID int) (bool, error) { + return addImageStudio(rw, otherID, subjectID) + }); err != nil { + return err + } + } + + return nil } // StudioGalleries searches for galleries whose path matches the provided studio name and tags the gallery with the studio, if studio is not already set on the gallery. -func StudioGalleries(p *models.Studio, paths []string, rw models.GalleryReaderWriter) error { - t := getStudioTagger(p) +func StudioGalleries(p *models.Studio, paths []string, aliases []string, rw models.GalleryReaderWriter) error { + t := getStudioTagger(p, aliases) - return t.tagGalleries(paths, rw, func(subjectID, otherID int) (bool, error) { - return addGalleryStudio(rw, otherID, subjectID) - }) + for _, tt := range t { + if err := tt.tagGalleries(paths, rw, func(subjectID, otherID int) (bool, error) { + return addGalleryStudio(rw, otherID, subjectID) + }); err != nil { + return err + } + } + + return nil } diff --git a/pkg/autotag/studio_test.go b/pkg/autotag/studio_test.go index 886ea1361..2d97bc3fa 100644 --- a/pkg/autotag/studio_test.go +++ b/pkg/autotag/studio_test.go @@ -8,35 +8,67 @@ import ( "github.com/stretchr/testify/assert" ) +type testStudioCase struct { + studioName string + expectedRegex string + aliasName string + aliasRegex string +} + +var testStudioCases = []testStudioCase{ + { + "studio name", + `(?i)(?:^|_|[^\w\d])studio[.\-_ ]*name(?:$|_|[^\w\d])`, + "", + "", + }, + { + "studio + name", + `(?i)(?:^|_|[^\w\d])studio[.\-_ ]*\+[.\-_ ]*name(?:$|_|[^\w\d])`, + "", + "", + }, + { + "studio name", + `(?i)(?:^|_|[^\w\d])studio[.\-_ ]*name(?:$|_|[^\w\d])`, + "alias name", + `(?i)(?:^|_|[^\w\d])alias[.\-_ ]*name(?:$|_|[^\w\d])`, + }, + { + "studio + name", + `(?i)(?:^|_|[^\w\d])studio[.\-_ ]*\+[.\-_ ]*name(?:$|_|[^\w\d])`, + "alias + name", + `(?i)(?:^|_|[^\w\d])alias[.\-_ ]*\+[.\-_ ]*name(?:$|_|[^\w\d])`, + }, +} + func TestStudioScenes(t *testing.T) { - type test struct { - studioName string - expectedRegex string - } - - studioNames := []test{ - { - "studio name", - `(?i)(?:^|_|[^\w\d])studio[.\-_ ]*name(?:$|_|[^\w\d])`, - }, - { - "studio + name", - `(?i)(?:^|_|[^\w\d])studio[.\-_ ]*\+[.\-_ ]*name(?:$|_|[^\w\d])`, - }, - } - - for _, p := range studioNames { - testStudioScenes(t, p.studioName, p.expectedRegex) + for _, p := range testStudioCases { + testStudioScenes(t, p) } } -func testStudioScenes(t *testing.T, studioName, expectedRegex string) { +func testStudioScenes(t *testing.T, tc testStudioCase) { + studioName := tc.studioName + expectedRegex := tc.expectedRegex + aliasName := tc.aliasName + aliasRegex := tc.aliasRegex + mockSceneReader := &mocks.SceneReaderWriter{} const studioID = 2 + var aliases []string + + testPathName := studioName + if aliasName != "" { + aliases = []string{aliasName} + testPathName = aliasName + } + + matchingPaths, falsePaths := generateTestPaths(testPathName, "mp4") + var scenes []*models.Scene - matchingPaths, falsePaths := generateTestPaths(studioName, sceneExt) for i, p := range append(matchingPaths, falsePaths...) { scenes = append(scenes, &models.Scene{ ID: i + 1, @@ -64,7 +96,23 @@ func testStudioScenes(t *testing.T, studioName, expectedRegex string) { PerPage: &perPage, } - mockSceneReader.On("Query", expectedSceneFilter, expectedFindFilter).Return(scenes, len(scenes), nil).Once() + // if alias provided, then don't find by name + onNameQuery := mockSceneReader.On("Query", expectedSceneFilter, expectedFindFilter) + if aliasName == "" { + onNameQuery.Return(scenes, len(scenes), nil).Once() + } else { + onNameQuery.Return(nil, 0, nil).Once() + + expectedAliasFilter := &models.SceneFilterType{ + Organized: &organized, + Path: &models.StringCriterionInput{ + Value: aliasRegex, + Modifier: models.CriterionModifierMatchesRegex, + }, + } + + mockSceneReader.On("Query", expectedAliasFilter, expectedFindFilter).Return(scenes, len(scenes), nil).Once() + } for i := range matchingPaths { sceneID := i + 1 @@ -76,7 +124,7 @@ func testStudioScenes(t *testing.T, studioName, expectedRegex string) { }).Return(nil, nil).Once() } - err := StudioScenes(&studio, nil, mockSceneReader) + err := StudioScenes(&studio, nil, aliases, mockSceneReader) assert := assert.New(t) @@ -85,34 +133,31 @@ func testStudioScenes(t *testing.T, studioName, expectedRegex string) { } func TestStudioImages(t *testing.T) { - type test struct { - studioName string - expectedRegex string - } - - studioNames := []test{ - { - "studio name", - `(?i)(?:^|_|[^\w\d])studio[.\-_ ]*name(?:$|_|[^\w\d])`, - }, - { - "studio + name", - `(?i)(?:^|_|[^\w\d])studio[.\-_ ]*\+[.\-_ ]*name(?:$|_|[^\w\d])`, - }, - } - - for _, p := range studioNames { - testStudioImages(t, p.studioName, p.expectedRegex) + for _, p := range testStudioCases { + testStudioImages(t, p) } } -func testStudioImages(t *testing.T, studioName, expectedRegex string) { +func testStudioImages(t *testing.T, tc testStudioCase) { + studioName := tc.studioName + expectedRegex := tc.expectedRegex + aliasName := tc.aliasName + aliasRegex := tc.aliasRegex + mockImageReader := &mocks.ImageReaderWriter{} const studioID = 2 + var aliases []string + + testPathName := studioName + if aliasName != "" { + aliases = []string{aliasName} + testPathName = aliasName + } + var images []*models.Image - matchingPaths, falsePaths := generateTestPaths(studioName, imageExt) + matchingPaths, falsePaths := generateTestPaths(testPathName, imageExt) for i, p := range append(matchingPaths, falsePaths...) { images = append(images, &models.Image{ ID: i + 1, @@ -140,7 +185,23 @@ func testStudioImages(t *testing.T, studioName, expectedRegex string) { PerPage: &perPage, } - mockImageReader.On("Query", expectedImageFilter, expectedFindFilter).Return(images, len(images), nil).Once() + // if alias provided, then don't find by name + onNameQuery := mockImageReader.On("Query", expectedImageFilter, expectedFindFilter) + if aliasName == "" { + onNameQuery.Return(images, len(images), nil).Once() + } else { + onNameQuery.Return(nil, 0, nil).Once() + + expectedAliasFilter := &models.ImageFilterType{ + Organized: &organized, + Path: &models.StringCriterionInput{ + Value: aliasRegex, + Modifier: models.CriterionModifierMatchesRegex, + }, + } + + mockImageReader.On("Query", expectedAliasFilter, expectedFindFilter).Return(images, len(images), nil).Once() + } for i := range matchingPaths { imageID := i + 1 @@ -152,7 +213,7 @@ func testStudioImages(t *testing.T, studioName, expectedRegex string) { }).Return(nil, nil).Once() } - err := StudioImages(&studio, nil, mockImageReader) + err := StudioImages(&studio, nil, aliases, mockImageReader) assert := assert.New(t) @@ -161,34 +222,30 @@ func testStudioImages(t *testing.T, studioName, expectedRegex string) { } func TestStudioGalleries(t *testing.T) { - type test struct { - studioName string - expectedRegex string - } - - studioNames := []test{ - { - "studio name", - `(?i)(?:^|_|[^\w\d])studio[.\-_ ]*name(?:$|_|[^\w\d])`, - }, - { - "studio + name", - `(?i)(?:^|_|[^\w\d])studio[.\-_ ]*\+[.\-_ ]*name(?:$|_|[^\w\d])`, - }, - } - - for _, p := range studioNames { - testStudioGalleries(t, p.studioName, p.expectedRegex) + for _, p := range testStudioCases { + testStudioGalleries(t, p) } } -func testStudioGalleries(t *testing.T, studioName, expectedRegex string) { +func testStudioGalleries(t *testing.T, tc testStudioCase) { + studioName := tc.studioName + expectedRegex := tc.expectedRegex + aliasName := tc.aliasName + aliasRegex := tc.aliasRegex mockGalleryReader := &mocks.GalleryReaderWriter{} const studioID = 2 + var aliases []string + + testPathName := studioName + if aliasName != "" { + aliases = []string{aliasName} + testPathName = aliasName + } + var galleries []*models.Gallery - matchingPaths, falsePaths := generateTestPaths(studioName, galleryExt) + matchingPaths, falsePaths := generateTestPaths(testPathName, galleryExt) for i, p := range append(matchingPaths, falsePaths...) { galleries = append(galleries, &models.Gallery{ ID: i + 1, @@ -216,7 +273,23 @@ func testStudioGalleries(t *testing.T, studioName, expectedRegex string) { PerPage: &perPage, } - mockGalleryReader.On("Query", expectedGalleryFilter, expectedFindFilter).Return(galleries, len(galleries), nil).Once() + // if alias provided, then don't find by name + onNameQuery := mockGalleryReader.On("Query", expectedGalleryFilter, expectedFindFilter) + if aliasName == "" { + onNameQuery.Return(galleries, len(galleries), nil).Once() + } else { + onNameQuery.Return(nil, 0, nil).Once() + + expectedAliasFilter := &models.GalleryFilterType{ + Organized: &organized, + Path: &models.StringCriterionInput{ + Value: aliasRegex, + Modifier: models.CriterionModifierMatchesRegex, + }, + } + + mockGalleryReader.On("Query", expectedAliasFilter, expectedFindFilter).Return(galleries, len(galleries), nil).Once() + } for i := range matchingPaths { galleryID := i + 1 @@ -228,7 +301,7 @@ func testStudioGalleries(t *testing.T, studioName, expectedRegex string) { }).Return(nil, nil).Once() } - err := StudioGalleries(&studio, nil, mockGalleryReader) + err := StudioGalleries(&studio, nil, aliases, mockGalleryReader) assert := assert.New(t) diff --git a/pkg/database/database.go b/pkg/database/database.go index b4a6a8c29..49fe94536 100644 --- a/pkg/database/database.go +++ b/pkg/database/database.go @@ -23,7 +23,7 @@ import ( var DB *sqlx.DB var WriteMu *sync.Mutex var dbPath string -var appSchemaVersion uint = 26 +var appSchemaVersion uint = 27 var databaseSchemaVersion uint var ( diff --git a/pkg/database/migrations/27_studio_aliases.up.sql b/pkg/database/migrations/27_studio_aliases.up.sql new file mode 100644 index 000000000..a7be876b9 --- /dev/null +++ b/pkg/database/migrations/27_studio_aliases.up.sql @@ -0,0 +1,7 @@ +CREATE TABLE `studio_aliases` ( + `studio_id` integer, + `alias` varchar(255) NOT NULL, + foreign key(`studio_id`) references `studios`(`id`) on delete CASCADE +); + +CREATE UNIQUE INDEX `studio_aliases_alias_unique` on `studio_aliases` (`alias`); diff --git a/pkg/manager/filename_parser.go b/pkg/manager/filename_parser.go index 6373c4a2a..9dbfecd81 100644 --- a/pkg/manager/filename_parser.go +++ b/pkg/manager/filename_parser.go @@ -3,6 +3,7 @@ package manager import ( "database/sql" "errors" + "github.com/stashapp/stash/pkg/studio" "path/filepath" "regexp" "strconv" @@ -537,7 +538,12 @@ func (p *SceneFilenameParser) queryStudio(qb models.StudioReader, studioName str return ret } - ret, _ := qb.FindByName(studioName, true) + ret, _ := studio.ByName(qb, studioName) + + // try to match on alias + if ret == nil { + ret, _ = studio.ByAlias(qb, studioName) + } // add result to cache p.studioCache[studioName] = ret diff --git a/pkg/manager/jsonschema/studio.go b/pkg/manager/jsonschema/studio.go index ed1b6d144..ee793acbc 100644 --- a/pkg/manager/jsonschema/studio.go +++ b/pkg/manager/jsonschema/studio.go @@ -17,6 +17,7 @@ type Studio struct { UpdatedAt models.JSONTime `json:"updated_at,omitempty"` Rating int `json:"rating,omitempty"` Details string `json:"details,omitempty"` + Aliases []string `json:"aliases,omitempty"` } func LoadStudioFile(filePath string) (*Studio, error) { diff --git a/pkg/manager/task_autotag.go b/pkg/manager/task_autotag.go index 6f577ffd3..669ffd7a2 100644 --- a/pkg/manager/task_autotag.go +++ b/pkg/manager/task_autotag.go @@ -215,13 +215,18 @@ func (j *autoTagJob) autoTagStudios(ctx context.Context, progress *job.Progress, } if err := j.txnManager.WithTxn(context.TODO(), func(r models.Repository) error { - if err := autotag.StudioScenes(studio, paths, r.Scene()); err != nil { + aliases, err := r.Studio().GetAliases(studio.ID) + if err != nil { return err } - if err := autotag.StudioImages(studio, paths, r.Image()); err != nil { + + if err := autotag.StudioScenes(studio, paths, aliases, r.Scene()); err != nil { return err } - if err := autotag.StudioGalleries(studio, paths, r.Gallery()); err != nil { + if err := autotag.StudioImages(studio, paths, aliases, r.Image()); err != nil { + return err + } + if err := autotag.StudioGalleries(studio, paths, aliases, r.Gallery()); err != nil { return err } diff --git a/pkg/models/mocks/StudioReaderWriter.go b/pkg/models/mocks/StudioReaderWriter.go index fbd8a1936..3c7b61ab0 100644 --- a/pkg/models/mocks/StudioReaderWriter.go +++ b/pkg/models/mocks/StudioReaderWriter.go @@ -199,6 +199,29 @@ func (_m *StudioReaderWriter) FindMany(ids []int) ([]*models.Studio, error) { return r0, r1 } +// GetAliases provides a mock function with given fields: studioID +func (_m *StudioReaderWriter) GetAliases(studioID int) ([]string, error) { + ret := _m.Called(studioID) + + var r0 []string + if rf, ok := ret.Get(0).(func(int) []string); ok { + r0 = rf(studioID) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(int) error); ok { + r1 = rf(studioID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetImage provides a mock function with given fields: studioID func (_m *StudioReaderWriter) GetImage(studioID int) ([]byte, error) { ret := _m.Called(studioID) @@ -342,6 +365,20 @@ func (_m *StudioReaderWriter) Update(updatedStudio models.StudioPartial) (*model return r0, r1 } +// UpdateAliases provides a mock function with given fields: studioID, aliases +func (_m *StudioReaderWriter) UpdateAliases(studioID int, aliases []string) error { + ret := _m.Called(studioID, aliases) + + var r0 error + if rf, ok := ret.Get(0).(func(int, []string) error); ok { + r0 = rf(studioID, aliases) + } else { + r0 = ret.Error(0) + } + + return r0 +} + // UpdateFull provides a mock function with given fields: updatedStudio func (_m *StudioReaderWriter) UpdateFull(updatedStudio models.Studio) (*models.Studio, error) { ret := _m.Called(updatedStudio) diff --git a/pkg/models/studio.go b/pkg/models/studio.go index 7aa2e87b8..6eec0cdf2 100644 --- a/pkg/models/studio.go +++ b/pkg/models/studio.go @@ -14,6 +14,7 @@ type StudioReader interface { GetImage(studioID int) ([]byte, error) HasImage(studioID int) (bool, error) GetStashIDs(studioID int) ([]*StashID, error) + GetAliases(studioID int) ([]string, error) } type StudioWriter interface { @@ -24,6 +25,7 @@ type StudioWriter interface { UpdateImage(studioID int, image []byte) error DestroyImage(studioID int) error UpdateStashIDs(studioID int, stashIDs []StashID) error + UpdateAliases(studioID int, aliases []string) error } type StudioReaderWriter interface { diff --git a/pkg/scraper/matchers.go b/pkg/scraper/matchers.go index fc9bf29e2..f129ec8b8 100644 --- a/pkg/scraper/matchers.go +++ b/pkg/scraper/matchers.go @@ -4,6 +4,7 @@ import ( "strconv" "github.com/stashapp/stash/pkg/models" + "github.com/stashapp/stash/pkg/studio" "github.com/stashapp/stash/pkg/tag" ) @@ -33,18 +34,26 @@ func MatchScrapedPerformer(qb models.PerformerReader, p *models.ScrapedPerformer // MatchScrapedStudio matches the provided studio with the studios // in the database and sets the ID field if one is found. func MatchScrapedStudio(qb models.StudioReader, s *models.ScrapedStudio) error { - studio, err := qb.FindByName(s.Name, true) + st, err := studio.ByName(qb, s.Name) if err != nil { return err } - if studio == nil { + if st == nil { + // try matching by alias + st, err = studio.ByAlias(qb, s.Name) + if err != nil { + return err + } + } + + if st == nil { // ignore - cannot match return nil } - id := strconv.Itoa(studio.ID) + id := strconv.Itoa(st.ID) s.StoredID = &id return nil } diff --git a/pkg/sqlite/setup_test.go b/pkg/sqlite/setup_test.go index f51f93a0e..e8dee00c2 100644 --- a/pkg/sqlite/setup_test.go +++ b/pkg/sqlite/setup_test.go @@ -963,6 +963,12 @@ func createStudios(sqb models.StudioReaderWriter, n int, o int) error { return err } + // add alias + alias := getStudioStringValue(i, "Alias") + if err := sqb.UpdateAliases(created.ID, []string{alias}); err != nil { + return fmt.Errorf("error setting studio alias: %s", err.Error()) + } + studioIDs = append(studioIDs, created.ID) studioNames = append(studioNames, created.Name.String) } diff --git a/pkg/sqlite/studio.go b/pkg/sqlite/studio.go index 34d657148..90fc6cfe4 100644 --- a/pkg/sqlite/studio.go +++ b/pkg/sqlite/studio.go @@ -10,6 +10,8 @@ import ( const studioTable = "studios" const studioIDColumn = "studio_id" +const studioAliasesTable = "studio_aliases" +const studioAliasColumn = "alias" type studioQueryBuilder struct { repository @@ -126,19 +128,50 @@ func (qb *studioQueryBuilder) QueryForAutoTag(words []string) ([]*models.Studio, // TODO - Query needs to be changed to support queries of this type, and // this method should be removed query := selectAll(studioTable) + query += " LEFT JOIN studio_aliases ON studio_aliases.studio_id = studios.id" var whereClauses []string var args []interface{} for _, w := range words { - whereClauses = append(whereClauses, "name like ?") - args = append(args, w+"%") + ww := w + "%" + whereClauses = append(whereClauses, "studios.name like ?") + args = append(args, ww) + + // include aliases + whereClauses = append(whereClauses, "studio_aliases.alias like ?") + args = append(args, ww) } where := strings.Join(whereClauses, " OR ") return qb.queryStudios(query+" WHERE "+where, args) } +func (qb *studioQueryBuilder) makeFilter(studioFilter *models.StudioFilterType) *filterBuilder { + query := &filterBuilder{} + + query.handleCriterion(stringCriterionHandler(studioFilter.Name, studioTable+".name")) + query.handleCriterion(stringCriterionHandler(studioFilter.Details, studioTable+".details")) + query.handleCriterion(stringCriterionHandler(studioFilter.URL, studioTable+".url")) + query.handleCriterion(intCriterionHandler(studioFilter.Rating, studioTable+".rating")) + + query.handleCriterion(criterionHandlerFunc(func(f *filterBuilder) { + if studioFilter.StashID != nil { + qb.stashIDRepository().join(f, "studio_stash_ids", "studios.id") + stringCriterionHandler(studioFilter.StashID, "scene_stash_ids.stash_id")(f) + } + })) + + query.handleCriterion(studioIsMissingCriterionHandler(qb, studioFilter.IsMissing)) + query.handleCriterion(studioSceneCountCriterionHandler(qb, studioFilter.SceneCount)) + query.handleCriterion(studioImageCountCriterionHandler(qb, studioFilter.ImageCount)) + query.handleCriterion(studioGalleryCountCriterionHandler(qb, studioFilter.GalleryCount)) + query.handleCriterion(studioParentCriterionHandler(qb, studioFilter.Parents)) + query.handleCriterion(studioAliasCriterionHandler(qb, studioFilter.Aliases)) + + return query +} + func (qb *studioQueryBuilder) Query(studioFilter *models.StudioFilterType, findFilter *models.FindFilterType) ([]*models.Studio, int, error) { if studioFilter == nil { studioFilter = &models.StudioFilterType{} @@ -150,57 +183,19 @@ func (qb *studioQueryBuilder) Query(studioFilter *models.StudioFilterType, findF query := qb.newQuery() query.body = selectDistinctIDs("studios") - query.body += ` - left join scenes on studios.id = scenes.studio_id - left join studio_stash_ids on studio_stash_ids.studio_id = studios.id - ` if q := findFilter.Q; q != nil && *q != "" { - searchColumns := []string{"studios.name"} + query.join(studioAliasesTable, "", "studio_aliases.studio_id = studios.id") + searchColumns := []string{"studios.name", "studio_aliases.alias"} clause, thisArgs := getSearchBinding(searchColumns, *q, false) query.addWhere(clause) query.addArg(thisArgs...) } - if parentsFilter := studioFilter.Parents; parentsFilter != nil && len(parentsFilter.Value) > 0 { - query.body += ` - left join studios as parent_studio on parent_studio.id = studios.parent_id - ` + filter := qb.makeFilter(studioFilter) - for _, studioID := range parentsFilter.Value { - query.addArg(studioID) - } - - whereClause, havingClause := getMultiCriterionClause("studios", "parent_studio", "", "", "parent_id", parentsFilter) - - query.addWhere(whereClause) - query.addHaving(havingClause) - } - - if rating := studioFilter.Rating; rating != nil { - query.handleIntCriterionInput(studioFilter.Rating, "studios.rating") - } - query.handleCountCriterion(studioFilter.SceneCount, studioTable, sceneTable, studioIDColumn) - query.handleCountCriterion(studioFilter.ImageCount, studioTable, imageTable, studioIDColumn) - query.handleCountCriterion(studioFilter.GalleryCount, studioTable, galleryTable, studioIDColumn) - query.handleStringCriterionInput(studioFilter.Name, "studios.name") - query.handleStringCriterionInput(studioFilter.Details, "studios.details") - query.handleStringCriterionInput(studioFilter.URL, "studios.url") - query.handleStringCriterionInput(studioFilter.StashID, "studio_stash_ids.stash_id") - - if isMissingFilter := studioFilter.IsMissing; isMissingFilter != nil && *isMissingFilter != "" { - switch *isMissingFilter { - case "image": - query.body += `left join studios_image on studios_image.studio_id = studios.id - ` - query.addWhere("studios_image.studio_id IS NULL") - case "stash_id": - query.addWhere("studio_stash_ids.studio_id IS NULL") - default: - query.addWhere("studios." + *isMissingFilter + " IS NULL") - } - } + query.addFilter(filter) query.sortAndPagination = qb.getStudioSort(findFilter) + getPagination(findFilter) idsResult, countResult, err := query.executeFind() @@ -221,6 +216,83 @@ func (qb *studioQueryBuilder) Query(studioFilter *models.StudioFilterType, findF return studios, countResult, nil } +func studioIsMissingCriterionHandler(qb *studioQueryBuilder, isMissing *string) criterionHandlerFunc { + return func(f *filterBuilder) { + if isMissing != nil && *isMissing != "" { + switch *isMissing { + case "image": + f.addJoin("studios_image", "", "studios_image.studio_id = studios.id") + f.addWhere("studios_image.studio_id IS NULL") + case "stash_id": + qb.stashIDRepository().join(f, "studio_stash_ids", "studios.id") + f.addWhere("studio_stash_ids.studio_id IS NULL") + default: + f.addWhere("(studios." + *isMissing + " IS NULL OR TRIM(studios." + *isMissing + ") = '')") + } + } + } +} + +func studioSceneCountCriterionHandler(qb *studioQueryBuilder, sceneCount *models.IntCriterionInput) criterionHandlerFunc { + return func(f *filterBuilder) { + if sceneCount != nil { + f.addJoin("scenes", "", "scenes.studio_id = studios.id") + clause, args := getIntCriterionWhereClause("count(distinct scenes.id)", *sceneCount) + + f.addHaving(clause, args...) + } + } +} + +func studioImageCountCriterionHandler(qb *studioQueryBuilder, imageCount *models.IntCriterionInput) criterionHandlerFunc { + return func(f *filterBuilder) { + if imageCount != nil { + f.addJoin("images", "", "images.studio_id = studios.id") + clause, args := getIntCriterionWhereClause("count(distinct images.id)", *imageCount) + + f.addHaving(clause, args...) + } + } +} + +func studioGalleryCountCriterionHandler(qb *studioQueryBuilder, galleryCount *models.IntCriterionInput) criterionHandlerFunc { + return func(f *filterBuilder) { + if galleryCount != nil { + f.addJoin("galleries", "", "galleries.studio_id = studios.id") + clause, args := getIntCriterionWhereClause("count(distinct galleries.id)", *galleryCount) + + f.addHaving(clause, args...) + } + } +} + +func studioParentCriterionHandler(qb *studioQueryBuilder, parents *models.MultiCriterionInput) criterionHandlerFunc { + addJoinsFunc := func(f *filterBuilder) { + f.addJoin("studios", "parent_studio", "parent_studio.id = studios.parent_id") + } + h := multiCriterionHandlerBuilder{ + primaryTable: studioTable, + foreignTable: "parent_studio", + joinTable: "", + primaryFK: studioIDColumn, + foreignFK: "parent_id", + addJoinsFunc: addJoinsFunc, + } + return h.handler(parents) +} + +func studioAliasCriterionHandler(qb *studioQueryBuilder, alias *models.StringCriterionInput) criterionHandlerFunc { + h := stringListCriterionHandlerBuilder{ + joinTable: studioAliasesTable, + stringColumn: studioAliasColumn, + addJoinTable: func(f *filterBuilder) { + qb.aliasRepository().join(f, "", "studios.id") + }, + } + + return h.handler(alias) +} + func (qb *studioQueryBuilder) getStudioSort(findFilter *models.FindFilterType) string { var sort string var direction string @@ -303,3 +375,22 @@ func (qb *studioQueryBuilder) GetStashIDs(studioID int) ([]*models.StashID, erro func (qb *studioQueryBuilder) UpdateStashIDs(studioID int, stashIDs []models.StashID) error { return qb.stashIDRepository().replace(studioID, stashIDs) } + +func (qb *studioQueryBuilder) aliasRepository() *stringRepository { + return &stringRepository{ + repository: repository{ + tx: qb.tx, + tableName: studioAliasesTable, + idColumn: studioIDColumn, + }, + stringColumn: studioAliasColumn, + } +} + +func (qb *studioQueryBuilder) GetAliases(studioID int) ([]string, error) { + return qb.aliasRepository().get(studioID) +} + +func (qb *studioQueryBuilder) UpdateAliases(studioID int, aliases []string) error { + return qb.aliasRepository().replace(studioID, aliases) +} diff --git a/pkg/sqlite/studio_test.go b/pkg/sqlite/studio_test.go index 929f86ca3..08c0d835c 100644 --- a/pkg/sqlite/studio_test.go +++ b/pkg/sqlite/studio_test.go @@ -62,6 +62,17 @@ func TestStudioQueryForAutoTag(t *testing.T) { assert.Equal(t, strings.ToLower(studioNames[studioIdxWithScene]), strings.ToLower(studios[0].Name.String)) assert.Equal(t, strings.ToLower(studioNames[studioIdxWithScene]), strings.ToLower(studios[1].Name.String)) + // find by alias + name = getStudioStringValue(studioIdxWithScene, "Alias") + studios, err = tqb.QueryForAutoTag([]string{name}) + + if err != nil { + t.Errorf("Error finding studios: %s", err.Error()) + } + + assert.Len(t, studios, 1) + assert.Equal(t, studioIDs[studioIdxWithScene], studios[0].ID) + return nil }) } @@ -460,7 +471,7 @@ func TestStudioQueryURL(t *testing.T) { URL: &urlCriterion, } - verifyFn := func(g *models.Studio) { + verifyFn := func(g *models.Studio, r models.Repository) { t.Helper() verifyNullString(t, g.URL, urlCriterion) } @@ -510,7 +521,7 @@ func TestStudioQueryRating(t *testing.T) { verifyStudiosRating(t, ratingCriterion) } -func verifyStudioQuery(t *testing.T, filter models.StudioFilterType, verifyFn func(s *models.Studio)) { +func verifyStudioQuery(t *testing.T, filter models.StudioFilterType, verifyFn func(s *models.Studio, r models.Repository)) { withTxn(func(r models.Repository) error { t.Helper() sqb := r.Studio() @@ -521,7 +532,7 @@ func verifyStudioQuery(t *testing.T, filter models.StudioFilterType, verifyFn fu assert.Greater(t, len(studios), 0) for _, studio := range studios { - verifyFn(studio) + verifyFn(studio, r) } return nil @@ -582,6 +593,106 @@ func queryStudio(t *testing.T, sqb models.StudioReader, studioFilter *models.Stu return studios } +func TestStudioQueryName(t *testing.T) { + const studioIdx = 1 + studioName := getStudioStringValue(studioIdx, "Name") + + nameCriterion := &models.StringCriterionInput{ + Value: studioName, + Modifier: models.CriterionModifierEquals, + } + + studioFilter := models.StudioFilterType{ + Name: nameCriterion, + } + + verifyFn := func(studio *models.Studio, r models.Repository) { + verifyNullString(t, studio.Name, *nameCriterion) + } + + verifyStudioQuery(t, studioFilter, verifyFn) + + nameCriterion.Modifier = models.CriterionModifierNotEquals + verifyStudioQuery(t, studioFilter, verifyFn) + + nameCriterion.Modifier = models.CriterionModifierMatchesRegex + nameCriterion.Value = "studio_.*1_Name" + verifyStudioQuery(t, studioFilter, verifyFn) + + nameCriterion.Modifier = models.CriterionModifierNotMatchesRegex + verifyStudioQuery(t, studioFilter, verifyFn) +} + +func TestStudioQueryAlias(t *testing.T) { + const studioIdx = 1 + studioName := getStudioStringValue(studioIdx, "Alias") + + aliasCriterion := &models.StringCriterionInput{ + Value: studioName, + Modifier: models.CriterionModifierEquals, + } + + studioFilter := models.StudioFilterType{ + Aliases: aliasCriterion, + } + + verifyFn := func(studio *models.Studio, r models.Repository) { + aliases, err := r.Studio().GetAliases(studio.ID) + if err != nil { + t.Errorf("Error querying studios: %s", err.Error()) + } + + var alias string + if len(aliases) > 0 { + alias = aliases[0] + } + + verifyString(t, alias, *aliasCriterion) + } + + verifyStudioQuery(t, studioFilter, verifyFn) + + aliasCriterion.Modifier = models.CriterionModifierNotEquals + verifyStudioQuery(t, studioFilter, verifyFn) + + aliasCriterion.Modifier = models.CriterionModifierMatchesRegex + aliasCriterion.Value = "studio_.*1_Alias" + verifyStudioQuery(t, studioFilter, verifyFn) + + aliasCriterion.Modifier = models.CriterionModifierNotMatchesRegex + verifyStudioQuery(t, studioFilter, verifyFn) +} + +func TestStudioUpdateAlias(t *testing.T) { + if err := withTxn(func(r models.Repository) error { + qb := r.Studio() + + // create studio to test against + const name = "TestStudioUpdateAlias" + created, err := createStudio(qb, name, nil) + if err != nil { + return fmt.Errorf("Error creating studio: %s", err.Error()) + } + + aliases := []string{"alias1", "alias2"} + err = qb.UpdateAliases(created.ID, aliases) + if err != nil { + return fmt.Errorf("Error updating studio aliases: %s", err.Error()) + } + + // ensure aliases set + storedAliases, err := qb.GetAliases(created.ID) + if err != nil { + return fmt.Errorf("Error getting aliases: %s", err.Error()) + } + assert.Equal(t, aliases, storedAliases) + + return nil + }); err != nil { + t.Error(err.Error()) + } +} + // TODO Create // TODO Update // TODO Destroy diff --git a/pkg/studio/export.go b/pkg/studio/export.go index dc71fd915..46b92a07d 100644 --- a/pkg/studio/export.go +++ b/pkg/studio/export.go @@ -42,6 +42,13 @@ func ToJSON(reader models.StudioReader, studio *models.Studio) (*jsonschema.Stud newStudioJSON.Rating = int(studio.Rating.Int64) } + aliases, err := reader.GetAliases(studio.ID) + if err != nil { + return nil, fmt.Errorf("error getting studio aliases: %s", err.Error()) + } + + newStudioJSON.Aliases = aliases + image, err := reader.GetImage(studio.ID) if err != nil { return nil, fmt.Errorf("error getting studio image: %s", err.Error()) diff --git a/pkg/studio/export_test.go b/pkg/studio/export_test.go index 516c3714e..9361a28c6 100644 --- a/pkg/studio/export_test.go +++ b/pkg/studio/export_test.go @@ -18,6 +18,7 @@ const ( errImageID = 3 missingParentStudioID = 4 errStudioID = 5 + errAliasID = 6 parentStudioID = 10 missingStudioID = 11 @@ -77,7 +78,7 @@ func createEmptyStudio(id int) models.Studio { } } -func createFullJSONStudio(parentStudio, image string) *jsonschema.Studio { +func createFullJSONStudio(parentStudio, image string, aliases []string) *jsonschema.Studio { return &jsonschema.Studio{ Name: studioName, URL: url, @@ -91,6 +92,7 @@ func createFullJSONStudio(parentStudio, image string) *jsonschema.Studio { ParentStudio: parentStudio, Image: image, Rating: rating, + Aliases: aliases, } } @@ -117,7 +119,7 @@ func initTestTable() { scenarios = []testScenario{ testScenario{ createFullStudio(studioID, parentStudioID), - createFullJSONStudio(parentStudioName, image), + createFullJSONStudio(parentStudioName, image, []string{"alias"}), false, }, testScenario{ @@ -132,7 +134,7 @@ func initTestTable() { }, testScenario{ createFullStudio(missingParentStudioID, missingStudioID), - createFullJSONStudio("", image), + createFullJSONStudio("", image, nil), false, }, testScenario{ @@ -140,6 +142,11 @@ func initTestTable() { nil, true, }, + testScenario{ + createFullStudio(errAliasID, parentStudioID), + nil, + true, + }, } } @@ -155,6 +162,7 @@ func TestToJSON(t *testing.T) { mockStudioReader.On("GetImage", errImageID).Return(nil, imageErr).Once() mockStudioReader.On("GetImage", missingParentStudioID).Return(imageBytes, nil).Maybe() mockStudioReader.On("GetImage", errStudioID).Return(imageBytes, nil).Maybe() + mockStudioReader.On("GetImage", errAliasID).Return(imageBytes, nil).Maybe() parentStudioErr := errors.New("error getting parent studio") @@ -162,6 +170,14 @@ func TestToJSON(t *testing.T) { mockStudioReader.On("Find", missingStudioID).Return(nil, nil) mockStudioReader.On("Find", errParentStudioID).Return(nil, parentStudioErr) + aliasErr := errors.New("error getting aliases") + + mockStudioReader.On("GetAliases", studioID).Return([]string{"alias"}, nil).Once() + mockStudioReader.On("GetAliases", noImageID).Return(nil, nil).Once() + mockStudioReader.On("GetAliases", errImageID).Return(nil, nil).Once() + mockStudioReader.On("GetAliases", missingParentStudioID).Return(nil, nil).Once() + mockStudioReader.On("GetAliases", errAliasID).Return(nil, aliasErr).Once() + for i, s := range scenarios { studio := s.input json, err := ToJSON(mockStudioReader, &studio) diff --git a/pkg/studio/import.go b/pkg/studio/import.go index f509c0626..a3a35023d 100644 --- a/pkg/studio/import.go +++ b/pkg/studio/import.go @@ -101,6 +101,10 @@ func (i *Importer) PostImport(id int) error { } } + if err := i.ReaderWriter.UpdateAliases(id, i.Input.Aliases); err != nil { + return fmt.Errorf("error setting tag aliases: %s", err.Error()) + } + return nil } diff --git a/pkg/studio/import_test.go b/pkg/studio/import_test.go index 29a0d8813..78c788fd0 100644 --- a/pkg/studio/import_test.go +++ b/pkg/studio/import_test.go @@ -53,7 +53,7 @@ func TestImporterPreImport(t *testing.T) { assert.Nil(t, err) - i.Input = *createFullJSONStudio(studioName, image) + i.Input = *createFullJSONStudio(studioName, image, []string{"alias"}) i.Input.ParentStudio = "" err = i.PreImport() @@ -151,13 +151,22 @@ func TestImporterPostImport(t *testing.T) { i := Importer{ ReaderWriter: readerWriter, - imageData: imageBytes, + Input: jsonschema.Studio{ + Aliases: []string{"alias"}, + }, + imageData: imageBytes, } updateStudioImageErr := errors.New("UpdateImage error") + updateTagAliasErr := errors.New("UpdateAlias error") readerWriter.On("UpdateImage", studioID, imageBytes).Return(nil).Once() readerWriter.On("UpdateImage", errImageID, imageBytes).Return(updateStudioImageErr).Once() + readerWriter.On("UpdateImage", errAliasID, imageBytes).Return(nil).Once() + + readerWriter.On("UpdateAliases", studioID, i.Input.Aliases).Return(nil).Once() + readerWriter.On("UpdateAliases", errImageID, i.Input.Aliases).Return(nil).Maybe() + readerWriter.On("UpdateAliases", errAliasID, i.Input.Aliases).Return(updateTagAliasErr).Once() err := i.PostImport(studioID) assert.Nil(t, err) @@ -165,6 +174,9 @@ func TestImporterPostImport(t *testing.T) { err = i.PostImport(errImageID) assert.NotNil(t, err) + err = i.PostImport(errAliasID) + assert.NotNil(t, err) + readerWriter.AssertExpectations(t) } diff --git a/pkg/studio/query.go b/pkg/studio/query.go new file mode 100644 index 000000000..5b2f68896 --- /dev/null +++ b/pkg/studio/query.go @@ -0,0 +1,51 @@ +package studio + +import "github.com/stashapp/stash/pkg/models" + +func ByName(qb models.StudioReader, name string) (*models.Studio, error) { + f := &models.StudioFilterType{ + Name: &models.StringCriterionInput{ + Value: name, + Modifier: models.CriterionModifierEquals, + }, + } + + pp := 1 + ret, count, err := qb.Query(f, &models.FindFilterType{ + PerPage: &pp, + }) + + if err != nil { + return nil, err + } + + if count > 0 { + return ret[0], nil + } + + return nil, nil +} + +func ByAlias(qb models.StudioReader, alias string) (*models.Studio, error) { + f := &models.StudioFilterType{ + Aliases: &models.StringCriterionInput{ + Value: alias, + Modifier: models.CriterionModifierEquals, + }, + } + + pp := 1 + ret, count, err := qb.Query(f, &models.FindFilterType{ + PerPage: &pp, + }) + + if err != nil { + return nil, err + } + + if count > 0 { + return ret[0], nil + } + + return nil, nil +} diff --git a/pkg/studio/update.go b/pkg/studio/update.go new file mode 100644 index 000000000..35a655a73 --- /dev/null +++ b/pkg/studio/update.go @@ -0,0 +1,65 @@ +package studio + +import ( + "fmt" + + "github.com/stashapp/stash/pkg/models" +) + +type NameExistsError struct { + Name string +} + +func (e *NameExistsError) Error() string { + return fmt.Sprintf("studio with name '%s' already exists", e.Name) +} + +type NameUsedByAliasError struct { + Name string + OtherStudio string +} + +func (e *NameUsedByAliasError) Error() string { + return fmt.Sprintf("name '%s' is used as alias for '%s'", e.Name, e.OtherStudio) +} + +// EnsureStudioNameUnique returns an error if the studio name provided +// is used as a name or alias of another existing tag. +func EnsureStudioNameUnique(id int, name string, qb models.StudioReader) error { + // ensure name is unique + sameNameStudio, err := ByName(qb, name) + if err != nil { + return err + } + + if sameNameStudio != nil && id != sameNameStudio.ID { + return &NameExistsError{ + Name: name, + } + } + + // query by alias + sameNameStudio, err = ByAlias(qb, name) + if err != nil { + return err + } + + if sameNameStudio != nil && id != sameNameStudio.ID { + return &NameUsedByAliasError{ + Name: name, + OtherStudio: sameNameStudio.Name.String, + } + } + + return nil +} + +func EnsureAliasesUnique(id int, aliases []string, qb models.StudioReader) error { + for _, a := range aliases { + if err := EnsureStudioNameUnique(id, a, qb); err != nil { + return err + } + } + + return nil +} diff --git a/ui/v2.5/src/components/Changelog/versions/v0100.md b/ui/v2.5/src/components/Changelog/versions/v0100.md index 6fec97614..9d86487e8 100644 --- a/ui/v2.5/src/components/Changelog/versions/v0100.md +++ b/ui/v2.5/src/components/Changelog/versions/v0100.md @@ -1,4 +1,5 @@ ### ✨ New Features +* Added support for Studio aliases. ([#1660](https://github.com/stashapp/stash/pull/1660)) * Added support for Tag hierarchies. ([#1519](https://github.com/stashapp/stash/pull/1519)) * Added native support for Apple Silicon / M1 Macs. ([#1646] https://github.com/stashapp/stash/pull/1646) * Added Movies to Scene bulk edit dialog. ([#1676](https://github.com/stashapp/stash/pull/1676)) diff --git a/ui/v2.5/src/components/Shared/Select.tsx b/ui/v2.5/src/components/Shared/Select.tsx index 22f89cf4a..7c8da3e03 100644 --- a/ui/v2.5/src/components/Shared/Select.tsx +++ b/ui/v2.5/src/components/Shared/Select.tsx @@ -427,14 +427,75 @@ export const PerformerSelect: React.FC = (props) => { export const StudioSelect: React.FC< IFilterProps & { excludeIds?: string[] } > = (props) => { + const [studioAliases, setStudioAliases] = useState>( + {} + ); + const [allAliases, setAllAliases] = useState([]); const { data, loading } = useAllStudiosForFilter(); const [createStudio] = useStudioCreate(); - const exclude = props.excludeIds ?? []; - const studios = (data?.allStudios ?? []).filter( - (studio) => !exclude.includes(studio.id) + const exclude = useMemo(() => props.excludeIds ?? [], [props.excludeIds]); + const studios = useMemo( + () => + (data?.allStudios ?? []).filter((studio) => !exclude.includes(studio.id)), + [data?.allStudios, exclude] ); + useEffect(() => { + // build the studio aliases map + const newAliases: Record = {}; + const newAll: string[] = []; + studios.forEach((s) => { + newAliases[s.id] = s.aliases; + newAll.push(...s.aliases); + }); + setStudioAliases(newAliases); + setAllAliases(newAll); + }, [studios]); + + const StudioOption: React.FC> = ( + optionProps + ) => { + const { inputValue } = optionProps.selectProps; + + let thisOptionProps = optionProps; + if ( + inputValue && + !optionProps.label.toLowerCase().includes(inputValue.toLowerCase()) + ) { + // must be alias + const newLabel = `${optionProps.data.label} (alias)`; + thisOptionProps = { + ...optionProps, + children: newLabel, + }; + } + + return ; + }; + + const filterOption = (option: Option, rawInput: string): boolean => { + if (!rawInput) { + return true; + } + + const input = rawInput.toLowerCase(); + const optionVal = option.label.toLowerCase(); + + if (optionVal.includes(input)) { + return true; + } + + // search for studio aliases + const aliases = studioAliases[option.value]; + // only match on alias if exact + if (aliases && aliases.some((a) => a.toLowerCase() === input)) { + return true; + } + + return false; + }; + const onCreate = async (name: string) => { const result = await createStudio({ variables: { @@ -444,9 +505,36 @@ export const StudioSelect: React.FC< return { item: result.data!.studioCreate!, message: "Created studio" }; }; + const isValidNewOption = ( + inputValue: string, + value: ValueType, + options: OptionsType