Skip to content

Commit e1e1643

Browse files
committed
Generate models based on yaml api definition for withings
1 parent c9762fb commit e1e1643

19 files changed

+318
-355
lines changed

server/pom.xml

+34
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,40 @@
126126
</excludes>
127127
</configuration>
128128
</plugin>
129+
<plugin>
130+
<groupId>org.openapitools</groupId>
131+
<artifactId>openapi-generator-maven-plugin</artifactId>
132+
<version>6.3.0</version>
133+
<executions>
134+
<execution>
135+
<goals>
136+
<goal>generate</goal>
137+
</goals>
138+
<configuration>
139+
<inputSpec>${project.basedir}/src/main/resources/withings.yaml</inputSpec>
140+
<generatorName>java</generatorName>
141+
<configOptions>
142+
<sourceFolder>src/gen/java/main</sourceFolder>
143+
<dateLibrary>java8</dateLibrary>
144+
<java8>true</java8>
145+
<useTags>true</useTags>
146+
<interfaceOnly>true</interfaceOnly>
147+
<library>native</library>
148+
<useJakartaEe>true</useJakartaEe>
149+
<supportUrlQuery>false</supportUrlQuery>
150+
<additionalModelTypeAnnotations>@lombok.Builder @lombok.AllArgsConstructor</additionalModelTypeAnnotations>
151+
</configOptions>
152+
<generateApis>false</generateApis>
153+
<generateApiTests>false</generateApiTests>
154+
<generateModelTests>false</generateModelTests>
155+
<generateModelDocumentation>false</generateModelDocumentation>
156+
<generateSupportingFiles>false</generateSupportingFiles>
157+
<generateApiDocumentation>false</generateApiDocumentation>
158+
<modelPackage>mucsi96.traininglog.withings</modelPackage>
159+
</configuration>
160+
</execution>
161+
</executions>
162+
</plugin>
129163
</plugins>
130164
</build>
131165

server/src/main/java/mucsi96/traininglog/withings/WithingsConfiguration.java

+147
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,163 @@
11
package mucsi96.traininglog.withings;
22

3+
import java.util.Arrays;
4+
import java.util.Map;
5+
import java.util.Set;
6+
37
import org.springframework.boot.context.properties.ConfigurationProperties;
8+
import org.springframework.context.annotation.Bean;
49
import org.springframework.context.annotation.Configuration;
10+
import org.springframework.core.convert.converter.Converter;
11+
import org.springframework.http.converter.FormHttpMessageConverter;
12+
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
13+
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
14+
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
15+
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
16+
import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient;
17+
import org.springframework.security.oauth2.client.endpoint.DefaultRefreshTokenTokenResponseClient;
18+
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
19+
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
20+
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequestEntityConverter;
21+
import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;
22+
import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequestEntityConverter;
23+
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
24+
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
25+
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
26+
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
27+
import org.springframework.security.oauth2.core.OAuth2AccessToken;
28+
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
29+
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
30+
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
31+
import org.springframework.security.web.SecurityFilterChain;
32+
import org.springframework.util.LinkedMultiValueMap;
33+
import org.springframework.util.MultiValueMap;
34+
import org.springframework.web.client.RestTemplate;
35+
36+
import com.fasterxml.jackson.core.type.TypeReference;
37+
import com.fasterxml.jackson.databind.ObjectMapper;
538

39+
import io.github.mucsi96.kubetools.security.KubetoolsSecurityConfigurer;
640
import lombok.Data;
741

842
@Data
943
@Configuration
1044
@ConfigurationProperties(prefix = "withings")
1145
public class WithingsConfiguration {
46+
public static final String registrationId = "withings-client";
47+
1248
private WithingsApiConfiguration api;
1349

50+
@Bean
51+
SecurityFilterChain withingsSecurityFilterChain(
52+
HttpSecurity http,
53+
KubetoolsSecurityConfigurer kubetoolsSecurityConfigurer) throws Exception {
54+
return kubetoolsSecurityConfigurer.configure(http)
55+
.securityMatcher("/withings/**")
56+
.oauth2Client(configurer -> configurer
57+
.authorizationCodeGrant(customizer -> customizer
58+
.accessTokenResponseClient(withingsAccessTokenResponseClient())))
59+
.build();
60+
}
61+
62+
@Bean
63+
OAuth2AuthorizedClientManager withingsAuthorizedClientManager(
64+
ClientRegistrationRepository clientRegistrationRepository,
65+
OAuth2AuthorizedClientRepository authorizedClientRepository) {
66+
67+
DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
68+
clientRegistrationRepository, authorizedClientRepository);
69+
70+
OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
71+
.authorizationCode()
72+
.refreshToken(configurer -> configurer.accessTokenResponseClient(withingsRefreshTokenResponseClient()))
73+
.build();
74+
75+
authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
76+
77+
return authorizedClientManager;
78+
}
79+
80+
OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> withingsAccessTokenResponseClient() {
81+
OAuth2AuthorizationCodeGrantRequestEntityConverter requestEntityConverter = new OAuth2AuthorizationCodeGrantRequestEntityConverter();
82+
requestEntityConverter.addParametersConverter(withingsAccessTokenRequestParametersConverter());
83+
84+
OAuth2AccessTokenResponseHttpMessageConverter accessTokenResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
85+
accessTokenResponseConverter.setAccessTokenResponseConverter(withingsAccessTokenResponseConverter());
86+
87+
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
88+
new FormHttpMessageConverter(),
89+
accessTokenResponseConverter));
90+
91+
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
92+
93+
DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();
94+
accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter);
95+
accessTokenResponseClient.setRestOperations(restTemplate);
96+
return accessTokenResponseClient;
97+
}
98+
99+
OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> withingsRefreshTokenResponseClient() {
100+
OAuth2RefreshTokenGrantRequestEntityConverter requestEntityConverter = new OAuth2RefreshTokenGrantRequestEntityConverter();
101+
requestEntityConverter.addParametersConverter(withingsRefreshTokenRequestParametersConverter());
102+
103+
OAuth2AccessTokenResponseHttpMessageConverter accessTokenResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
104+
accessTokenResponseConverter.setAccessTokenResponseConverter(withingsAccessTokenResponseConverter());
105+
106+
RestTemplate restTemplate = new RestTemplate(Arrays.asList(
107+
new FormHttpMessageConverter(),
108+
accessTokenResponseConverter));
109+
110+
restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
111+
112+
DefaultRefreshTokenTokenResponseClient tokenResponseClient = new DefaultRefreshTokenTokenResponseClient();
113+
tokenResponseClient.setRequestEntityConverter(requestEntityConverter);
114+
tokenResponseClient.setRestOperations(restTemplate);
115+
return tokenResponseClient;
116+
}
117+
118+
Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> withingsAccessTokenRequestParametersConverter() {
119+
return request -> {
120+
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
121+
parameters.add("action", "requesttoken");
122+
parameters.add(OAuth2ParameterNames.CLIENT_ID, request.getClientRegistration().getClientId());
123+
parameters.add(OAuth2ParameterNames.CLIENT_SECRET, request.getClientRegistration().getClientSecret());
124+
return parameters;
125+
};
126+
}
127+
128+
Converter<Map<String, Object>, OAuth2AccessTokenResponse> withingsAccessTokenResponseConverter() {
129+
return rawResponse -> {
130+
ObjectMapper mapper = new ObjectMapper();
131+
WithingsGetAccessTokenResponse response = mapper.convertValue(rawResponse, new TypeReference<>() {
132+
});
133+
134+
if (response.getStatus() != 0) {
135+
throw new WithingsTechnicalException();
136+
}
137+
138+
WithingsGetAccessTokenResponseBody body = response.getBody();
139+
140+
return OAuth2AccessTokenResponse
141+
.withToken(body.getAccessToken())
142+
.refreshToken(body.getRefreshToken())
143+
.expiresIn(body.getExpiresIn())
144+
.scopes(Set.of(body.getScope()))
145+
.tokenType(OAuth2AccessToken.TokenType.BEARER)
146+
.additionalParameters(Map.of("userId", body.getUserid()))
147+
.build();
148+
};
149+
}
150+
151+
Converter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>> withingsRefreshTokenRequestParametersConverter() {
152+
return request -> {
153+
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
154+
parameters.add("action", "requesttoken");
155+
parameters.add(OAuth2ParameterNames.CLIENT_ID, request.getClientRegistration().getClientId());
156+
parameters.add(OAuth2ParameterNames.CLIENT_SECRET, request.getClientRegistration().getClientSecret());
157+
return parameters;
158+
};
159+
}
160+
14161
@Data
15162
public static class WithingsApiConfiguration {
16163
private String uri;

server/src/main/java/mucsi96/traininglog/withings/WithingsController.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
import jakarta.servlet.http.HttpServletResponse;
1717
import lombok.RequiredArgsConstructor;
1818
import mucsi96.traininglog.weight.WeightService;
19-
import mucsi96.traininglog.withings.oauth.WithingsClient;
2019

2120
@RestController
2221
@RequestMapping("/withings")
@@ -57,7 +56,7 @@ public RedirectView authorize(
5756
private OAuth2AuthorizedClient getAuthorizedClient(Authentication principal, HttpServletRequest servletRequest,
5857
HttpServletResponse servletResponse) {
5958
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
60-
.withClientRegistrationId(WithingsClient.id)
59+
.withClientRegistrationId(WithingsConfiguration.registrationId)
6160
.principal(principal)
6261
.attribute(HttpServletRequest.class.getName(), servletRequest)
6362
.attribute(HttpServletResponse.class.getName(), servletResponse)

server/src/main/java/mucsi96/traininglog/withings/WithingsService.java

+9-14
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,6 @@
1818

1919
import lombok.RequiredArgsConstructor;
2020
import mucsi96.traininglog.weight.Weight;
21-
import mucsi96.traininglog.withings.data.GetMeasureResponse;
22-
import mucsi96.traininglog.withings.data.GetMeasureResponseBody;
23-
import mucsi96.traininglog.withings.data.Measure;
24-
import mucsi96.traininglog.withings.data.MeasureGroup;
25-
import mucsi96.traininglog.withings.oauth.WithingsClient;
2621

2722
@Service
2823
@RequiredArgsConstructor
@@ -46,20 +41,20 @@ private String getMeasureUrl() {
4641
.toUriString();
4742
}
4843

49-
private GetMeasureResponseBody getMeasure(OAuth2AuthorizedClient authorizedClient) {
44+
private WithingsGetMeasureResponseBody getMeasure(OAuth2AuthorizedClient authorizedClient) {
5045
HttpHeaders headers = new HttpHeaders();
5146
headers.setBearerAuth(authorizedClient.getAccessToken().getTokenValue());
5247
HttpEntity<String> request = new HttpEntity<>("", headers);
5348
RestTemplate restTemplate = new RestTemplate();
54-
GetMeasureResponse response = restTemplate
55-
.postForObject(getMeasureUrl(), request, GetMeasureResponse.class);
49+
WithingsGetMeasureResponse response = restTemplate
50+
.postForObject(getMeasureUrl(), request, WithingsGetMeasureResponse.class);
5651

5752
if (response == null) {
5853
throw new WithingsTechnicalException();
5954
}
6055

6156
if (response.getStatus() == 401) {
62-
throw new ClientAuthorizationRequiredException(WithingsClient.id);
57+
throw new ClientAuthorizationRequiredException(WithingsConfiguration.registrationId);
6358
}
6459

6560
if (response.getStatus() != 0) {
@@ -69,22 +64,22 @@ private GetMeasureResponseBody getMeasure(OAuth2AuthorizedClient authorizedClien
6964
return response.getBody();
7065
}
7166

72-
private Optional<Weight> getFirstMeasureValue(GetMeasureResponseBody measureResponseBody) {
73-
List<MeasureGroup> measureGroups = measureResponseBody.getMeasureGroups();
67+
private Optional<Weight> getFirstMeasureValue(WithingsGetMeasureResponseBody measureResponseBody) {
68+
List<WithingsMeasureGroup> measureGroups = measureResponseBody.getMeasuregrps();
7469

7570
if (measureGroups == null || measureGroups.isEmpty()) {
7671
return Optional.empty();
7772
}
7873

79-
MeasureGroup measureGroup = measureGroups.get(0);
74+
WithingsMeasureGroup measureGroup = measureGroups.get(0);
8075

81-
List<Measure> measures = measureGroup.getMeasures();
76+
List<WithingsMeasure> measures = measureGroup.getMeasures();
8277

8378
if (measures == null || measures.isEmpty()) {
8479
return Optional.empty();
8580
}
8681

87-
Measure measure = measures.get(0);
82+
WithingsMeasure measure = measures.get(0);
8883
double weight = measure.getValue() * Math.pow(10, measure.getUnit());
8984

9085
return Optional.of(Weight.builder().value(weight).createdAt(Instant.ofEpochSecond(measureGroup.getDate())).build());

server/src/main/java/mucsi96/traininglog/withings/data/GetAccessTokenResponseBody.java

-30
This file was deleted.

server/src/main/java/mucsi96/traininglog/withings/data/GetMeasureResponse.java

-9
This file was deleted.

server/src/main/java/mucsi96/traininglog/withings/data/GetMeasureResponseBody.java

-17
This file was deleted.

server/src/main/java/mucsi96/traininglog/withings/data/Measure.java

-12
This file was deleted.

server/src/main/java/mucsi96/traininglog/withings/data/MeasureGroup.java

-22
This file was deleted.

server/src/main/java/mucsi96/traininglog/withings/data/WithingsResponse.java

-12
This file was deleted.

0 commit comments

Comments
 (0)