Skip to content

Commit

Permalink
Generate models based on yaml api definition for withings
Browse files Browse the repository at this point in the history
  • Loading branch information
mucsi96 committed Jul 18, 2023
1 parent c9762fb commit e1e1643
Show file tree
Hide file tree
Showing 19 changed files with 318 additions and 355 deletions.
34 changes: 34 additions & 0 deletions server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,40 @@
</excludes>
</configuration>
</plugin>
<plugin>
<groupId>org.openapitools</groupId>
<artifactId>openapi-generator-maven-plugin</artifactId>
<version>6.3.0</version>
<executions>
<execution>
<goals>
<goal>generate</goal>
</goals>
<configuration>
<inputSpec>${project.basedir}/src/main/resources/withings.yaml</inputSpec>
<generatorName>java</generatorName>
<configOptions>
<sourceFolder>src/gen/java/main</sourceFolder>
<dateLibrary>java8</dateLibrary>
<java8>true</java8>
<useTags>true</useTags>
<interfaceOnly>true</interfaceOnly>
<library>native</library>
<useJakartaEe>true</useJakartaEe>
<supportUrlQuery>false</supportUrlQuery>
<additionalModelTypeAnnotations>@lombok.Builder @lombok.AllArgsConstructor</additionalModelTypeAnnotations>
</configOptions>
<generateApis>false</generateApis>
<generateApiTests>false</generateApiTests>
<generateModelTests>false</generateModelTests>
<generateModelDocumentation>false</generateModelDocumentation>
<generateSupportingFiles>false</generateSupportingFiles>
<generateApiDocumentation>false</generateApiDocumentation>
<modelPackage>mucsi96.traininglog.withings</modelPackage>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,163 @@
package mucsi96.traininglog.withings;

import java.util.Arrays;
import java.util.Map;
import java.util.Set;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.http.converter.FormHttpMessageConverter;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider;
import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder;
import org.springframework.security.oauth2.client.endpoint.DefaultAuthorizationCodeTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.DefaultRefreshTokenTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AccessTokenResponseClient;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequest;
import org.springframework.security.oauth2.client.endpoint.OAuth2AuthorizationCodeGrantRequestEntityConverter;
import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequest;
import org.springframework.security.oauth2.client.endpoint.OAuth2RefreshTokenGrantRequestEntityConverter;
import org.springframework.security.oauth2.client.http.OAuth2ErrorResponseErrorHandler;
import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository;
import org.springframework.security.oauth2.client.web.DefaultOAuth2AuthorizedClientManager;
import org.springframework.security.oauth2.client.web.OAuth2AuthorizedClientRepository;
import org.springframework.security.oauth2.core.OAuth2AccessToken;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.http.converter.OAuth2AccessTokenResponseHttpMessageConverter;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import io.github.mucsi96.kubetools.security.KubetoolsSecurityConfigurer;
import lombok.Data;

@Data
@Configuration
@ConfigurationProperties(prefix = "withings")
public class WithingsConfiguration {
public static final String registrationId = "withings-client";

private WithingsApiConfiguration api;

@Bean
SecurityFilterChain withingsSecurityFilterChain(
HttpSecurity http,
KubetoolsSecurityConfigurer kubetoolsSecurityConfigurer) throws Exception {
return kubetoolsSecurityConfigurer.configure(http)
.securityMatcher("/withings/**")
.oauth2Client(configurer -> configurer
.authorizationCodeGrant(customizer -> customizer
.accessTokenResponseClient(withingsAccessTokenResponseClient())))
.build();
}

@Bean
OAuth2AuthorizedClientManager withingsAuthorizedClientManager(
ClientRegistrationRepository clientRegistrationRepository,
OAuth2AuthorizedClientRepository authorizedClientRepository) {

DefaultOAuth2AuthorizedClientManager authorizedClientManager = new DefaultOAuth2AuthorizedClientManager(
clientRegistrationRepository, authorizedClientRepository);

OAuth2AuthorizedClientProvider authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
.authorizationCode()
.refreshToken(configurer -> configurer.accessTokenResponseClient(withingsRefreshTokenResponseClient()))
.build();

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

return authorizedClientManager;
}

OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> withingsAccessTokenResponseClient() {
OAuth2AuthorizationCodeGrantRequestEntityConverter requestEntityConverter = new OAuth2AuthorizationCodeGrantRequestEntityConverter();
requestEntityConverter.addParametersConverter(withingsAccessTokenRequestParametersConverter());

OAuth2AccessTokenResponseHttpMessageConverter accessTokenResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
accessTokenResponseConverter.setAccessTokenResponseConverter(withingsAccessTokenResponseConverter());

RestTemplate restTemplate = new RestTemplate(Arrays.asList(
new FormHttpMessageConverter(),
accessTokenResponseConverter));

restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());

DefaultAuthorizationCodeTokenResponseClient accessTokenResponseClient = new DefaultAuthorizationCodeTokenResponseClient();
accessTokenResponseClient.setRequestEntityConverter(requestEntityConverter);
accessTokenResponseClient.setRestOperations(restTemplate);
return accessTokenResponseClient;
}

OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> withingsRefreshTokenResponseClient() {
OAuth2RefreshTokenGrantRequestEntityConverter requestEntityConverter = new OAuth2RefreshTokenGrantRequestEntityConverter();
requestEntityConverter.addParametersConverter(withingsRefreshTokenRequestParametersConverter());

OAuth2AccessTokenResponseHttpMessageConverter accessTokenResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter();
accessTokenResponseConverter.setAccessTokenResponseConverter(withingsAccessTokenResponseConverter());

RestTemplate restTemplate = new RestTemplate(Arrays.asList(
new FormHttpMessageConverter(),
accessTokenResponseConverter));

restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());

DefaultRefreshTokenTokenResponseClient tokenResponseClient = new DefaultRefreshTokenTokenResponseClient();
tokenResponseClient.setRequestEntityConverter(requestEntityConverter);
tokenResponseClient.setRestOperations(restTemplate);
return tokenResponseClient;
}

Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> withingsAccessTokenRequestParametersConverter() {
return request -> {
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.add("action", "requesttoken");
parameters.add(OAuth2ParameterNames.CLIENT_ID, request.getClientRegistration().getClientId());
parameters.add(OAuth2ParameterNames.CLIENT_SECRET, request.getClientRegistration().getClientSecret());
return parameters;
};
}

Converter<Map<String, Object>, OAuth2AccessTokenResponse> withingsAccessTokenResponseConverter() {
return rawResponse -> {
ObjectMapper mapper = new ObjectMapper();
WithingsGetAccessTokenResponse response = mapper.convertValue(rawResponse, new TypeReference<>() {
});

if (response.getStatus() != 0) {
throw new WithingsTechnicalException();
}

WithingsGetAccessTokenResponseBody body = response.getBody();

return OAuth2AccessTokenResponse
.withToken(body.getAccessToken())
.refreshToken(body.getRefreshToken())
.expiresIn(body.getExpiresIn())
.scopes(Set.of(body.getScope()))
.tokenType(OAuth2AccessToken.TokenType.BEARER)
.additionalParameters(Map.of("userId", body.getUserid()))
.build();
};
}

Converter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>> withingsRefreshTokenRequestParametersConverter() {
return request -> {
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.add("action", "requesttoken");
parameters.add(OAuth2ParameterNames.CLIENT_ID, request.getClientRegistration().getClientId());
parameters.add(OAuth2ParameterNames.CLIENT_SECRET, request.getClientRegistration().getClientSecret());
return parameters;
};
}

@Data
public static class WithingsApiConfiguration {
private String uri;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import mucsi96.traininglog.weight.WeightService;
import mucsi96.traininglog.withings.oauth.WithingsClient;

@RestController
@RequestMapping("/withings")
Expand Down Expand Up @@ -57,7 +56,7 @@ public RedirectView authorize(
private OAuth2AuthorizedClient getAuthorizedClient(Authentication principal, HttpServletRequest servletRequest,
HttpServletResponse servletResponse) {
OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest
.withClientRegistrationId(WithingsClient.id)
.withClientRegistrationId(WithingsConfiguration.registrationId)
.principal(principal)
.attribute(HttpServletRequest.class.getName(), servletRequest)
.attribute(HttpServletResponse.class.getName(), servletResponse)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@

import lombok.RequiredArgsConstructor;
import mucsi96.traininglog.weight.Weight;
import mucsi96.traininglog.withings.data.GetMeasureResponse;
import mucsi96.traininglog.withings.data.GetMeasureResponseBody;
import mucsi96.traininglog.withings.data.Measure;
import mucsi96.traininglog.withings.data.MeasureGroup;
import mucsi96.traininglog.withings.oauth.WithingsClient;

@Service
@RequiredArgsConstructor
Expand All @@ -46,20 +41,20 @@ private String getMeasureUrl() {
.toUriString();
}

private GetMeasureResponseBody getMeasure(OAuth2AuthorizedClient authorizedClient) {
private WithingsGetMeasureResponseBody getMeasure(OAuth2AuthorizedClient authorizedClient) {
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(authorizedClient.getAccessToken().getTokenValue());
HttpEntity<String> request = new HttpEntity<>("", headers);
RestTemplate restTemplate = new RestTemplate();
GetMeasureResponse response = restTemplate
.postForObject(getMeasureUrl(), request, GetMeasureResponse.class);
WithingsGetMeasureResponse response = restTemplate
.postForObject(getMeasureUrl(), request, WithingsGetMeasureResponse.class);

if (response == null) {
throw new WithingsTechnicalException();
}

if (response.getStatus() == 401) {
throw new ClientAuthorizationRequiredException(WithingsClient.id);
throw new ClientAuthorizationRequiredException(WithingsConfiguration.registrationId);
}

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

private Optional<Weight> getFirstMeasureValue(GetMeasureResponseBody measureResponseBody) {
List<MeasureGroup> measureGroups = measureResponseBody.getMeasureGroups();
private Optional<Weight> getFirstMeasureValue(WithingsGetMeasureResponseBody measureResponseBody) {
List<WithingsMeasureGroup> measureGroups = measureResponseBody.getMeasuregrps();

if (measureGroups == null || measureGroups.isEmpty()) {
return Optional.empty();
}

MeasureGroup measureGroup = measureGroups.get(0);
WithingsMeasureGroup measureGroup = measureGroups.get(0);

List<Measure> measures = measureGroup.getMeasures();
List<WithingsMeasure> measures = measureGroup.getMeasures();

if (measures == null || measures.isEmpty()) {
return Optional.empty();
}

Measure measure = measures.get(0);
WithingsMeasure measure = measures.get(0);
double weight = measure.getValue() * Math.pow(10, measure.getUnit());

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

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading

0 comments on commit e1e1643

Please sign in to comment.