diff --git a/.golangci.yml b/.golangci.yml
index 741ac8e..5633122 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -32,6 +32,9 @@ linters:
linters-settings:
lll:
line-length: 150
+ misspell:
+ ignore-words:
+ - autor
issues:
exclude-rules:
diff --git a/go.mod b/go.mod
index 31c1334..bff3b5b 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,17 @@
module github.com/Kichiyaki/lubimyczytacrss
go 1.18
+
+require (
+ github.com/PuerkitoBio/goquery v1.8.0
+ github.com/go-chi/chi/v5 v5.0.7
+ github.com/stretchr/testify v1.7.1
+)
+
+require (
+ github.com/andybalholm/cascadia v1.3.1 // indirect
+ github.com/davecgh/go-spew v1.1.0 // indirect
+ github.com/pmezard/go-difflib v1.0.0 // indirect
+ golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 // indirect
+ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
+)
diff --git a/go.sum b/go.sum
new file mode 100644
index 0000000..7b8b357
--- /dev/null
+++ b/go.sum
@@ -0,0 +1,24 @@
+github.com/PuerkitoBio/goquery v1.8.0 h1:PJTF7AmFCFKk1N6V6jmKfrNH9tV5pNE6lZMkG0gta/U=
+github.com/PuerkitoBio/goquery v1.8.0/go.mod h1:ypIiRMtY7COPGk+I/YbZLbxsxn9g5ejnI2HSMtkjZvI=
+github.com/andybalholm/cascadia v1.3.1 h1:nhxRkql1kdYCc8Snf7D5/D3spOX+dBgjA6u8x004T2c=
+github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA=
+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/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
+github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
+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.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
+github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+golang.org/x/net v0.0.0-20210916014120-12bc252f5db8 h1:/6y1LfuqNuQdHAm0jjtPtgRcxIxjVZgm5OTu8/QhZvk=
+golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
+golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
+golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
+golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/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/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/internal/lubimyczytac/client.go b/internal/lubimyczytac/client.go
new file mode 100644
index 0000000..b6684bc
--- /dev/null
+++ b/internal/lubimyczytac/client.go
@@ -0,0 +1,146 @@
+package lubimyczytac
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "net/http"
+ "net/url"
+ "strings"
+
+ "github.com/PuerkitoBio/goquery"
+)
+
+const (
+ defaultBaseURL = "https://lubimyczytac.pl"
+)
+
+var (
+ ErrAuthorNotFound = errors.New("author not found")
+ ErrUnexpectedStatus = errors.New("unexpected http status was returned by lubimyczytac.pl")
+)
+
+type Book struct {
+ Title string
+ URL string
+}
+
+type Author struct {
+ ID string
+ Name string
+ ShortDescription string
+ URL string
+ Books []Book
+}
+
+type Client struct {
+ http *http.Client
+ baseURL string
+}
+
+type ClientOption func(c *Client)
+
+func WithBaseURL(baseURL string) ClientOption {
+ return func(c *Client) {
+ c.baseURL = baseURL
+ }
+}
+
+func NewClient(client *http.Client, opts ...ClientOption) *Client {
+ c := &Client{http: client, baseURL: defaultBaseURL}
+ for _, opt := range opts {
+ opt(c)
+ }
+ return c
+}
+
+func (c *Client) GetAuthor(ctx context.Context, id string) (Author, error) {
+ req, err := http.NewRequestWithContext(
+ ctx,
+ http.MethodGet,
+ fmt.Sprintf("%s/autor/%s/x", c.baseURL, id),
+ nil,
+ )
+ if err != nil {
+ return Author{}, fmt.Errorf("http.NewRequestWithContext: %w", err)
+ }
+
+ resp, err := c.http.Do(req)
+ if err != nil {
+ return Author{}, fmt.Errorf("httpClient.Do: %w", err)
+ }
+ defer func() {
+ _ = resp.Body.Close()
+ }()
+ if resp.StatusCode != http.StatusOK {
+ _, _ = io.Copy(io.Discard, resp.Body)
+ if resp.StatusCode == http.StatusNotFound {
+ return Author{}, ErrAuthorNotFound
+ }
+ return Author{}, ErrUnexpectedStatus
+ }
+
+ p, err := newAuthorPageParser(resp.Body)
+ if err != nil {
+ _, _ = io.Copy(io.Discard, resp.Body)
+ return Author{}, fmt.Errorf("newAuthorPageParser: %w", err)
+ }
+
+ return Author{
+ ID: id,
+ Name: p.name(),
+ ShortDescription: p.shortDescription(),
+ URL: p.url(),
+ Books: p.books(),
+ }, nil
+}
+
+type authorPageParser struct {
+ doc *goquery.Document
+ baseURL *url.URL
+}
+
+func newAuthorPageParser(r io.Reader) (authorPageParser, error) {
+ doc, err := goquery.NewDocumentFromReader(r)
+ if err != nil {
+ return authorPageParser{}, fmt.Errorf("goquery.NewDocumentFromReader: %w", err)
+ }
+ baseURL, err := url.Parse(doc.Find("head base").AttrOr("href", ""))
+ if err != nil {
+ return authorPageParser{}, fmt.Errorf("url.Parse: %w", err)
+ }
+ return authorPageParser{
+ doc: doc,
+ baseURL: baseURL,
+ }, nil
+}
+
+func (p authorPageParser) name() string {
+ return strings.TrimSpace(p.doc.Find("#author-info .title-container").Text())
+}
+
+func (p authorPageParser) url() string {
+ return strings.TrimSpace(p.doc.Find(`meta[property="og:url"]`).AttrOr("content", ""))
+}
+
+func (p authorPageParser) shortDescription() string {
+ return strings.TrimSpace(p.doc.Find(`meta[name="description"]`).AttrOr("content", ""))
+}
+
+func (p authorPageParser) books() []Book {
+ booksSel := p.doc.Find("#authorBooks .authorAllBooks__single")
+ books := make([]Book, booksSel.Length())
+ booksSel.Each(func(i int, selection *goquery.Selection) {
+ bookUrl := url.URL{
+ Scheme: p.baseURL.Scheme,
+ Host: p.baseURL.Host,
+ Path: selection.Find(".authorAllBooks__singleTextTitle").AttrOr("href", ""),
+ }
+ books[i] = Book{
+ Title: strings.TrimSpace(selection.Find(".authorAllBooks__singleTextTitle").Text()),
+ URL: bookUrl.String(),
+ }
+ })
+ return books
+}
diff --git a/internal/lubimyczytac/client_test.go b/internal/lubimyczytac/client_test.go
new file mode 100644
index 0000000..83ec801
--- /dev/null
+++ b/internal/lubimyczytac/client_test.go
@@ -0,0 +1,48 @@
+package lubimyczytac_test
+
+import (
+ "context"
+ "fmt"
+ "net/http"
+ "net/http/httptest"
+ "testing"
+
+ "github.com/Kichiyaki/lubimyczytacrss/internal/lubimyczytac/testdata"
+
+ "github.com/stretchr/testify/assert"
+
+ "github.com/Kichiyaki/lubimyczytacrss/internal/lubimyczytac"
+)
+
+func TestClient_GetAuthor(t *testing.T) {
+ t.Parallel()
+
+ t.Run("OK", func(t *testing.T) {
+ t.Parallel()
+
+ for _, author := range testdata.Authors {
+ author := author
+
+ t.Run("authorID="+author.ID, func(t *testing.T) {
+ t.Parallel()
+
+ srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ if r.URL.Path != fmt.Sprintf("/autor/%s/x", author.ID) || r.Method != http.MethodGet {
+ w.WriteHeader(http.StatusBadRequest)
+ return
+ }
+
+ w.WriteHeader(http.StatusOK)
+ _, _ = w.Write(author.HTML)
+ }))
+ defer srv.Close()
+
+ result, err := lubimyczytac.
+ NewClient(srv.Client(), lubimyczytac.WithBaseURL(srv.URL)).
+ GetAuthor(context.Background(), author.ID)
+ assert.NoError(t, err)
+ assert.Equal(t, author.Author, result)
+ })
+ }
+ })
+}
diff --git a/internal/lubimyczytac/testdata/john_flanagan.html b/internal/lubimyczytac/testdata/john_flanagan.html
new file mode 100644
index 0000000..152e233
--- /dev/null
+++ b/internal/lubimyczytac/testdata/john_flanagan.html
@@ -0,0 +1 @@
+
John Flanagan | Wszystkie książki, wywiady, artykuły | Lubimyczytać.pl
John Flanagan Urodzony i wychowany w Sydney w Australii, John Flanagan od dzieciństwa marzył o tym, by zostać pisarzem. Nie było łatwo. Pracował w agencji reklamowej, ale dopiero satyryczny wiersz opisujący paskudnego kolegę zwrócił na niego uwagę przełożonych. Jeden z szefów agencji uznał, że warto zainwestować w młodego pracownika i tak Flanagan dołączył do grona copywriterów. Przez dwie dekady pisał spoty reklamowe i scenariusze, później zaś trafił do telewizji i został jednym ze współtwórców sukcesu sitcomu Hi Dad!, zasłynął jednak jako autor “Zwiadowców”, serii osadzonych w fantastycznym świecie powieści dla młodzieży. Flanagan zaczął pisać historię Willa dla swego syna, Michaela, jednak to, co miało być opowieściami na dobranoc, szybko przerodziło się w cykl powieści. Pisarz wraz z żoną mieszka na przedmieściach Manly. Ma troje dzieci i czworo wnuków.
Średnia ocena książek autora 7,8 / 10
16 107 przeczytało książki autora 38 338 chce przeczytać książki autora 1 840 fanów autora
Zostań fanem
diff --git a/internal/lubimyczytac/testdata/remigiusz_mroz.html b/internal/lubimyczytac/testdata/remigiusz_mroz.html
new file mode 100644
index 0000000..30d5e39
--- /dev/null
+++ b/internal/lubimyczytac/testdata/remigiusz_mroz.html
@@ -0,0 +1 @@
+Remigiusz Mróz | Wszystkie książki, wywiady, artykuły | Lubimyczytać.pl
Żródło zdjecia: facebook.com/remigiuszmroz
Znany również jako: Ove Løgmansbø Żródło zdjecia: facebook.com/remigiuszmroz
Pisze książki: fantasy, science fiction , horror , kryminał, sensacja, thriller , literatura obyczajowa, romans , powieść historyczna , biografia, autobiografia, pamiętnik , czasopisma Oficjalna strona: Przejdź do strony www Urodzony: 15 stycznia 1987 Polski pisarz, autor powieści kryminalnych oraz cyklu publicystycznego „Kurs pisania”. Ukończył z wyróżnieniem Akademię Leona Koźmińskiego w Warszawie, gdzie uzyskał stopień naukowy doktora nauk prawnych. Laureat Nagrody Czytelników Wielkiego Kalibru z 2016 roku za powieść pt. „Kasacja”. W 2017 roku ujawnił się jako Ove Løgmansbø, autor kolejnych trzech powieści. Jego książki były nominowane w Plebiscycie Książka Roku lubimyczytać.pl w kategorii kryminał, sensacja, thriller. Otrzymał dotąd dwie statuetki. Seria z Joanną Chyłką stała się podstawą serialu „Chyłka. Zaginięcie” w reżyserii Łukasza Palkowskiego z Magdaleną Cielecką w roli głównej. Wykupiono także prawa do ekranizacji innych serii autora - z komisarzem Forstem i cyklu „W kręgach władzy”.
Średnia ocena książek autora 7,1 / 10
56 858 przeczytało książki autora 99 778 chce przeczytać książki autora 5 436 fanów autora
Zostań fanem
diff --git a/internal/lubimyczytac/testdata/testdata.go b/internal/lubimyczytac/testdata/testdata.go
new file mode 100644
index 0000000..9a2889f
--- /dev/null
+++ b/internal/lubimyczytac/testdata/testdata.go
@@ -0,0 +1,83 @@
+package testdata
+
+import (
+ _ "embed"
+
+ "github.com/Kichiyaki/lubimyczytacrss/internal/lubimyczytac"
+)
+
+//go:embed remigiusz_mroz.html
+var remigiuszMrozHTML []byte
+
+//go:embed john_flanagan.html
+var johnFlanaganHTML []byte
+
+type AuthorWithHTML struct {
+ lubimyczytac.Author
+ HTML []byte
+}
+
+var Authors = []AuthorWithHTML{
+ {
+ Author: lubimyczytac.Author{
+ ID: "82094",
+ Name: "Remigiusz Mróz",
+ ShortDescription: "Polski pisarz, autor powieści kryminalnych oraz cyklu publicystycznego „Kurs pisania”.Ukończył z wyróżnieniem Akademię Leona Koźmińskiego w Warszawie, gd...",
+ URL: "https://lubimyczytac.pl/autor/82094/remigiusz-mroz",
+ Books: []lubimyczytac.Book{
+ {Title: "Obrazy z przeszłości", URL: "https://lubimyczytac.pl/ksiazka/5016998/obrazy-z-przeszlosci"},
+ {Title: "Skazanie", URL: "https://lubimyczytac.pl/ksiazka/5009453/skazanie"},
+ {Title: "Behawiorysta", URL: "https://lubimyczytac.pl/ksiazka/5006528/behawiorysta"},
+ {Title: "Projekt Riese", URL: "https://lubimyczytac.pl/ksiazka/4998407/projekt-riese"},
+ {Title: "Immunitet", URL: "https://lubimyczytac.pl/ksiazka/4990168/immunitet"},
+ {Title: "Przepaść", URL: "https://lubimyczytac.pl/ksiazka/4988766/przepasc"},
+ {Title: "Egzekucja", URL: "https://lubimyczytac.pl/ksiazka/4983192/egzekucja"},
+ {Title: "Wybaczam ci", URL: "https://lubimyczytac.pl/ksiazka/4975543/wybaczam-ci"},
+ {Title: "Inwigilacja", URL: "https://lubimyczytac.pl/ksiazka/4968996/inwigilacja"},
+ {Title: "Ekstremista", URL: "https://lubimyczytac.pl/ksiazka/4968712/ekstremista"},
+ {Title: "Afekt", URL: "https://lubimyczytac.pl/ksiazka/4962195/afekt"},
+ {Title: "Głębia osobliwości cz. 2", URL: "https://lubimyczytac.pl/ksiazka/5009924/glebia-osobliwosci-cz-2"},
+ {Title: "Szepty spoza nicości", URL: "https://lubimyczytac.pl/ksiazka/4955433/szepty-spoza-nicosci"},
+ {Title: "W cieniu prawa", URL: "https://lubimyczytac.pl/ksiazka/4947287/w-cieniu-prawa"},
+ {Title: "Księgarenka przy ulicy Wiśniowej", URL: "https://lubimyczytac.pl/ksiazka/4944645/ksiegarenka-przy-ulicy-wisniowej"},
+ {Title: "Rewizja", URL: "https://lubimyczytac.pl/ksiazka/4943912/rewizja"},
+ {Title: "Halny", URL: "https://lubimyczytac.pl/ksiazka/4943948/halny"},
+ {Title: "Precedens", URL: "https://lubimyczytac.pl/ksiazka/4939105/precedens"},
+ {Title: "Osiedle RZNiW", URL: "https://lubimyczytac.pl/ksiazka/4932748/osiedle-rzniw"},
+ {Title: "Lot 202", URL: "https://lubimyczytac.pl/ksiazka/4926011/lot-202"},
+ },
+ },
+ HTML: remigiuszMrozHTML,
+ },
+ {
+ Author: lubimyczytac.Author{
+ ID: "19013",
+ Name: "John Flanagan",
+ ShortDescription: "John Flanagan Urodzony i wychowany w Sydney w Australii, John Flanagan od dzieciństwa marzył o tym, by zostać pisarzem. Nie było łatwo. Pracował w agencji r...",
+ URL: "https://lubimyczytac.pl/autor/19013/john-flanagan",
+ Books: []lubimyczytac.Book{
+ {Title: "Morska pogoń", URL: "https://lubimyczytac.pl/ksiazka/5016250/morska-pogon"},
+ {Title: "Ruiny Gorlanu", URL: "https://lubimyczytac.pl/ksiazka/4989803/ruiny-gorlanu"},
+ {Title: "Ziemia skuta lodem", URL: "https://lubimyczytac.pl/ksiazka/4989807/ziemia-skuta-lodem"},
+ {Title: "Bitwa o Skandię", URL: "https://lubimyczytac.pl/ksiazka/4989809/bitwa-o-skandie"},
+ {Title: "Ucieczka z zamku Falaise", URL: "https://lubimyczytac.pl/ksiazka/4984975/ucieczka-z-zamku-falaise"},
+ {Title: "Płonący most", URL: "https://lubimyczytac.pl/ksiazka/4989806/plonacy-most"},
+ {Title: "Zaginiony książę", URL: "https://lubimyczytac.pl/ksiazka/4935503/zaginiony-ksiaze"},
+ {Title: "Powrót Temudżeinów", URL: "https://lubimyczytac.pl/ksiazka/4897979/powrot-temudzeinow"},
+ {Title: "Pojedynek w Araluenie", URL: "https://lubimyczytac.pl/ksiazka/4861058/pojedynek-w-araluenie"},
+ {Title: "Klan Czerwonego Lisa", URL: "https://lubimyczytac.pl/ksiazka/4851290/klan-czerwonego-lisa"},
+ {Title: "Kaldera", URL: "https://lubimyczytac.pl/ksiazka/4811916/kaldera"},
+ {Title: "Bitwa na Wrzosowiskach", URL: "https://lubimyczytac.pl/ksiazka/3874675/bitwa-na-wrzosowiskach"},
+ {Title: "Nieznany ląd", URL: "https://lubimyczytac.pl/ksiazka/303434/nieznany-lad"},
+ {Title: "Turniej w Gorlanie", URL: "https://lubimyczytac.pl/ksiazka/266058/turniej-w-gorlanie"},
+ {Title: "Góra Skorpiona", URL: "https://lubimyczytac.pl/ksiazka/230915/gora-skorpiona"},
+ {Title: "Niewolnicy z Socorro", URL: "https://lubimyczytac.pl/ksiazka/220609/niewolnicy-z-socorro"},
+ {Title: "Królewski zwiadowca", URL: "https://lubimyczytac.pl/ksiazka/192775/krolewski-zwiadowca"},
+ {Title: "Pościg", URL: "https://lubimyczytac.pl/ksiazka/167327/poscig"},
+ {Title: "Najeźdźcy", URL: "https://lubimyczytac.pl/ksiazka/144610/najezdzcy"},
+ {Title: "Zaginione historie", URL: "https://lubimyczytac.pl/ksiazka/131440/zaginione-historie"},
+ },
+ },
+ HTML: johnFlanaganHTML,
+ },
+}
diff --git a/main.go b/main.go
index 764763a..a1dafe0 100644
--- a/main.go
+++ b/main.go
@@ -2,17 +2,42 @@ package main
import (
"context"
+ "encoding/xml"
"log"
"net/http"
"os"
"os/signal"
"time"
+
+ "github.com/go-chi/chi/v5/middleware"
+
+ "github.com/go-chi/chi/v5"
+
+ "github.com/Kichiyaki/lubimyczytacrss/internal/lubimyczytac"
+)
+
+const (
+ defaultClientTimeout = 5 * time.Second
)
func main() {
+ r := chi.NewRouter()
+ r.Use(
+ middleware.RealIP,
+ middleware.RequestLogger(&middleware.DefaultLogFormatter{
+ NoColor: true,
+ Logger: log.Default(),
+ }),
+ middleware.Recoverer,
+ middleware.Heartbeat("/health"),
+ )
+ newHandler(lubimyczytac.NewClient(&http.Client{
+ Timeout: defaultClientTimeout,
+ })).register(r)
+
httpSrv := &http.Server{
Addr: ":9234",
- Handler: nil,
+ Handler: r,
ReadTimeout: 2 * time.Second,
ReadHeaderTimeout: 2 * time.Second,
WriteTimeout: 2 * time.Second,
@@ -35,6 +60,90 @@ func main() {
ctxShutdown, cancelCtxShutdown := context.WithTimeout(context.Background(), 10*time.Second)
defer cancelCtxShutdown()
if err := httpSrv.Shutdown(ctxShutdown); err != nil {
- log.Fatalln("httpSrv.Shutdown:", err)
+ log.Println("httpSrv.Shutdown:", err)
}
}
+
+type rssItem struct {
+ XMLName xml.Name `xml:"item"`
+ Title string `xml:"title"`
+ Link string `xml:"link"`
+ GUID string `xml:"guid"`
+ Description string `xml:"description"`
+}
+
+func rssItemsFromBooks(books []lubimyczytac.Book) []rssItem {
+ items := make([]rssItem, len(books))
+ for i, b := range books {
+ items[i] = rssItem{
+ Title: b.Title,
+ Link: b.URL,
+ GUID: b.URL,
+ Description: "",
+ }
+ }
+ return items
+}
+
+type rssChannel struct {
+ XMLName xml.Name `xml:"channel"`
+ Title string `xml:"title"`
+ Link string `xml:"link"`
+ Description string `xml:"description"`
+ Language string `xml:"language"`
+ Items []rssItem `xml:"items"`
+}
+
+func rssChannelFromAuthor(author lubimyczytac.Author) rssChannel {
+ return rssChannel{
+ Title: author.Name,
+ Description: author.ShortDescription,
+ Link: author.URL,
+ Items: rssItemsFromBooks(author.Books),
+ }
+}
+
+type rssMain struct {
+ XMLName xml.Name `xml:"rss"`
+ Version string `xml:"version,attr"`
+ Channel rssChannel `xml:"channel"`
+}
+
+func rssMainFromAuthor(author lubimyczytac.Author) rssMain {
+ return rssMain{
+ Version: "2.0",
+ Channel: rssChannelFromAuthor(author),
+ }
+}
+
+type handler struct {
+ client *lubimyczytac.Client
+}
+
+func newHandler(client *lubimyczytac.Client) *handler {
+ return &handler{client: client}
+}
+
+func (h *handler) register(r chi.Router) {
+ r.Get("/api/v1/rss/author/{authorID}", h.getRSSAuthor)
+}
+
+func (h *handler) getRSSAuthor(w http.ResponseWriter, r *http.Request) {
+ ctx := r.Context()
+
+ author, err := h.client.GetAuthor(ctx, chi.URLParamFromCtx(ctx, "authorID"))
+ if err == lubimyczytac.ErrAuthorNotFound {
+ w.WriteHeader(http.StatusNotFound)
+ _, _ = w.Write([]byte(`author not found`))
+ return
+ }
+ if err != nil {
+ w.WriteHeader(http.StatusInternalServerError)
+ _, _ = w.Write([]byte(`something went wrong while getting author info: ` + err.Error()))
+ return
+ }
+
+ w.Header().Set("Content-Type", "text/xml; charset=utf-8")
+ w.WriteHeader(http.StatusOK)
+ _ = xml.NewEncoder(w).Encode(rssMainFromAuthor(author))
+}
DYSKUSJE