feat: Add Diyanet API provider

feat: Add UI
This commit is contained in:
2023-03-05 14:41:38 +01:00
parent d4ff42387f
commit 93af84cef8
17 changed files with 642 additions and 43 deletions
+7 -3
View File
@@ -15,10 +15,14 @@ type Fetcher interface {
FetchParsed(ctx context.Context, url string) (*goquery.Document, error)
}
type Diyanet struct {
type Provider struct {
FetcherFunc func(ctx context.Context, url string) (*goquery.Document, error)
}
func New(fetcherFunc func(ctx context.Context, url string) (*goquery.Document, error)) *Provider {
return &Provider{FetcherFunc: fetcherFunc}
}
var reNumeric = regexp.MustCompile(`\d+`)
func validateLocation(location string) error {
@@ -28,7 +32,7 @@ func validateLocation(location string) error {
return nil
}
func (d Diyanet) Get(ctx context.Context, location string) ([]prayer.Times, error) {
func (d Provider) Get(ctx context.Context, location string) ([]prayer.Times, error) {
if err := validateLocation(location); err != nil {
return nil, fmt.Errorf("%w: %v", prayer.ErrInvalidLocation, err)
}
@@ -63,6 +67,6 @@ func (d Diyanet) Get(ctx context.Context, location string) ([]prayer.Times, erro
return times, err
}
func (d Diyanet) Name() string {
func (d Provider) Name() string {
return "diyanet"
}
+5 -5
View File
@@ -9,7 +9,7 @@ import (
"github.com/PuerkitoBio/goquery"
"github.com/stretchr/testify/assert"
"prayertimes/internal/scrapeutils"
"prayertimes/internal/net"
"prayertimes/pkg/prayer"
)
@@ -62,13 +62,13 @@ const mockHtml = `
func TestDiyanet_Get(t *testing.T) {
t.Run("validates location", func(t *testing.T) {
d := Diyanet{}
d := Provider{}
_, err := d.Get(context.Background(), " not numeric ")
assert.ErrorIs(t, err, prayer.ErrInvalidLocation)
})
t.Run("extracts prayer times", func(t *testing.T) {
d := Diyanet{
d := Provider{
FetcherFunc: mockFetcher(mockHtml).Fetch,
}
actual, err := d.Get(context.Background(), "1234")
@@ -102,8 +102,8 @@ func TestDiyanet_Get(t *testing.T) {
t.Skip()
}
d := Diyanet{
FetcherFunc: scrapeutils.GetParsed,
d := Provider{
FetcherFunc: net.GetParsed,
}
times, err := d.Get(context.Background(), "9205")
assert.NoError(t, err)
+100
View File
@@ -0,0 +1,100 @@
package diyanetapi
import (
"context"
"fmt"
"time"
"github.com/imroc/req/v3"
"prayertimes/pkg/prayer"
)
type Provider struct {
http *req.Client
}
func New(c *req.Client) Provider {
return Provider{
http: c.Clone().SetCommonBasicAuth("diyanet", "Q6Y3vYt5F3x2txPaaMF3uPgbK99EJhpM"),
}
}
func (d Provider) GetByCoords(ctx context.Context, coords prayer.Coordinates) ([]prayer.Times, error) {
res, err := d.http.NewRequest().
SetContext(ctx).
SetQueryParams(map[string]string{
"latitude": fmt.Sprintf("%f", coords.Latitude),
"longitude": fmt.Sprintf("%f", coords.Longitude),
}).
Get("https://namazvakti.diyanet.gov.tr/api/NamazVakti/Aylik")
if err != nil {
return nil, fmt.Errorf("failed to get prayer times by coords: %w", err)
}
return d.parseResponse(res)
}
func (d Provider) Get(ctx context.Context, locationID string) ([]prayer.Times, error) {
res, err := d.http.NewRequest().
SetContext(ctx).
SetQueryParam("ilceId", locationID).
Get("https://namazvakti.diyanet.gov.tr/api/NamazVakti/Aylik")
if err != nil {
return nil, fmt.Errorf("failed to get prayer times by location id: %w", err)
}
return d.parseResponse(res)
}
func (d Provider) parseResponse(res *req.Response) ([]prayer.Times, error) {
var response struct {
Success bool `json:"success"`
ResultObject struct {
Location struct {
ID int `json:"konum_Id"`
Timezone string `json:"timezone"`
} `json:"konum"`
PrayerTimes []struct {
Date time.Time `json:"miladi_tarih_uzun_Iso8601"`
Fajr time.Time `json:"imsak"`
Sunrise time.Time `json:"gunes"`
Dhuhr time.Time `json:"ogle"`
Asr time.Time `json:"ikindi"`
Maghrib time.Time `json:"aksam"`
Isha time.Time `json:"yatsi"`
} `json:"namazVakti"`
} `json:"resultObject"`
}
if err := res.Unmarshal(&response); err != nil {
return nil, fmt.Errorf("failed to unmarshal as json: %w", err)
}
if !response.Success {
return nil, fmt.Errorf("received error: %s", res.String())
}
var times []prayer.Times
const format = "15:04"
now := time.Now()
for _, pt := range response.ResultObject.PrayerTimes {
then := pt.Date.UTC().Truncate(time.Hour * 24)
if then.Before(now) {
continue
}
times = append(times, prayer.Times{
Date: then,
Fajr: pt.Fajr.Format(format),
Sunrise: pt.Sunrise.Format(format),
Dhuhr: pt.Dhuhr.Format(format),
Asr: pt.Asr.Format(format),
Maghrib: pt.Maghrib.Format(format),
Isha: pt.Isha.Format(format),
})
}
return times, nil
}
func (d Provider) Name() string {
return "diyanet"
}
+35
View File
@@ -0,0 +1,35 @@
package diyanetapi
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
"prayertimes/internal/net"
"prayertimes/pkg/prayer"
)
func TestDiyanetAPI_GetByCoords(t *testing.T) {
p := New(net.ReqClient)
t.Run("by coords", func(t *testing.T) {
t.Parallel()
times, err := p.GetByCoords(context.Background(), prayer.Coordinates{
Latitude: 52.5100846,
Longitude: 13.4518284,
})
assert.NoError(t, err)
assert.NotEmpty(t, times)
t.Logf("%#+v", times[0])
})
t.Run("by id", func(t *testing.T) {
t.Parallel()
times, err := p.Get(context.Background(), "11104")
assert.NoError(t, err)
assert.NotEmpty(t, times)
t.Logf("%#+v", times[0])
})
}
+10
View File
@@ -13,6 +13,16 @@ type TimesProvider interface {
Name() string
}
type Coordinates struct {
Latitude float64
Longitude float64
}
type LocationTimesProvider interface {
GetByCoords(ctx context.Context, coords Coordinates) ([]Times, error)
Name() string
}
type Times struct {
Date time.Time `json:"date"`
Fajr string `json:"fajr"`