Coverage Summary for Class: WeatherDataImporter (dev.karlkadak.backend.service)

Class Method, % Branch, % Line, %
WeatherDataImporter 100% (4/4) 100% (4/4) 100% (46/46)
WeatherDataImporter$1 100% (1/1) 100% (1/1)
Total 100% (5/5) 100% (4/4) 100% (47/47)


 package dev.karlkadak.backend.service;
 
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.core.type.TypeReference;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import dev.karlkadak.backend.entity.City;
 import dev.karlkadak.backend.entity.WeatherData;
 import dev.karlkadak.backend.exception.FailedWeatherDataFetchException;
 import dev.karlkadak.backend.repository.CityRepository;
 import dev.karlkadak.backend.repository.WeatherDataRepository;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 import org.springframework.web.client.RestTemplate;
 
 import java.util.List;
 import java.util.Map;
 import java.util.logging.Logger;
 
 /**
  * Used for importing weather data to the database from <a href="https://openweathermap.org/api">OpenWeather API</a><br>
  * Also logs the results of imports using {@link java.util.logging.Logger}
  */
 @Service
 public class WeatherDataImporter {
 
     private final WeatherDataRepository weatherDataRepository;
     private final CityRepository cityRepository;
     private final Logger logger;
     private final RestTemplate restTemplate;
     private final ObjectMapper objectMapper;
 
     /**
      * API key gathered from application.properties which is used for accessing the
      * <a href="https://openweathermap.org/api">OpenWeather API</a>
      */
     @Value("${openweather.api.key}")
     private String apiKey;
 
     @Autowired
     public WeatherDataImporter(WeatherDataRepository weatherDataRepository, CityRepository cityRepository,
                                Logger logger, RestTemplate restTemplate, ObjectMapper objectMapper) {
         this.weatherDataRepository = weatherDataRepository;
         this.cityRepository = cityRepository;
         this.logger = logger;
         this.restTemplate = restTemplate;
         this.objectMapper = objectMapper;
     }
 
     /**
      * Fetches the weather data of all {@link dev.karlkadak.backend.entity.City City} objects which have
      * {@link dev.karlkadak.backend.entity.City#importingData importingData} set as {@link java.lang.Boolean#TRUE TRUE}
      * and saves it to the database
      */
     public void defaultImport() {
         final List<City> citiesToFetch = cityRepository.findAllByImportingDataTrue();
         if (citiesToFetch.isEmpty()) return;
         Integer fetchedCityCount = 0;
 
         logger.info(String.format("Starting to fetch weather data for %d cities.", citiesToFetch.size()));
 
         for (City city : citiesToFetch) {
             try {
                 fetchAndSave(city);
                 fetchedCityCount++;
             } catch (FailedWeatherDataFetchException e) {
                 logger.warning(String.format("Failed fetching weather data for city \"%s\". Reason: %s", city.getName(),
                                              e.getMessage()));
             }
 
             logger.info(String.format("Fetched weather data for city \"%s\".", city.getName()));
         }
 
         logger.info(String.format("Saved weather data for %d cities.", fetchedCityCount));
     }
 
     /**
      * Fetches the weather data of the specified {@link dev.karlkadak.backend.entity.City City} and saves it to the
      * database
      *
      * @param city {@link dev.karlkadak.backend.entity.City City} to fetch weather data about
      */
     protected void fetchAndSave(City city) {
         WeatherData fetchedData;
 
         // Attempt requesting the weather data, throw a
         try {
             fetchedData = requestData(city);
         } catch (Exception e) {
             throw new FailedWeatherDataFetchException(e.getMessage());
         }
 
         // Save the fetched data to the database
         weatherDataRepository.save(fetchedData);
     }
 
     /**
      * Requests and returns the weather data of the specified city
      *
      * @param city {@link dev.karlkadak.backend.entity.City City} to fetch weather data about
      * @return {@link dev.karlkadak.backend.entity.WeatherData} about the specified
      * {@link dev.karlkadak.backend.entity.City City}
      * @throws JsonProcessingException in case of API response processing error
      */
     private WeatherData requestData(City city)
             throws JsonProcessingException {
         // Initialize datapoint variables
         final long timestamp;
         Double airTemperature = null;
         Double windSpeed = null;
         Integer humidity = null;
         String iconCode = null;
 
         // Variables needed for performing the API request
         Double latitude = city.getCoordinatePair().getLatitude();
         Double longitude = city.getCoordinatePair().getLongitude();
         String requestUrl = String.format(
                 "https://api.openweathermap.org/data/2.5/weather?units=metric&lat=%f&lon=%f&appid=%s", latitude,
                 longitude, apiKey);
         Map<String, Object> responseMap;
 
         // Process the API response
         String jsonResponse = restTemplate.getForObject(requestUrl, String.class);
         responseMap = objectMapper.readValue(jsonResponse, new TypeReference<>() {
         });
         timestamp = ((Integer) responseMap.get("dt")).longValue();
         try {
             List<Map<String, Object>> weatherList = (List<Map<String, Object>>) responseMap.get("weather");
             Map<String, Object> weatherObject = weatherList.get(0);
             iconCode = (String) weatherObject.get("icon");
         } catch (Exception _) {
         }
         try {
             Map<String, Object> mainObject = (Map<String, Object>) responseMap.get("main");
             Map<String, Object> mainData = (Map<String, Object>) mainObject;
             airTemperature = (Double) mainData.get("temp");
             humidity = (Integer) mainData.get("humidity");
         } catch (Exception _) {
         }
         try {
             Map<String, Object> windObject = (Map<String, Object>) responseMap.get("wind");
             Map<String, Object> windData = (Map<String, Object>) windObject;
             windSpeed = (Double) windData.get("speed");
         } catch (Exception _) {
         }
 
         return new WeatherData(city, timestamp, airTemperature, windSpeed, humidity, iconCode);
     }
 }