computeResponse performs location resolution, weather fetch, rendering and formatting. Returns ready-to-cache domain.CacheEntry or error. Does NOT write to ResponseWriter — that stays in handler.
( ctx context.Context, r *http.Request, tracker *TimeTracker, )
| 250 | // Returns ready-to-cache domain.CacheEntry or error. |
| 251 | // Does NOT write to ResponseWriter — that stays in handler. |
| 252 | func (s *WeatherService) computeResponse( |
| 253 | ctx context.Context, r *http.Request, |
| 254 | tracker *TimeTracker, |
| 255 | ) (*domain.CacheEntry, error) { |
| 256 | debugRequested := r.URL.Query().Get("debug") != "" |
| 257 | |
| 258 | clientIP := getClientIP(r) |
| 259 | |
| 260 | // ── Determine location string ──────────────────────────────────────── |
| 261 | start := time.Now() |
| 262 | path := r.URL.Path // cleanPath(r.URL.Path) // helper: trim / and extensions |
| 263 | autoDetect := isAutoDetectPath(path) |
| 264 | |
| 265 | // 0. Locate IP |
| 266 | var ipData *domain.IPData |
| 267 | var errIP error |
| 268 | for _, ipLocator := range s.IPLocators { |
| 269 | ipData, errIP = ipLocator.GetIPData(clientIP) |
| 270 | if errIP == nil && ipData != nil { |
| 271 | break |
| 272 | } |
| 273 | } |
| 274 | tracker.Add("IP locating", time.Since(start)) |
| 275 | |
| 276 | // For testing purposes, it is possibe to simulate US-based clients |
| 277 | // by setting country code in the HTTP headers of the request. |
| 278 | if countryCode := r.Header.Get("X-Client-Country-Code"); countryCode != "" { |
| 279 | ipData.CountryCode = countryCode |
| 280 | } |
| 281 | |
| 282 | ///////////////////// |
| 283 | // This part should be moved to queryparser. |
| 284 | ipOpts := options.Options{} |
| 285 | if isClientInUSA(ipData) { |
| 286 | ipOpts.UseMetric = false |
| 287 | ipOpts.UseImperial = true |
| 288 | } else { |
| 289 | ipOpts.UseMetric = true |
| 290 | ipOpts.UseImperial = false |
| 291 | } |
| 292 | /////////////////// |
| 293 | |
| 294 | // 1. Parse options (cheap, always first) |
| 295 | start = time.Now() |
| 296 | opts, err := s.QueryParser.Parse(ctx, r, &ipOpts) |
| 297 | if err != nil { |
| 298 | return nil, err |
| 299 | } |
| 300 | tracker.Add("Options parsing", time.Since(start)) |
| 301 | |
| 302 | locStr := opts.Location |
| 303 | |
| 304 | if ipData != nil { |
| 305 | if autoDetect { |
| 306 | if ipData.City == "" { |
| 307 | locStr = fmt.Sprintf("%s,%s", ipData.Latitude, ipData.Longitude) |
| 308 | } else { |
| 309 | locStr = fmt.Sprintf("%s, %s, %s", ipData.City, ipData.Region, ipData.CountryCode) |
no test coverage detected