-
Notifications
You must be signed in to change notification settings - Fork 0
/
spacewatch.go
139 lines (121 loc) · 3.79 KB
/
spacewatch.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package spacewatch
import (
"encoding/json"
"fmt"
"log"
"net/http"
"time"
"github.com/qba73/spacewatch/iss"
"github.com/qba73/spacewatch/weather"
cache "github.com/victorspringer/http-cache"
"github.com/victorspringer/http-cache/adapter/memory"
)
// Status represents the ISS status report.
type Status struct {
// Lat/Long are coordinates of the ISS.
Lat float64 `json:"lat"`
Long float64 `json:"long"`
Timezone string `json:"timezone"`
CloudCoverage int `json:"cloud_coverage"`
DayPart string `json:"day_part"`
// IsVisible is calculated based on
// cloud coverage and part of the day.
IsVisible bool `json:"is_visible"`
}
// isVisible holds logic for calculating visibility of the ISS.
// It is assumed that the Space Station is visible during
// the night and when the cloud coverage is less than 30%.
func isVisible(cloudCoverage int, dayPart string) bool {
return cloudCoverage <= 30 && dayPart == "d"
}
// Location holds coordinates information.
type Location struct {
Lat float64
Long float64
}
// GetISSLocation is a high level function that knows how to return
// current location (lat/long) of the International Space Station.
//
// GetISSLocation uses default implementation of the iss client.
func GetISSLocation() (Location, error) {
pos, err := iss.New().Get()
if err != nil {
return Location{}, err
}
p := Location{Lat: pos.Lat, Long: pos.Long}
return p, nil
}
// GetISSStatus holds the core logic used for generating
// ISS status update. It takes APIKEY required by the undelying
// weather service and return the Status struct, or error
// if any of the internal operation fail.
//
// GetISSStatus leverages deafult clients for ISS location
// and weather status.
func GetISSStatus(apikey string) (Status, error) {
loc, err := GetISSLocation()
if err != nil {
return Status{}, err
}
weather, err := weather.New(apikey).Get(weather.Location{Lat: loc.Lat, Long: loc.Long})
if err != nil {
return Status{}, nil
}
dayPart := map[string]string{"d": "day", "n": "night"}
return Status{
Lat: weather.Lat,
Long: weather.Long,
Timezone: weather.Timezone,
CloudCoverage: weather.Clouds,
DayPart: dayPart[weather.DayNight],
IsVisible: isVisible(weather.Clouds, weather.DayNight),
}, nil
}
// =======================================================
// ISS Handlers
type ISSStatusHandler struct {
ApiKey string
Log *log.Logger
StatusChecker func(apikey string) (Status, error)
}
func (s *ISSStatusHandler) Get(w http.ResponseWriter, r *http.Request) {
issStatus, err := s.StatusChecker(s.ApiKey)
if err != nil {
s.Log.Printf("error: getting weather report %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
data, err := json.Marshal(issStatus)
if err != nil {
s.Log.Printf("error: marshalling weather report %v", err)
w.WriteHeader(http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json; charset=utf-8")
w.WriteHeader(http.StatusOK)
if _, err := w.Write(data); err != nil {
s.Log.Printf("error writing weather report: %v", err)
}
}
// =======================================================
// Server-side Caching
// NewCacheClient knows how to construct default in-memory
// cache with the specified TTL parameter.
func NewCacheClient(ttl time.Duration) (*cache.Client, error) {
memcached, err := memory.NewAdapter(
memory.AdapterWithAlgorithm(memory.LRU),
memory.AdapterWithCapacity(10000000),
)
if err != nil {
return nil, fmt.Errorf("creating memory cache adapter: %v", err)
}
cacheClient, err := cache.NewClient(
cache.ClientWithAdapter(memcached),
cache.ClientWithTTL(ttl),
cache.ClientWithRefreshKey("opn"),
)
if err != nil {
return nil, fmt.Errorf("creating cache client: %v", err)
}
return cacheClient, nil
}