From f1f5b405c46d08a65f34a9ca4711e466291a7a40 Mon Sep 17 00:00:00 2001 From: Knut Ahlers Date: Mon, 29 Nov 2021 14:55:28 +0100 Subject: [PATCH] Add `regex` fetcher Signed-off-by: Knut Ahlers --- internal/fetcher/regex.go | 89 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 internal/fetcher/regex.go diff --git a/internal/fetcher/regex.go b/internal/fetcher/regex.go new file mode 100644 index 0000000..e19399b --- /dev/null +++ b/internal/fetcher/regex.go @@ -0,0 +1,89 @@ +package fetcher + +import ( + "context" + "io/ioutil" + "net/http" + "regexp" + "time" + + "github.com/pkg/errors" + + "github.com/Luzifer/go-latestver/internal/database" + "github.com/Luzifer/go_helpers/v2/fieldcollection" +) + +const ( + httpStatus3xx = 300 + regexpFetcherExpectedLength = 2 +) + +type ( + RegexFetcher struct{} +) + +func init() { registerFetcher("regex", func() Fetcher { return &RegexFetcher{} }) } + +func (h RegexFetcher) FetchVersion(ctx context.Context, attrs *fieldcollection.FieldCollection) (string, time.Time, error) { + req, err := http.NewRequestWithContext(ctx, http.MethodGet, attrs.MustString("url", nil), nil) + if err != nil { + return "", time.Time{}, errors.Wrap(err, "creating request") + } + + resp, err := http.DefaultClient.Do(req) + if err != nil { + return "", time.Time{}, errors.Wrap(err, "executing request") + } + defer resp.Body.Close() + + if resp.StatusCode >= httpStatus3xx { + return "", time.Time{}, errors.Errorf("HTTP status %d", resp.StatusCode) + } + + data, err := ioutil.ReadAll(resp.Body) + if err != nil { + return "", time.Time{}, errors.Wrap(err, "reading response body") + } + + matches := regexp.MustCompile(attrs.MustString("regex", nil)).FindStringSubmatch(string(data)) + if matches == nil { + return "", time.Time{}, errors.New("regex did not match the response") + } + + if l := len(matches); l != regexpFetcherExpectedLength { + return "", time.Time{}, errors.Errorf("unpexected number of matches: %d != 2", l) + } + + return matches[1], time.Now(), nil +} + +func (h RegexFetcher) Links(attrs *fieldcollection.FieldCollection) []database.CatalogLink { + return []database.CatalogLink{ + { + IconClass: "fas fa-globe", + Name: "Website", + URL: attrs.MustString("url", nil), + }, + } +} + +func (h RegexFetcher) Validate(attrs *fieldcollection.FieldCollection) error { + if v, err := attrs.String("url"); err != nil || v == "" { + return errors.New("url is expected to be non-empty string") + } + + if v, err := attrs.String("regex"); err != nil || v == "" { + return errors.New("regex is expected to be non-empty string") + } + + r, err := regexp.Compile(attrs.MustString("regex", nil)) + if err != nil { + return errors.Wrap(err, "compiling regex expression") + } + + if n := r.NumSubexp(); n != 1 { + return errors.Errorf("regex must have 1 submatch, has %d", n) + } + + return nil +}