refactor: using url.URL between components

This commit is contained in:
Felipe Martin Garcia 2022-08-07 12:01:21 +02:00
parent 3854a1fe11
commit 89543fe637
Signed by: fmartingr
GPG Key ID: 716BC147715E716F
13 changed files with 64 additions and 70 deletions

View File

@ -1,7 +1,10 @@
package clients package clients
import "io" import (
"io"
"net/url"
)
type Client interface { type Client interface {
Get(url string) (io.Reader, error) Get(u *url.URL) (io.Reader, error)
} }

View File

@ -4,14 +4,15 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"net/url"
) )
type HttpClient struct { type HttpClient struct {
http http.Client http http.Client
} }
func (c HttpClient) Get(url string) (io.Reader, error) { func (c HttpClient) Get(u *url.URL) (io.Reader, error) {
res, err := c.http.Get(url) res, err := c.http.Get(u.String())
if err != nil { if err != nil {
return nil, fmt.Errorf("error retrieving url: %s", err) return nil, fmt.Errorf("error retrieving url: %s", err)
} }

View File

@ -12,15 +12,10 @@ import (
// in this same package based on the requested host. // in this same package based on the requested host.
type MockClient struct{} type MockClient struct{}
func (c MockClient) Get(urlString string) (io.Reader, error) { func (c MockClient) Get(u *url.URL) (io.Reader, error) {
parsedUrl, err := url.Parse(urlString) f, err := mockdata.Data.Open(u.Host + ".html")
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing url: %s", urlString) return nil, fmt.Errorf("can't open mock data for %s", u.Host)
}
f, err := mockdata.Data.Open(parsedUrl.Host + ".html")
if err != nil {
return nil, fmt.Errorf("can't open mock data for %s", parsedUrl.Host)
} }
return f, nil return f, nil

View File

@ -48,7 +48,7 @@ func (m *Manager) Retrieve(productURL string) (*models.Product, error) {
return nil, ErrShopNotFound return nil, ErrShopNotFound
} }
return shop.Get(productURL) return shop.Get(itemUrl)
} }
func NewManager() Manager { func NewManager() Manager {

View File

@ -1,11 +1,15 @@
package models package models
import "github.com/fmartingr/bazaar/pkg/clients" import (
"net/url"
"github.com/fmartingr/bazaar/pkg/clients"
)
type ShopFactory func(baseShop ShopOptions) Shop type ShopFactory func(baseShop ShopOptions) Shop
type Shop interface { type Shop interface {
Get(url string) (*Product, error) Get(*url.URL) (*Product, error)
} }
type ShopOptions struct { type ShopOptions struct {

View File

@ -2,6 +2,7 @@ package akiracomics
import ( import (
"fmt" "fmt"
"net/url"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -17,8 +18,8 @@ type AkiraShop struct {
domains []string domains []string
} }
func (s *AkiraShop) Get(url string) (*models.Product, error) { func (s *AkiraShop) Get(u *url.URL) (*models.Product, error) {
body, err := s.ShopOptions.Client.Get(url) body, err := s.ShopOptions.Client.Get(u)
if err != nil { if err != nil {
return nil, fmt.Errorf("error during request: %s", err) return nil, fmt.Errorf("error during request: %s", err)
} }
@ -36,7 +37,7 @@ func (s *AkiraShop) Get(url string) (*models.Product, error) {
} }
product := models.Product{ product := models.Product{
URL: url, URL: u.String(),
Description: description, Description: description,
} }

View File

@ -1,6 +1,7 @@
package akiracomics_test package akiracomics_test
import ( import (
"net/url"
"testing" "testing"
"github.com/fmartingr/bazaar/pkg/clients" "github.com/fmartingr/bazaar/pkg/clients"
@ -12,7 +13,7 @@ import (
func TestAkiraComics_Ok(t *testing.T) { func TestAkiraComics_Ok(t *testing.T) {
shop := akiracomics.NewAkiraShopFactory()(models.NewShopOptions(clients.NewMockClient())) shop := akiracomics.NewAkiraShopFactory()(models.NewShopOptions(clients.NewMockClient()))
testUrl := "https://www.akiracomics.com/test/" testUrl, _ := url.Parse("https://www.akiracomics.com/test/")
product, err := shop.Get(testUrl) product, err := shop.Get(testUrl)
if err != nil { if err != nil {
@ -27,5 +28,5 @@ func TestAkiraComics_Ok(t *testing.T) {
assert.Equal(t, "https://www.akiracomics.com/imagenes/poridentidad?identidad=24552a54-365d-4d31-a73e-9fd5f927c3a0&ancho=900&alto=", product.ImageURL) assert.Equal(t, "https://www.akiracomics.com/imagenes/poridentidad?identidad=24552a54-365d-4d31-a73e-9fd5f927c3a0&ancho=900&alto=", product.ImageURL)
assert.Equal(t, 8.55, product.Price) assert.Equal(t, 8.55, product.Price)
assert.Equal(t, "8,55 €", product.PriceText) assert.Equal(t, "8,55 €", product.PriceText)
assert.Equal(t, testUrl, product.URL) assert.Equal(t, testUrl.String(), product.URL)
} }

View File

@ -4,7 +4,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"log" "log"
"net/http" "net/url"
"strconv" "strconv"
"strings" "strings"
@ -33,23 +33,19 @@ var releaseDateLayoutByDomain = map[string]string{
Domains[1]: "January 2, 2006", Domains[1]: "January 2, 2006",
} }
func (s *AmazonShop) Get(url string) (*models.Product, error) { func (s *AmazonShop) Get(u *url.URL) (*models.Product, error) {
res, err := http.Get(url) body, err := s.ShopOptions.Client.Get(u)
if err != nil { if err != nil {
return nil, fmt.Errorf("error retrieving url: %s", err) return nil, fmt.Errorf("error during request: %s", err)
}
defer res.Body.Close()
if res.StatusCode != 200 {
return nil, fmt.Errorf("error retrieving url: %d %s", res.StatusCode, res.Status)
} }
doc, err := goquery.NewDocumentFromReader(res.Body) doc, err := goquery.NewDocumentFromReader(body)
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing body: %s", err) return nil, fmt.Errorf("error parsing body: %s", err)
} }
product := models.Product{ product := models.Product{
URL: url, URL: u.String(),
} }
var tentativePrice string var tentativePrice string
@ -69,11 +65,14 @@ func (s *AmazonShop) Get(url string) (*models.Product, error) {
product.Name = strings.TrimSpace(doc.Find("#productTitle").Text()) product.Name = strings.TrimSpace(doc.Find("#productTitle").Text())
imagesJSON, _ := doc.Find("#main-image-container img").Attr("data-a-dynamic-image") imagesJSON, exists := doc.Find("#main-image-container img").Attr("data-a-dynamic-image")
// TODO: error handling if !exists {
log.Printf("Can't find image for %s", u.String())
}
var images map[string]interface{} var images map[string]interface{}
json.Unmarshal([]byte(imagesJSON), &images) if err := json.Unmarshal([]byte(imagesJSON), &images); err != nil {
// TODO: error handling log.Printf("error unmarshalling: %s", err)
}
var lastImage string var lastImage string
for key := range images { for key := range images {
lastImage = key lastImage = key
@ -84,7 +83,7 @@ func (s *AmazonShop) Get(url string) (*models.Product, error) {
if len(releaseDateElement.Nodes) > 0 { if len(releaseDateElement.Nodes) > 0 {
releaseDateRaw := releaseDateElement.Parent().Parent().Find(".rpi-attribute-value").Text() releaseDateRaw := releaseDateElement.Parent().Parent().Find(".rpi-attribute-value").Text()
releaseDate, err := utils.ParseReleaseDate(releaseDateLayoutByDomain[res.Request.URL.Host], strings.TrimSpace(releaseDateRaw), monday.LocaleEsES) releaseDate, err := utils.ParseReleaseDate(releaseDateLayoutByDomain[u.Host], strings.TrimSpace(releaseDateRaw), monday.LocaleEsES)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
} else { } else {

View File

@ -3,6 +3,7 @@ package casadellibro
import ( import (
"fmt" "fmt"
"log" "log"
"net/url"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
@ -26,8 +27,8 @@ type CasaDelLibroShop struct {
priceRegexp *regexp.Regexp priceRegexp *regexp.Regexp
} }
func (s *CasaDelLibroShop) Get(url string) (*models.Product, error) { func (s *CasaDelLibroShop) Get(u *url.URL) (*models.Product, error) {
body, err := s.ShopOptions.Client.Get(url) body, err := s.ShopOptions.Client.Get(u)
if err != nil { if err != nil {
return nil, fmt.Errorf("error during request: %s", err) return nil, fmt.Errorf("error during request: %s", err)
} }
@ -38,7 +39,7 @@ func (s *CasaDelLibroShop) Get(url string) (*models.Product, error) {
} }
product := models.Product{ product := models.Product{
URL: url, URL: u.String(),
} }
// Price // Price

View File

@ -1,6 +1,7 @@
package casadellibro_test package casadellibro_test
import ( import (
"net/url"
"testing" "testing"
"time" "time"
@ -13,7 +14,7 @@ import (
func TestCasaDelLibro_Ok(t *testing.T) { func TestCasaDelLibro_Ok(t *testing.T) {
shop := casadellibro.NewCasaDelLibroShopFactory()(models.NewShopOptions(clients.NewMockClient())) shop := casadellibro.NewCasaDelLibroShopFactory()(models.NewShopOptions(clients.NewMockClient()))
testUrl := "https://www.casadellibro.com/test/" testUrl, _ := url.Parse("https://www.casadellibro.com/test/")
product, err := shop.Get(testUrl) product, err := shop.Get(testUrl)
if err != nil { if err != nil {
@ -27,5 +28,5 @@ func TestCasaDelLibro_Ok(t *testing.T) {
assert.Equal(t, 15.96, product.Price) assert.Equal(t, 15.96, product.Price)
assert.Equal(t, "15.96", product.PriceText) assert.Equal(t, "15.96", product.PriceText)
assert.Equal(t, "2019-01-01T00:00:00Z", product.ReleaseDate.Format(time.RFC3339)) assert.Equal(t, "2019-01-01T00:00:00Z", product.ReleaseDate.Format(time.RFC3339))
assert.Equal(t, testUrl, product.URL) assert.Equal(t, testUrl.String(), product.URL)
} }

View File

@ -3,7 +3,7 @@ package gtmstore
import ( import (
"fmt" "fmt"
"log" "log"
"net/http" "net/url"
"strconv" "strconv"
"strings" "strings"
@ -19,23 +19,19 @@ type GTMStoreShop struct {
domains []string domains []string
} }
func (s *GTMStoreShop) Get(url string) (*models.Product, error) { func (s *GTMStoreShop) Get(u *url.URL) (*models.Product, error) {
res, err := http.Get(url) body, err := s.ShopOptions.Client.Get(u)
if err != nil { if err != nil {
return nil, fmt.Errorf("error retrieving url: %s", err) return nil, fmt.Errorf("error during request: %s", err)
}
defer res.Body.Close()
if res.StatusCode != 200 {
return nil, fmt.Errorf("error retrieving url: %d %s", res.StatusCode, res.Status)
} }
doc, err := goquery.NewDocumentFromReader(res.Body) doc, err := goquery.NewDocumentFromReader(body)
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing body: %s", err) return nil, fmt.Errorf("error parsing body: %s", err)
} }
product := models.Product{ product := models.Product{
URL: url, URL: u.String(),
} }
doc.Find(`div.primary_block`).Each(func(i int, s *goquery.Selection) { doc.Find(`div.primary_block`).Each(func(i int, s *goquery.Selection) {

View File

@ -2,7 +2,7 @@ package heroesdepapel
import ( import (
"fmt" "fmt"
"net/http" "net/url"
"strconv" "strconv"
"strings" "strings"
@ -17,23 +17,19 @@ type HeroesDePapelShop struct {
domains []string domains []string
} }
func (s *HeroesDePapelShop) Get(url string) (*models.Product, error) { func (s *HeroesDePapelShop) Get(u *url.URL) (*models.Product, error) {
res, err := http.Get(url) body, err := s.ShopOptions.Client.Get(u)
if err != nil { if err != nil {
return nil, fmt.Errorf("error retrieving url: %s", err) return nil, fmt.Errorf("error during request: %s", err)
}
defer res.Body.Close()
if res.StatusCode != 200 {
return nil, fmt.Errorf("error retrieving url: %d %s", res.StatusCode, res.Status)
} }
doc, err := goquery.NewDocumentFromReader(res.Body) doc, err := goquery.NewDocumentFromReader(body)
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing body: %s", err) return nil, fmt.Errorf("error parsing body: %s", err)
} }
product := models.Product{ product := models.Product{
URL: url, URL: u.String(),
} }
doc.Find(".section-product-details").Each(func(i int, s *goquery.Selection) { doc.Find(".section-product-details").Each(func(i int, s *goquery.Selection) {

View File

@ -2,7 +2,7 @@ package steam
import ( import (
"fmt" "fmt"
"net/http" "net/url"
"strconv" "strconv"
"strings" "strings"
"time" "time"
@ -18,23 +18,19 @@ type SteamShop struct {
domains []string domains []string
} }
func (s *SteamShop) Get(url string) (*models.Product, error) { func (s *SteamShop) Get(u *url.URL) (*models.Product, error) {
res, err := http.Get(url) body, err := s.ShopOptions.Client.Get(u)
if err != nil { if err != nil {
return nil, fmt.Errorf("error retrieving url: %s", err) return nil, fmt.Errorf("error during request: %s", err)
}
defer res.Body.Close()
if res.StatusCode != 200 {
return nil, fmt.Errorf("error retrieving url: %d %s", res.StatusCode, res.Status)
} }
doc, err := goquery.NewDocumentFromReader(res.Body) doc, err := goquery.NewDocumentFromReader(body)
if err != nil { if err != nil {
return nil, fmt.Errorf("error parsing body: %s", err) return nil, fmt.Errorf("error parsing body: %s", err)
} }
product := models.Product{ product := models.Product{
URL: url, URL: u.String(),
} }
doc.Find(`.page_content_ctn`).Each(func(i int, s *goquery.Selection) { doc.Find(`.page_content_ctn`).Each(func(i int, s *goquery.Selection) {