Skip to content

Commit

Permalink
Merge pull request #5 from kashifkhan0771/feature/history-api
Browse files Browse the repository at this point in the history
Added history API
  • Loading branch information
kashifkhan0771 authored Sep 24, 2024
2 parents 09bade5 + cf21a73 commit 450dee4
Show file tree
Hide file tree
Showing 6 changed files with 86 additions and 16 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ go get "github.com/kashifkhan0771/go-weather"
)

func main() {
config := weatherClient.WeatherAPIConfig{
XApiKey: "<your_x_api_key>",
config, err := weatherClient.NewWeatherAPIConfig(<Your_API_Key>)
if err != nil {
panic(err)
}

weather, err := config.GetCurrentWeather(weatherClient.Options{Query: "Paris"})
Expand Down
1 change: 1 addition & 0 deletions config/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ const (

// API EndPoints
CurrentWeatherJSON = "/current.json"
HistoryWeatherJSON = "/history.json"

// APIsTimeout is a timeout for all the API request to api.weatherapi.com
APIsTimeout = 30
Expand Down
23 changes: 23 additions & 0 deletions helper.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package go_weather

import (
"time"
)

// Define the minimum allowed date (2010-01-01)
var minAllowedDate = time.Date(2010, 1, 1, 0, 0, 0, 0, time.UTC)

// validateQuery ensure the required query in the option is not empty
func validateQuery(q string) bool {
return q != ""
}

// Ensure the date is on or after January 1st, 2010
func validateDate(date time.Time) bool {
if date.IsZero() {
return false
}

// Check if the input date is on or after 2010-01-01
return !date.Before(minAllowedDate)
}
4 changes: 4 additions & 0 deletions options.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package go_weather

import "time"

/*
Options represent the query parameters of the APIs.
Expand All @@ -8,4 +10,6 @@ For more details about each query parameters, please visit: https://www.weathera
type Options struct {
// Query parameter based on which data is sent back (Required)
Query string `json:"q"`
// Date is required for history and future API, it restricts date output for Forecast and History API
Date time.Time `json:"dt"`
}
46 changes: 33 additions & 13 deletions weather_api.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,15 @@ import (
"encoding/json"
"fmt"
"net/http"
"time"

"github.com/kashifkhan0771/go-weather/config"
"github.com/kashifkhan0771/go-weather/models"
)

// GetCurrentWeather return current weather response based on the option query
func (c WeatherAPIConfig) GetCurrentWeather(options Options) (*models.WeatherResponse, error) {
if options.Query == "" {
return nil, fmt.Errorf("query is empty")
if !validateQuery(options.Query) {
return nil, fmt.Errorf("invalid query parameter")
}

url := fmt.Sprintf("%s%s?q=%s", config.BaseURL, config.CurrentWeatherJSON, options.Query)
Expand All @@ -25,27 +24,44 @@ func (c WeatherAPIConfig) GetCurrentWeather(options Options) (*models.WeatherRes

defer resp.Body.Close()

// Check response status code
if resp.StatusCode != http.StatusOK {
// Decode JSON response body
var weatherResp models.WeatherResponse
err = json.NewDecoder(resp.Body).Decode(&weatherResp)
if err != nil {
return nil, err
}

return &weatherResp, nil
}

func (c WeatherAPIConfig) WeatherHistory(options Options) (*models.WeatherResponse, error) {
if !validateQuery(options.Query) {
return nil, fmt.Errorf("invalid query parameter")
} else if !validateDate(options.Date) {
return nil, fmt.Errorf("invalid date parameter")
}

url := fmt.Sprintf("%s%s?q=%s&dt=%s", config.BaseURL, config.HistoryWeatherJSON, options.Query, options.Date.Format("2006-01-02"))

resp, err := c.makeRequest(http.MethodGet, url)
if err != nil {
return nil, err
}

defer resp.Body.Close()

// Decode JSON response body
var weatherResp *models.WeatherResponse
var weatherResp models.WeatherResponse
err = json.NewDecoder(resp.Body).Decode(&weatherResp)
if err != nil {
return nil, err
}

return weatherResp, nil
return &weatherResp, nil
}

// makeRequest is a helper function for making API requests.
func (c WeatherAPIConfig) makeRequest(method, url string) (*http.Response, error) {
client := &http.Client{
Timeout: time.Second * config.APIsTimeout,
}

req, err := http.NewRequest(method, url, http.NoBody)
if err != nil {
return nil, err
Expand All @@ -54,9 +70,13 @@ func (c WeatherAPIConfig) makeRequest(method, url string) (*http.Response, error
req.Header.Set("key", c.XApiKey)

// Send request
resp, err := client.Do(req)
resp, err := c.HttpClient.Do(req)
if err != nil {
return nil, err
return nil, fmt.Errorf("HTTP request failed: %w", err)
}

if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}

return resp, nil
Expand Down
23 changes: 22 additions & 1 deletion weather_api_config.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,28 @@
package go_weather

import (
"errors"
"net/http"
"time"

"github.com/kashifkhan0771/go-weather/config"
)

// WeatherAPIConfig has the configurations required to call the APIs
type WeatherAPIConfig struct {
// XApiKey can be obtained from: https://www.weatherapi.com/ after you log in
XApiKey string `json:"key"`
XApiKey string `json:"key"`
HttpClient *http.Client `json:"-"`
}

// NewWeatherAPIConfig creates a WeatherAPIConfig with apiKey passed.
func NewWeatherAPIConfig(apiKey string) (*WeatherAPIConfig, error) {
if apiKey == "" {
return nil, errors.New("API key cannot be empty")
}

return &WeatherAPIConfig{
XApiKey: apiKey,
HttpClient: &http.Client{Timeout: time.Second * config.APIsTimeout},
}, nil
}

0 comments on commit 450dee4

Please sign in to comment.