Improve tests
This commit is contained in:
1
.idea/compiler.xml
generated
1
.idea/compiler.xml
generated
@@ -59,6 +59,7 @@
|
||||
<entry name="$USER_HOME$/.m2/peak-repo/org/antlr/antlr4-runtime/4.13.2/antlr4-runtime-4.13.2.jar" />
|
||||
<entry name="$USER_HOME$/.m2/peak-repo/net/bytebuddy/byte-buddy/1.17.8/byte-buddy-1.17.8.jar" />
|
||||
<entry name="$USER_HOME$/.m2/peak-repo/org/jboss/logging/jboss-logging/3.6.1.Final/jboss-logging-3.6.1.Final.jar" />
|
||||
<entry name="$USER_HOME$/.m2/peak-repo/org/mapstruct/mapstruct-processor/1.6.3/mapstruct-processor-1.6.3.jar" />
|
||||
<entry name="$USER_HOME$/.m2/peak-repo/org/projectlombok/lombok/1.18.42/lombok-1.18.42.jar" />
|
||||
<entry name="$USER_HOME$/.m2/peak-repo/org/mapstruct/mapstruct-processor/1.6.3/mapstruct-processor-1.6.3.jar" />
|
||||
<entry name="$USER_HOME$/.m2/peak-repo/com/google/errorprone/error_prone_core/2.37.0/error_prone_core-2.37.0.jar" />
|
||||
|
||||
19
.idea/inspectionProfiles/Project_Default.xml
generated
19
.idea/inspectionProfiles/Project_Default.xml
generated
@@ -19,11 +19,30 @@
|
||||
<option name="REPORT_ANNOTATION_NOT_PROPAGATED_TO_OVERRIDERS" value="true" />
|
||||
<option name="REPORT_NULLS_PASSED_TO_NON_ANNOTATED_METHOD" value="true" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="OptionalOfNullableMisuse" enabled="true" level="ERROR" enabled_by_default="true" />
|
||||
<inspection_tool class="SameParameterValue" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="UnnecessaryLocalVariable" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="m_ignoreImmediatelyReturnedVariables" value="false" />
|
||||
<option name="m_ignoreAnnotatedVariables" value="false" />
|
||||
</inspection_tool>
|
||||
<inspection_tool class="UnusedProperty" enabled="false" level="WARNING" enabled_by_default="false" />
|
||||
<inspection_tool class="VulnerableLibrariesLocal" enabled="false" level="WARNING" enabled_by_default="false">
|
||||
<option name="isIgnoringEnabled" value="true" />
|
||||
<option name="ignoredModules">
|
||||
<list>
|
||||
<option value="peakup-backend" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="ignoredPackages">
|
||||
<list>
|
||||
<option value="io.netty:netty-codec-http:4.1.130.Final" />
|
||||
</list>
|
||||
</option>
|
||||
<option name="ignoredReasons">
|
||||
<list>
|
||||
<option value="Not exploitable" />
|
||||
</list>
|
||||
</option>
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
134
peakup-backend/checkstyle.xml
Normal file
134
peakup-backend/checkstyle.xml
Normal file
@@ -0,0 +1,134 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE module PUBLIC
|
||||
"-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
|
||||
"https://checkstyle.org/dtds/configuration_1_3.dtd">
|
||||
|
||||
<!--
|
||||
Checkstyle configuration mirroring IntelliJ IDEA's default Java code style.
|
||||
- 4-space indentation, no tabs
|
||||
- 120-char line length (IntelliJ right-margin default)
|
||||
- K&R brace style (opening brace on same line)
|
||||
- Import order: all non-java first, then java.* / javax.* separated by blank line, statics at bottom
|
||||
- Standard naming conventions (lowerCamelCase, UpperCamelCase, UPPER_SNAKE)
|
||||
- Suppressions for mapper files (aligned @Mapping annotations) in suppressions.xml
|
||||
-->
|
||||
<module name="Checker">
|
||||
<property name="severity" value="warning"/>
|
||||
<property name="fileExtensions" value="java"/>
|
||||
|
||||
<!-- No tab characters anywhere in the file -->
|
||||
<module name="FileTabCharacter">
|
||||
<property name="eachLine" value="true"/>
|
||||
</module>
|
||||
|
||||
<!-- Files must end with a newline -->
|
||||
<module name="NewlineAtEndOfFile"/>
|
||||
|
||||
<!-- No trailing whitespace -->
|
||||
<module name="RegexpSingleline">
|
||||
<property name="format" value="\s+$"/>
|
||||
<property name="message" value="Trailing whitespace"/>
|
||||
</module>
|
||||
|
||||
<!-- 120-char line length (IntelliJ right-margin default); imports and URLs ignored -->
|
||||
<module name="LineLength">
|
||||
<property name="max" value="120"/>
|
||||
<property name="ignorePattern" value="^package.*|^import.*|https?://|ftp://"/>
|
||||
</module>
|
||||
|
||||
<module name="TreeWalker">
|
||||
|
||||
<!-- ===== IMPORTS ===== -->
|
||||
<module name="AvoidStarImport"/>
|
||||
<module name="UnusedImports"/>
|
||||
<!-- Order: all non-java/* imports alphabetically, then java.* / javax.* after a blank line, static at bottom -->
|
||||
<module name="ImportOrder">
|
||||
<property name="groups" value="*,/^java\./,/^javax\./"/>
|
||||
<property name="separated" value="true"/>
|
||||
<property name="separatedStaticGroups" value="true"/>
|
||||
<property name="option" value="bottom"/>
|
||||
<property name="sortStaticImportsAlphabetically" value="true"/>
|
||||
</module>
|
||||
|
||||
<!-- ===== INDENTATION ===== -->
|
||||
<module name="Indentation">
|
||||
<property name="basicOffset" value="4"/>
|
||||
<property name="braceAdjustment" value="0"/>
|
||||
<property name="caseIndent" value="4"/>
|
||||
<property name="throwsIndent" value="8"/>
|
||||
<property name="lineWrappingIndentation" value="8"/>
|
||||
<property name="arrayInitIndent" value="4"/>
|
||||
</module>
|
||||
|
||||
<!-- ===== BRACES ===== -->
|
||||
<!-- Opening { on same line (K&R) -->
|
||||
<module name="LeftCurly"/>
|
||||
<!-- Closing } on own line -->
|
||||
<module name="RightCurly"/>
|
||||
<!-- Always use braces for if/else/for/while/do -->
|
||||
<module name="NeedBraces">
|
||||
<property name="tokens" value="LITERAL_IF, LITERAL_ELSE, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO"/>
|
||||
</module>
|
||||
|
||||
<!-- ===== WHITESPACE ===== -->
|
||||
<module name="WhitespaceAfter">
|
||||
<property name="tokens" value="COMMA, SEMI, TYPECAST, LITERAL_IF, LITERAL_ELSE, LITERAL_FOR, LITERAL_WHILE, LITERAL_DO, LITERAL_RETURN, LITERAL_SWITCH"/>
|
||||
</module>
|
||||
<module name="WhitespaceAround">
|
||||
<property name="tokens" value="ASSIGN, BAND, BAND_ASSIGN, BOR, BOR_ASSIGN, BSR, BSR_ASSIGN, BXOR, BXOR_ASSIGN, DIV, DIV_ASSIGN, EQUAL, GE, GT, LAND, LE, LOR, LT, MINUS, MINUS_ASSIGN, MOD, MOD_ASSIGN, NOT_EQUAL, PLUS, PLUS_ASSIGN, QUESTION, SL, SL_ASSIGN, SR, SR_ASSIGN, STAR, STAR_ASSIGN, TYPE_EXTENSION_AND"/>
|
||||
<property name="allowEmptyConstructors" value="true"/>
|
||||
<property name="allowEmptyMethods" value="true"/>
|
||||
<property name="allowEmptyTypes" value="true"/>
|
||||
<property name="allowEmptyLoops" value="true"/>
|
||||
<property name="allowEmptyLambdas" value="true"/>
|
||||
</module>
|
||||
<module name="NoWhitespaceBefore">
|
||||
<property name="tokens" value="COMMA, SEMI, POST_INC, POST_DEC"/>
|
||||
<property name="allowLineBreaks" value="true"/>
|
||||
</module>
|
||||
<module name="GenericWhitespace"/>
|
||||
<module name="MethodParamPad"/>
|
||||
<module name="ParenPad"/>
|
||||
|
||||
<!-- Blank lines between methods/constructors/inner classes (not between fields) -->
|
||||
<module name="EmptyLineSeparator">
|
||||
<property name="allowNoEmptyLineBetweenFields" value="true"/>
|
||||
<property name="allowMultipleEmptyLines" value="false"/>
|
||||
<property name="allowMultipleEmptyLinesInsideClassMembers" value="false"/>
|
||||
<property name="tokens" value="METHOD_DEF, CTOR_DEF, STATIC_INIT, INSTANCE_INIT, CLASS_DEF, INTERFACE_DEF, ENUM_DEF"/>
|
||||
</module>
|
||||
|
||||
<!-- ===== NAMING ===== -->
|
||||
<module name="TypeName"/>
|
||||
<module name="MethodName"/>
|
||||
<module name="LocalVariableName"/>
|
||||
<module name="MemberName"/>
|
||||
<module name="ParameterName"/>
|
||||
<module name="StaticVariableName"/>
|
||||
<module name="ConstantName"/>
|
||||
<module name="PackageName">
|
||||
<property name="format" value="^[a-z]+(\.[a-z][a-z0-9]*)*$"/>
|
||||
</module>
|
||||
|
||||
<!-- ===== CODE STYLE ===== -->
|
||||
<!-- One statement per line -->
|
||||
<module name="OneStatementPerLine"/>
|
||||
<!-- No multiple variable declarations on one line -->
|
||||
<module name="MultipleVariableDeclarations"/>
|
||||
<!-- Only one top-level type per file -->
|
||||
<module name="OneTopLevelClass"/>
|
||||
<!-- Modifier order: public protected private abstract default static final ... -->
|
||||
<module name="ModifierOrder"/>
|
||||
<!-- No empty statements (stray semicolons) -->
|
||||
<module name="EmptyStatement"/>
|
||||
<!-- Use equals() for string comparison, not == -->
|
||||
<module name="StringLiteralEquality"/>
|
||||
<!-- No finalize() method -->
|
||||
<module name="NoFinalizer"/>
|
||||
<!-- C-style array declarations (String[] args, not String args[]) -->
|
||||
<module name="ArrayTypeStyle"/>
|
||||
<!-- Use L not l for long literals -->
|
||||
<module name="UpperEll"/>
|
||||
|
||||
</module>
|
||||
</module>
|
||||
@@ -244,6 +244,37 @@
|
||||
</systemPropertyVariables>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>checkstyle</id>
|
||||
<phase>verify</phase>
|
||||
<goals>
|
||||
<goal>check</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
<configuration>
|
||||
<configLocation>checkstyle.xml</configLocation>
|
||||
<suppressionsLocation>suppressions.xml</suppressionsLocation>
|
||||
<excludes>**/generated-sources/**,**/generated/**</excludes>
|
||||
<includeTestSourceDirectory>false</includeTestSourceDirectory>
|
||||
<violationSeverity>warning</violationSeverity>
|
||||
<failsOnError>false</failsOnError>
|
||||
<failOnViolation>false</failOnViolation>
|
||||
<consoleOutput>true</consoleOutput>
|
||||
</configuration>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.puppycrawl.tools</groupId>
|
||||
<artifactId>checkstyle</artifactId>
|
||||
<version>10.21.4</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
<version>${surefire-plugin.version}</version>
|
||||
|
||||
@@ -9,4 +9,4 @@ public class PeakupMain {
|
||||
System.out.println("Peakup Engine starts...");
|
||||
Quarkus.run(args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.peakup.activity;
|
||||
|
||||
import com.peakup.activity.model.IntervalsActivity;
|
||||
import com.peakup.activity.service.IntervalsActivityService;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.persistence.NoResultException;
|
||||
@@ -13,7 +14,6 @@ import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
|
||||
|
||||
@@ -4,10 +4,15 @@ import com.peakup.activity.model.ActivityMapStreamPoint;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.*;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.List;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@lombok.extern.slf4j.Slf4j
|
||||
@ApplicationScoped
|
||||
public class ActivityMapStreamDAO {
|
||||
|
||||
@@ -5,10 +5,16 @@ import jakarta.annotation.Nullable;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
import java.sql.*;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Timestamp;
|
||||
import java.sql.Types;
|
||||
import java.util.List;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
@lombok.extern.slf4j.Slf4j
|
||||
@ApplicationScoped
|
||||
public class ActivityStreamDAO {
|
||||
|
||||
@@ -72,14 +72,18 @@ public class IntervalsActivityImporter
|
||||
protected int persist(Long athleteId, List<IntervalsClient.IntervalsActivity> raws) {
|
||||
int count = 0;
|
||||
for (IntervalsClient.IntervalsActivity raw : raws) {
|
||||
if (self.persistSingle(raw, athleteId)) count++;
|
||||
if (self.persistSingle(raw, athleteId)) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public boolean persistSingle(IntervalsClient.IntervalsActivity raw, Long athleteId) {
|
||||
if (findExisting(raw) != null) return false;
|
||||
if (findExisting(raw) != null) {
|
||||
return false;
|
||||
}
|
||||
IntervalsActivity activity = map(raw, athleteId);
|
||||
intervalsActivityDAO.insert(activity);
|
||||
return true;
|
||||
@@ -136,7 +140,9 @@ public class IntervalsActivityImporter
|
||||
|
||||
private boolean importStreams(IntervalsClient.StreamsData streams, java.time.Instant startTime, Long activityId)
|
||||
throws SQLException {
|
||||
if (streams.size() == 0) return false;
|
||||
if (streams.size() == 0) {
|
||||
return false;
|
||||
}
|
||||
activityStreamDAO.insertBatch(intervalsActivityMapper.mapStreams(streams, startTime, activityId));
|
||||
return true;
|
||||
}
|
||||
@@ -145,8 +151,12 @@ public class IntervalsActivityImporter
|
||||
java.time.Instant startTime, Long activityId)
|
||||
throws SQLException {
|
||||
IntervalsClient.MapData mapData = intervalsActivityConnector.getMap(originId);
|
||||
if (mapData.latlngs == null || mapData.latlngs.isEmpty()) return false;
|
||||
if (streams.time == null) return false;
|
||||
if (mapData.latlngs == null || mapData.latlngs.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
if (streams.time == null) {
|
||||
return false;
|
||||
}
|
||||
activityMapStreamDAO.insertBatch(
|
||||
intervalsActivityMapper.mapMapStream(mapData, streams.time, startTime, activityId));
|
||||
return true;
|
||||
@@ -170,7 +180,8 @@ public class IntervalsActivityImporter
|
||||
intervalsActivityConnector.fetchForRange(intervalsAthleteId, oldest, newest);
|
||||
allRaws.stream()
|
||||
.filter(a -> a.start_date == null)
|
||||
.forEach(a -> log.warn("Skipping intervals.icu activity without start_date: id={}, name={}", a.id, a.name));
|
||||
.forEach(a -> log.warn("Skipping intervals.icu activity without start_date: id={}, name={}",
|
||||
a.id, a.name));
|
||||
List<IntervalsClient.IntervalsActivity> raws = allRaws.stream()
|
||||
.filter(a -> a.start_date != null)
|
||||
.toList();
|
||||
|
||||
@@ -2,7 +2,11 @@ package com.peakup.activity.model;
|
||||
|
||||
import com.peakup.common.model.AbstractTimescaleEntity;
|
||||
import com.peakup.common.model.DependentEntity;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.IdClass;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@@ -3,7 +3,11 @@ package com.peakup.activity.model;
|
||||
import com.peakup.common.model.AbstractTimescaleEntity;
|
||||
import com.peakup.common.model.DependentEntity;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.IdClass;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@@ -2,7 +2,14 @@ package com.peakup.activity.model;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.persistence.CollectionTable;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.ElementCollection;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.FetchType;
|
||||
import jakarta.persistence.JoinColumn;
|
||||
import jakarta.persistence.OrderColumn;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.annotations.JdbcTypeCode;
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.peakup.athlete;
|
||||
|
||||
import com.peakup.athlete.model.Athlete;
|
||||
import com.peakup.athlete.service.AthleteService;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.persistence.NoResultException;
|
||||
@@ -12,7 +13,6 @@ import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.annotation.Nullable;
|
||||
|
||||
@Path("/api/athletes")
|
||||
@ApplicationScoped
|
||||
|
||||
@@ -28,4 +28,4 @@ public class AthleteDAO extends AbstractTimestampedDAO<Athlete> {
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ public class CorsFilter implements ContainerResponseFilter {
|
||||
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) {
|
||||
responseContext.getHeaders().add("Access-Control-Allow-Origin", "*");
|
||||
responseContext.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, PATCH");
|
||||
responseContext.getHeaders().add("Access-Control-Allow-Headers", "accept, authorization, content-type, x-requested-with");
|
||||
responseContext.getHeaders().add("Access-Control-Allow-Headers",
|
||||
"accept, authorization, content-type, x-requested-with");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
package com.peakup.common;
|
||||
|
||||
public class PeakUpException extends RuntimeException {
|
||||
public PeakUpException(String msg) { super(msg); }
|
||||
public PeakUpException(String msg, Throwable cause) { super(msg, cause); }
|
||||
|
||||
public PeakUpException(String msg) {
|
||||
super(msg);
|
||||
}
|
||||
|
||||
public PeakUpException(String msg, Throwable cause) {
|
||||
super(msg, cause);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ public class TokenCache {
|
||||
return token;
|
||||
}
|
||||
|
||||
|
||||
public void invalidate(Long athleteId) {
|
||||
cache.invalidate(athleteId);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
package com.peakup.common.annotation;
|
||||
|
||||
|
||||
import javax.annotation.meta.TypeQualifierDefault;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
|
||||
import javax.annotation.meta.TypeQualifierDefault;
|
||||
|
||||
@jakarta.annotation.Nonnull // Für moderne Frameworks & IDE
|
||||
@TypeQualifierDefault({
|
||||
|
||||
@@ -17,7 +17,9 @@ public abstract class AbstractEnumConverter<V extends PersistableEnum> implement
|
||||
|
||||
@Override
|
||||
public @Nullable V convertToEntityAttribute(@Nullable Integer dbData) {
|
||||
if (dbData == null) return null;
|
||||
if (dbData == null) {
|
||||
return null;
|
||||
}
|
||||
return getEnumByKey(dbData);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,10 @@ public interface PersistableEnum<V extends PersistableEnum> {
|
||||
|
||||
static <T extends PersistableEnum> T getEnumByKey(Integer key, T[] values) {
|
||||
for (T v : values) {
|
||||
if (v.getKey().equals(key)) return v;
|
||||
if (v.getKey().equals(key)) {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unknown key: " + key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
package com.peakup.common.model;
|
||||
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.persistence.*;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.MappedSuperclass;
|
||||
import jakarta.persistence.SequenceGenerator;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@@ -32,9 +32,12 @@ public class StravaActivityConnector implements ActivityConnector<StravaClient.A
|
||||
long beforeEpoch = before != null ? before.getEpochSecond() : 0L;
|
||||
List<StravaClient.Activity> all = new ArrayList<>();
|
||||
for (int page = 1; page <= MAX_PAGES; page++) {
|
||||
List<StravaClient.Activity> page_ = stravaClient.getAthleteActivities(afterEpoch, beforeEpoch, page, PAGE_SIZE);
|
||||
if (page_.isEmpty()) break;
|
||||
all.addAll(page_);
|
||||
List<StravaClient.Activity> activities =
|
||||
stravaClient.getAthleteActivities(afterEpoch, beforeEpoch, page, PAGE_SIZE);
|
||||
if (activities.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
all.addAll(activities);
|
||||
}
|
||||
return all;
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
|
||||
@Path("/oauth")
|
||||
@RegisterRestClient(configKey = "strava-api")
|
||||
@ApplicationScoped
|
||||
@@ -23,4 +22,4 @@ public interface StravaAuthClient {
|
||||
@FormParam("grant_type") String grantType,
|
||||
@FormParam("refresh_token") String refreshToken
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ import java.util.List;
|
||||
@RegisterClientHeaders(StravaHeadersFactory.class)
|
||||
public interface StravaClient {
|
||||
|
||||
|
||||
@GET
|
||||
@Path("/athlete/activities")
|
||||
List<Activity> getAthleteActivities(
|
||||
@@ -46,7 +45,7 @@ public interface StravaClient {
|
||||
public @Nullable Double max_heartrate;
|
||||
public @Nullable Double average_watts;
|
||||
public @Nullable Double max_watts;
|
||||
public @Nullable Double weighted_average_watts;// Normalized Power
|
||||
public @Nullable Double weighted_average_watts; // Normalized Power
|
||||
public @Nullable Double average_cadence;
|
||||
public @Nullable Double kilojoules;
|
||||
public @Nullable Double calories;
|
||||
|
||||
@@ -27,4 +27,4 @@ public class StravaContext {
|
||||
public void clear() {
|
||||
ATHLETE_ID.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,4 +23,4 @@ public class StravaConnectorDAO extends AbstractDAO<StravaConnector> {
|
||||
cq.select(root).where(cb.equal(root.get(StravaConnector_.ATHLETE_ID), athleteId));
|
||||
return em.createQuery(cq).getSingleResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,17 +18,14 @@ import java.util.Objects;
|
||||
@ApplicationScoped
|
||||
public class StravaConnectorService extends AbstractService<StravaConnector, StravaConnectorDAO> {
|
||||
|
||||
|
||||
@RestClient
|
||||
@SuppressWarnings("NullAway.Init")
|
||||
StravaAuthClient stravaAuthClient;
|
||||
|
||||
|
||||
@Inject
|
||||
@SuppressWarnings("NullAway.Init")
|
||||
StravaConnectorDAO stravaConnectorDAO;
|
||||
|
||||
|
||||
@Override
|
||||
protected StravaConnectorDAO getDAO() {
|
||||
return stravaConnectorDAO;
|
||||
@@ -54,8 +51,11 @@ public class StravaConnectorService extends AbstractService<StravaConnector, Str
|
||||
public String getToken(long atheleteId, long currentTimeInSeconds) {
|
||||
StravaConnector stravaConnector = getDAO().getByAthleteId(atheleteId);
|
||||
if (currentTimeInSeconds > stravaConnector.getExpiresAt()) {
|
||||
log.info("The token for athlete {} has been expired for {} seconds.", stravaConnector.getAthleteId(), (currentTimeInSeconds - stravaConnector.getExpiresAt()));
|
||||
Map<String, Object> result = stravaAuthClient.refreshToken(stravaConnector.getClientId(), stravaConnector.getClientSecret(), "refresh_token", stravaConnector.getRefreshToken());
|
||||
log.info("The token for athlete {} has been expired for {} seconds.",
|
||||
stravaConnector.getAthleteId(), (currentTimeInSeconds - stravaConnector.getExpiresAt()));
|
||||
Map<String, Object> result = stravaAuthClient.refreshToken(
|
||||
stravaConnector.getClientId(), stravaConnector.getClientSecret(),
|
||||
"refresh_token", stravaConnector.getRefreshToken());
|
||||
Assert.isEqual(result.get("token_type"), "Bearer");
|
||||
update(Objects.requireNonNull(stravaConnector.getId()),
|
||||
Objects.requireNonNull(result.get("access_token")).toString(),
|
||||
|
||||
@@ -2,9 +2,9 @@ package com.peakup.goal.dao;
|
||||
|
||||
import com.peakup.common.dao.AbstractDAO;
|
||||
import com.peakup.goal.model.Goal;
|
||||
import com.peakup.goal.model.Goal_;
|
||||
import com.peakup.goal.model.GoalMetricEnum;
|
||||
import com.peakup.goal.model.GoalPeriodEnum;
|
||||
import com.peakup.goal.model.Goal_;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.persistence.criteria.CriteriaBuilder;
|
||||
@@ -31,7 +31,8 @@ public class GoalDAO extends AbstractDAO<Goal> {
|
||||
return em.createQuery(cq).getResultList();
|
||||
}
|
||||
|
||||
public @Nullable Goal findByAthleteAndPeriodAndMetric(Long athleteId, GoalPeriodEnum period, GoalMetricEnum metric) {
|
||||
public @Nullable Goal findByAthleteAndPeriodAndMetric(
|
||||
Long athleteId, GoalPeriodEnum period, GoalMetricEnum metric) {
|
||||
CriteriaBuilder cb = em.getCriteriaBuilder();
|
||||
CriteriaQuery<Goal> cq = cb.createQuery(Goal.class);
|
||||
Root<Goal> root = cq.from(Goal.class);
|
||||
|
||||
@@ -42,7 +42,8 @@ public class GoalService extends AbstractService<Goal, GoalDAO> {
|
||||
return goalDAO.getByAthleteId(athleteId);
|
||||
}
|
||||
|
||||
public @Nullable Goal findByAthleteAndPeriodAndMetric(Long athleteId, GoalPeriodEnum period, GoalMetricEnum metric) {
|
||||
public @Nullable Goal findByAthleteAndPeriodAndMetric(
|
||||
Long athleteId, GoalPeriodEnum period, GoalMetricEnum metric) {
|
||||
return goalDAO.findByAthleteAndPeriodAndMetric(athleteId, period, metric);
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,8 @@ public class ActivityImportScheduler {
|
||||
for (IntervalsConnector connector : intervalsConnectorService.getIntervalsConnectors()) {
|
||||
try {
|
||||
intervalsContext.setAthleteId(connector.getAthleteId());
|
||||
intervalsActivityImporter.importActivities(connector.getAthleteId(), connector.getIntervalsAthleteId());
|
||||
intervalsActivityImporter.importActivities(
|
||||
connector.getAthleteId(), connector.getIntervalsAthleteId());
|
||||
} catch (Exception e) {
|
||||
log.error("Failed to import Intervals activities for athleteId {}.", connector.getAthleteId(), e);
|
||||
} finally {
|
||||
|
||||
@@ -4,5 +4,5 @@ public enum IntensityMetric {
|
||||
POWER,
|
||||
HEART_RATE,
|
||||
PACE
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -7,20 +7,22 @@ import com.peakup.common.model.DependentEntity;
|
||||
import jakarta.annotation.Nullable;
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import org.hibernate.annotations.DiscriminatorFormula;
|
||||
import jakarta.persistence.Inheritance;
|
||||
import jakarta.persistence.InheritanceType;
|
||||
import jakarta.persistence.Table;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.hibernate.annotations.DiscriminatorFormula;
|
||||
|
||||
@Entity
|
||||
@Table(name = "workout_step")
|
||||
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
|
||||
@DiscriminatorFormula("CASE WHEN step_type = 3 THEN 'RAMP' ELSE 'FIXED' END")
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY, property = "stepType", visible = true)
|
||||
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXISTING_PROPERTY,
|
||||
property = "stepType", visible = true)
|
||||
@JsonSubTypes({
|
||||
@JsonSubTypes.Type(value = FixedWorkoutStep.class, names = {"WARM_UP", "WORK", "COOL_DOWN", "RECOVERY", "REST"}),
|
||||
@JsonSubTypes.Type(value = FixedWorkoutStep.class,
|
||||
names = {"WARM_UP", "WORK", "COOL_DOWN", "RECOVERY", "REST"}),
|
||||
@JsonSubTypes.Type(value = RampWorkoutStep.class, name = "RAMP")
|
||||
})
|
||||
@Data
|
||||
|
||||
@@ -68,7 +68,8 @@ public class WeekPlanImportService {
|
||||
try {
|
||||
var ignored = DayOfWeek.valueOf(w.getDay().toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException e) {
|
||||
errors.add(prefix + ".day: must be one of monday, tuesday, wednesday, thursday, friday, saturday, sunday");
|
||||
errors.add(prefix + ".day: must be one of monday, tuesday, wednesday,"
|
||||
+ " thursday, friday, saturday, sunday");
|
||||
}
|
||||
}
|
||||
if (w.getType() == null) {
|
||||
@@ -124,10 +125,16 @@ public class WeekPlanImportService {
|
||||
}
|
||||
|
||||
if (stepType == WorkoutStepTypeEnum.RAMP) {
|
||||
if (s.getTargetValuePctStart() == null) errors.add(prefix + ".targetValuePctStart: required for RAMP steps");
|
||||
if (s.getTargetValuePctEnd() == null) errors.add(prefix + ".targetValuePctEnd: required for RAMP steps");
|
||||
if (s.getTargetValuePctStart() == null) {
|
||||
errors.add(prefix + ".targetValuePctStart: required for RAMP steps");
|
||||
}
|
||||
if (s.getTargetValuePctEnd() == null) {
|
||||
errors.add(prefix + ".targetValuePctEnd: required for RAMP steps");
|
||||
}
|
||||
} else if (stepType != null) {
|
||||
if (s.getTargetValuePct() == null) errors.add(prefix + ".targetValuePct: required");
|
||||
if (s.getTargetValuePct() == null) {
|
||||
errors.add(prefix + ".targetValuePct: required");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package com.peakup.workout.service;
|
||||
|
||||
import com.peakup.common.service.AbstractService;
|
||||
import com.peakup.workout.dao.WorkoutDAO;
|
||||
import com.peakup.workout.model.Workout;
|
||||
import com.peakup.common.service.AbstractService;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
|
||||
|
||||
30
peakup-backend/suppressions.xml
Normal file
30
peakup-backend/suppressions.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE suppressions PUBLIC
|
||||
"-//Checkstyle//DTD SuppressionFilter Configuration 1.2//EN"
|
||||
"https://checkstyle.org/dtds/suppressions_1_2.dtd">
|
||||
|
||||
<suppressions>
|
||||
<!-- Mapper files use vertically-aligned @Mapping annotations (extra spaces after commas).
|
||||
NeedBraces suppressed: compact null-guard / stream-mapping single-liners are intentional. -->
|
||||
<suppress files="[\\/]mapper[\\/]" checks="WhitespaceAfter"/>
|
||||
<suppress files="[\\/]mapper[\\/]" checks="LineLength"/>
|
||||
<suppress files="[\\/]mapper[\\/]" checks="NeedBraces"/>
|
||||
|
||||
<!-- DAO files use IntelliJ-style aligned try-with-resources and aligned batch params -->
|
||||
<suppress files="ActivityStreamDAO\.java" checks="Indentation"/>
|
||||
<suppress files="ActivityStreamDAO\.java" checks="MethodParamPad"/>
|
||||
<suppress files="ActivityStreamDAO\.java" checks="WhitespaceAfter"/>
|
||||
<suppress files="ActivityMapStreamDAO\.java" checks="Indentation"/>
|
||||
<suppress files="ActivityMapStreamDAO\.java" checks="MethodParamPad"/>
|
||||
|
||||
<!-- Client DTO inner classes mirror JSON field names — snake_case is intentional -->
|
||||
<suppress files="[\\/]client[\\/]" checks="MemberName"/>
|
||||
<!-- Client interfaces may use wildcard imports for JAX-RS annotations -->
|
||||
<suppress files="[\\/]client[\\/]" checks="AvoidStarImport"/>
|
||||
<!-- Client DTOs can have long lines due to field declarations with long API names -->
|
||||
<suppress files="[\\/]client[\\/]" checks="LineLength"/>
|
||||
|
||||
<!-- package-info.java files have no class body or trailing newline -->
|
||||
<suppress files="package-info\.java" checks="EmptyLineSeparator"/>
|
||||
<suppress files="package-info\.java" checks="NewlineAtEndOfFile"/>
|
||||
</suppressions>
|
||||
Reference in New Issue
Block a user