├── src
├── main
│ ├── resources
│ │ ├── banner.jpg
│ │ ├── static
│ │ │ ├── favicon.ico
│ │ │ ├── img
│ │ │ │ ├── background.jpg
│ │ │ │ └── twitter-logo.png
│ │ │ ├── fonts
│ │ │ │ ├── owfont-regular.eot
│ │ │ │ ├── owfont-regular.otf
│ │ │ │ ├── owfont-regular.ttf
│ │ │ │ ├── owfont-regular.woff
│ │ │ │ ├── SpringOnePlatform.eot
│ │ │ │ ├── SpringOnePlatform.ttf
│ │ │ │ ├── SpringOnePlatform.woff
│ │ │ │ ├── SpringOnePlatform.svg
│ │ │ │ └── owfont-regular.svg
│ │ │ ├── error
│ │ │ │ └── 404.html
│ │ │ └── css
│ │ │ │ ├── style.css
│ │ │ │ ├── owfont-regular.min.css
│ │ │ │ └── owfont-regular.css
│ │ ├── application.properties
│ │ └── templates
│ │ │ └── summary.html
│ ├── java
│ │ └── com
│ │ │ └── example
│ │ │ └── weather
│ │ │ ├── integration
│ │ │ └── ows
│ │ │ │ ├── Weather.java
│ │ │ │ ├── WeatherForecast.java
│ │ │ │ ├── WeatherEntry.java
│ │ │ │ └── WeatherService.java
│ │ │ ├── WeatherApp.java
│ │ │ ├── web
│ │ │ ├── WeatherApiController.java
│ │ │ ├── WeatherSummary.java
│ │ │ └── WeatherSummaryController.java
│ │ │ └── WeatherAppProperties.java
│ └── asciidoc
│ │ └── index.adoc
└── test
│ ├── resources
│ ├── logback-test.xml
│ └── com
│ │ └── example
│ │ └── weather
│ │ └── integration
│ │ └── ows
│ │ ├── weather-barcelona.json
│ │ └── forecast-barcelona.json
│ └── java
│ └── com
│ └── example
│ └── weather
│ ├── integration
│ └── ows
│ │ └── WeatherServiceTest.java
│ └── web
│ ├── WeatherApiControllerTest.java
│ └── WeatherApiDocumentationTest.java
├── .gitignore
├── README.adoc
├── pom.xml
└── LICENSE
/src/main/resources/banner.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpringOnePlatform2016/weather-app/HEAD/src/main/resources/banner.jpg
--------------------------------------------------------------------------------
/src/main/resources/static/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpringOnePlatform2016/weather-app/HEAD/src/main/resources/static/favicon.ico
--------------------------------------------------------------------------------
/src/main/resources/static/img/background.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpringOnePlatform2016/weather-app/HEAD/src/main/resources/static/img/background.jpg
--------------------------------------------------------------------------------
/src/main/resources/static/img/twitter-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpringOnePlatform2016/weather-app/HEAD/src/main/resources/static/img/twitter-logo.png
--------------------------------------------------------------------------------
/src/main/resources/static/fonts/owfont-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpringOnePlatform2016/weather-app/HEAD/src/main/resources/static/fonts/owfont-regular.eot
--------------------------------------------------------------------------------
/src/main/resources/static/fonts/owfont-regular.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpringOnePlatform2016/weather-app/HEAD/src/main/resources/static/fonts/owfont-regular.otf
--------------------------------------------------------------------------------
/src/main/resources/static/fonts/owfont-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpringOnePlatform2016/weather-app/HEAD/src/main/resources/static/fonts/owfont-regular.ttf
--------------------------------------------------------------------------------
/src/main/resources/static/fonts/owfont-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpringOnePlatform2016/weather-app/HEAD/src/main/resources/static/fonts/owfont-regular.woff
--------------------------------------------------------------------------------
/src/main/resources/static/fonts/SpringOnePlatform.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpringOnePlatform2016/weather-app/HEAD/src/main/resources/static/fonts/SpringOnePlatform.eot
--------------------------------------------------------------------------------
/src/main/resources/static/fonts/SpringOnePlatform.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpringOnePlatform2016/weather-app/HEAD/src/main/resources/static/fonts/SpringOnePlatform.ttf
--------------------------------------------------------------------------------
/src/main/resources/static/fonts/SpringOnePlatform.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/SpringOnePlatform2016/weather-app/HEAD/src/main/resources/static/fonts/SpringOnePlatform.woff
--------------------------------------------------------------------------------
/src/test/resources/logback-test.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *.iml
3 | *.ipr
4 | *.iws
5 |
6 | .classpath
7 | .factorypath
8 | .project
9 | .settings
10 |
11 | target
12 |
13 | src/main/resources/application-secrets.properties
--------------------------------------------------------------------------------
/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | spring.profiles.active=secrets
2 |
3 | spring.jackson.serialization.write_dates_as_timestamps=false
4 |
5 | app.weather.locations=Belgium/Brussels,USA/Las Vegas
6 |
7 | spring.cache.caffeine.spec=recordStats,maximumSize=500,expireAfterWrite=600s
--------------------------------------------------------------------------------
/src/main/java/com/example/weather/integration/ows/Weather.java:
--------------------------------------------------------------------------------
1 | package com.example.weather.integration.ows;
2 |
3 | public class Weather extends WeatherEntry {
4 |
5 | private String name;
6 |
7 | public String getName() {
8 | return this.name;
9 | }
10 |
11 | public void setName(String name) {
12 | this.name = name;
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/resources/static/error/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Ooops, page not found
6 |
16 |
17 |
18 |
19 |
20 |
¯\_(ツ)_/¯
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/src/main/java/com/example/weather/WeatherApp.java:
--------------------------------------------------------------------------------
1 | package com.example.weather;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 | import org.springframework.boot.context.properties.EnableConfigurationProperties;
6 | import org.springframework.cache.annotation.EnableCaching;
7 |
8 | @SpringBootApplication
9 | @EnableConfigurationProperties(WeatherAppProperties.class)
10 | @EnableCaching(proxyTargetClass = true)
11 | public class WeatherApp {
12 |
13 | public static void main(String[] args) {
14 | SpringApplication.run(WeatherApp.class, args);
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/resources/templates/summary.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Weather Summary
8 |
9 |
10 |
11 | Weather Summary
12 |
13 | {{#summary}}
14 | -
15 |
{{city}}, {{country}}
16 |
17 |
18 |
{{fahrenheitTemperature}}°F / {{celsiusTemperature}}°C
19 |
20 |
21 |
22 | {{/summary}}
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/README.adoc:
--------------------------------------------------------------------------------
1 | = Simple weather app
2 |
3 | == Getting started
4 |
5 | To use this app, you need to register an http://openweathermap.org/appid[API Key] on the
6 | http://openweathermap.org/[OpenWeatherApp] service.
7 |
8 | You can create an `application-secrets.properties` in `src/main/resources` and add your
9 | API key there:
10 |
11 | ```
12 | app.weather.api.key=
13 | ```
14 |
15 | TIP: The `application-secrets.properties` file is referenced in the `.gitignore` of the
16 | project so you won't commit it by mistake. The `secrets` profile is enabled by default
17 | via the `spring.profiles.active` in the main configuration.
18 |
19 | If you don't like specifying the key in the project directly, you can set an OS
20 | environment property instead, something like:
21 |
22 | ```
23 | $ EXPORT APP_WEATHER_API_KEY=
24 | ```
25 |
26 |
--------------------------------------------------------------------------------
/src/test/resources/com/example/weather/integration/ows/weather-barcelona.json:
--------------------------------------------------------------------------------
1 | {
2 | "coord": {
3 | "lon": 2.16,
4 | "lat": 41.39
5 | },
6 | "weather": [
7 | {
8 | "id": 800,
9 | "main": "Clear",
10 | "description": "clear sky",
11 | "icon": "01d"
12 | }
13 | ],
14 | "base": "cmc stations",
15 | "main": {
16 | "temp": 286.72,
17 | "pressure": 1012,
18 | "humidity": 62,
19 | "temp_min": 282.59,
20 | "temp_max": 290.37
21 | },
22 | "wind": {
23 | "speed": 5.1,
24 | "deg": 310
25 | },
26 | "clouds": {
27 | "all": 0
28 | },
29 | "dt": 1461484642,
30 | "sys": {
31 | "type": 1,
32 | "id": 5470,
33 | "message": 0.0105,
34 | "country": "ES",
35 | "sunrise": 1461473841,
36 | "sunset": 1461523325
37 | },
38 | "id": 6544100,
39 | "name": "Barcelona",
40 | "cod": 200
41 | }
--------------------------------------------------------------------------------
/src/main/java/com/example/weather/integration/ows/WeatherForecast.java:
--------------------------------------------------------------------------------
1 | package com.example.weather.integration.ows;
2 |
3 | import java.io.Serializable;
4 | import java.util.ArrayList;
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | import com.fasterxml.jackson.annotation.JsonProperty;
9 | import com.fasterxml.jackson.annotation.JsonSetter;
10 |
11 | public class WeatherForecast implements Serializable {
12 |
13 | private String name;
14 |
15 | private List entries = new ArrayList<>();
16 |
17 | public String getName() {
18 | return this.name;
19 | }
20 |
21 | public void setName(String name) {
22 | this.name = name;
23 | }
24 |
25 | @JsonProperty("entries")
26 | public List getEntries() {
27 | return this.entries;
28 | }
29 |
30 | @JsonSetter("list")
31 | public void setEntries(List entries) {
32 | this.entries = entries;
33 | }
34 |
35 | @JsonProperty("city")
36 | public void setCity(Map city) {
37 | setName(city.get("name").toString());
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/example/weather/web/WeatherApiController.java:
--------------------------------------------------------------------------------
1 | package com.example.weather.web;
2 |
3 | import com.example.weather.integration.ows.Weather;
4 | import com.example.weather.integration.ows.WeatherForecast;
5 | import com.example.weather.integration.ows.WeatherService;
6 |
7 | import org.springframework.web.bind.annotation.PathVariable;
8 | import org.springframework.web.bind.annotation.RequestMapping;
9 | import org.springframework.web.bind.annotation.RestController;
10 |
11 | @RestController
12 | @RequestMapping("/api/weather")
13 | public class WeatherApiController {
14 |
15 | private final WeatherService weatherService;
16 |
17 | public WeatherApiController(WeatherService weatherService) {
18 | this.weatherService = weatherService;
19 | }
20 |
21 | @RequestMapping("/now/{country}/{city}")
22 | public Weather getWeather(@PathVariable String country,
23 | @PathVariable String city) {
24 | return this.weatherService.getWeather(country, city);
25 | }
26 |
27 | @RequestMapping("/weekly/{country}/{city}")
28 | public WeatherForecast getWeatherForecast(@PathVariable String country,
29 | @PathVariable String city) {
30 | return this.weatherService.getWeatherForecast(country, city);
31 | }
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/example/weather/WeatherAppProperties.java:
--------------------------------------------------------------------------------
1 | package com.example.weather;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 | import javax.validation.Valid;
6 | import javax.validation.constraints.NotNull;
7 |
8 | import org.springframework.boot.context.properties.ConfigurationProperties;
9 |
10 | @ConfigurationProperties("app.weather")
11 | public class WeatherAppProperties {
12 |
13 | @Valid
14 | private final Api api = new Api();
15 |
16 | /**
17 | * Comma-separated list of locations to display. Each entry should have the
18 | * form "Country/City".
19 | */
20 | private List locations = Arrays.asList("UK/London", "Russia/Moscow");
21 |
22 | public Api getApi() {
23 | return this.api;
24 | }
25 |
26 | public List getLocations() {
27 | return this.locations;
28 | }
29 |
30 | public void setLocations(List locations) {
31 | this.locations = locations;
32 | }
33 |
34 | public static class Api {
35 |
36 | /**
37 | * API key of the OpenWeatherMap service.
38 | */
39 | @NotNull
40 | private String key;
41 |
42 | public String getKey() {
43 | return this.key;
44 | }
45 |
46 | public void setKey(String key) {
47 | this.key = key;
48 | }
49 |
50 | }
51 |
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/com/example/weather/web/WeatherSummary.java:
--------------------------------------------------------------------------------
1 | package com.example.weather.web;
2 |
3 | import com.example.weather.integration.ows.Weather;
4 |
5 | class WeatherSummary {
6 |
7 | private final String country;
8 |
9 | private final String city;
10 |
11 | private final Integer code;
12 |
13 | private final String icon;
14 |
15 | private final double temperature;
16 |
17 | WeatherSummary(String country, String city, Weather weather) {
18 | this.country = country;
19 | this.city = city;
20 | this.code = weather.getWeatherId();
21 | this.icon = weather.getWeatherIcon();
22 | this.temperature = weather.getTemperature();
23 | }
24 |
25 | public String getCountry() {
26 | return this.country;
27 | }
28 |
29 | public String getCity() {
30 | return this.city;
31 | }
32 |
33 | public Integer getCode() {
34 | return this.code;
35 | }
36 |
37 | public String getIcon() {
38 | return this.icon;
39 | }
40 |
41 | public String getFahrenheitTemperature() {
42 | double fahrenheitTemp = (this.temperature * 1.8) - 459.67;
43 | return String.format("%4.2f", fahrenheitTemp);
44 | }
45 |
46 | public String getCelsiusTemperature() {
47 | double celsiusTemp = this.temperature - 273.15;
48 | return String.format("%4.2f", celsiusTemp);
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/com/example/weather/integration/ows/WeatherEntry.java:
--------------------------------------------------------------------------------
1 | package com.example.weather.integration.ows;
2 |
3 | import java.io.Serializable;
4 | import java.time.Instant;
5 | import java.util.List;
6 | import java.util.Map;
7 |
8 | import com.fasterxml.jackson.annotation.JsonProperty;
9 | import com.fasterxml.jackson.annotation.JsonSetter;
10 |
11 | public class WeatherEntry implements Serializable {
12 |
13 | private Instant timestamp;
14 |
15 | private double temperature;
16 |
17 | private Integer weatherId;
18 |
19 | private String weatherIcon;
20 |
21 | @JsonProperty("timestamp")
22 | public Instant getTimestamp() {
23 | return this.timestamp;
24 | }
25 |
26 | @JsonSetter("dt")
27 | public void setTimestamp(long unixTime) {
28 | this.timestamp = Instant.ofEpochMilli(unixTime * 1000);
29 | }
30 |
31 | /**
32 | * Return the temperature in Kelvin (K).
33 | */
34 | public double getTemperature() {
35 | return this.temperature;
36 | }
37 |
38 | public void setTemperature(double temperature) {
39 | this.temperature = temperature;
40 | }
41 |
42 | @JsonProperty("main")
43 | public void setMain(Map main) {
44 | setTemperature(Double.parseDouble(main.get("temp").toString()));
45 | }
46 |
47 | public Integer getWeatherId() {
48 | return this.weatherId;
49 | }
50 |
51 | public void setWeatherId(Integer weatherId) {
52 | this.weatherId = weatherId;
53 | }
54 |
55 | public String getWeatherIcon() {
56 | return this.weatherIcon;
57 | }
58 |
59 | public void setWeatherIcon(String weatherIcon) {
60 | this.weatherIcon = weatherIcon;
61 | }
62 |
63 | @JsonProperty("weather")
64 | public void setWeather(List