Improve tests

This commit is contained in:
2026-03-29 20:05:00 +02:00
parent 48f048e413
commit 9fb3d38d96
35 changed files with 333 additions and 54 deletions

1
.idea/compiler.xml generated
View File

@@ -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" />

View File

@@ -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>

View 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>

View File

@@ -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>

View File

@@ -9,4 +9,4 @@ public class PeakupMain {
System.out.println("Peakup Engine starts...");
Quarkus.run(args);
}
}
}

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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();

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -28,4 +28,4 @@ public class AthleteDAO extends AbstractTimestampedDAO<Athlete> {
.findFirst()
.orElse(null);
}
}
}

View File

@@ -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");
}
}

View File

@@ -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);
}
}

View File

@@ -21,7 +21,6 @@ public class TokenCache {
return token;
}
public void invalidate(Long athleteId) {
cache.invalidate(athleteId);
}

View File

@@ -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({

View File

@@ -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);
}

View File

@@ -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);
}
}
}

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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
);
}
}

View File

@@ -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;

View File

@@ -27,4 +27,4 @@ public class StravaContext {
public void clear() {
ATHLETE_ID.remove();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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(),

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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 {

View File

@@ -4,5 +4,5 @@ public enum IntensityMetric {
POWER,
HEART_RATE,
PACE
}

View File

@@ -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

View File

@@ -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");
}
}
}

View File

@@ -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;

View 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>