irisExerciseSettingsRepository,
+ ParticipationRepository participationRepository) {
this.groupNotificationService = groupNotificationService;
this.websocketMessagingService = websocketMessagingService;
this.resultWebsocketService = resultWebsocketService;
this.ltiNewResultService = ltiNewResultService;
this.teamRepository = teamRepository;
+ this.irisExerciseSettingsRepository = irisExerciseSettingsRepository;
this.participationRepository = participationRepository;
this.pyrisEventService = pyrisEventService;
}
@@ -188,13 +193,16 @@ public void notifyUserAboutNewResult(Result result, ProgrammingExerciseParticipa
if (participation instanceof ProgrammingExerciseStudentParticipation studentParticipation) {
// do not try to report results for template or solution participations
ltiNewResultService.ifPresent(newResultService -> newResultService.onNewResult(studentParticipation));
- // Inform Iris about the submission status
+ // Inform Iris about the submission status (when certain conditions are met)
notifyIrisAboutSubmissionStatus(result, studentParticipation);
}
}
/**
* Notify Iris about the submission status for the given result and student participation.
+ * Only notifies if the user has accepted Iris, the exercise is not an exam exercise, and the exercise chat is enabled in the exercise settings
+ * NOTE: we check those settings early to prevent unnecessary database queries and exceptions later on in most cases. More sophisticated checks are done in the Iris service.
+ *
* If the submission was successful, Iris will be informed about the successful submission.
* If the submission failed, Iris will be informed about the submission failure.
* Iris will only be informed about the submission status if the participant is a user.
@@ -203,14 +211,18 @@ public void notifyUserAboutNewResult(Result result, ProgrammingExerciseParticipa
* @param studentParticipation the student participation for which Iris should be informed about the submission status
*/
private void notifyIrisAboutSubmissionStatus(Result result, ProgrammingExerciseStudentParticipation studentParticipation) {
- if (studentParticipation.getParticipant() instanceof User) {
+ if (studentParticipation.getParticipant() instanceof User user) {
pyrisEventService.ifPresent(eventService -> {
- // Inform event service about the new result
- try {
- eventService.trigger(new NewResultEvent(result));
- }
- catch (Exception e) {
- log.error("Could not trigger service for result {}", result.getId(), e);
+ final var exercise = studentParticipation.getExercise();
+ if (user.hasAcceptedIris() && !exercise.isExamExercise() && irisExerciseSettingsRepository.get().isExerciseChatEnabled(exercise.getId())) {
+ // Inform event service about the new result
+ try {
+ // This is done asynchronously to prevent blocking the current thread
+ eventService.trigger(new NewResultEvent(result));
+ }
+ catch (Exception e) {
+ log.error("Could not trigger service for result {}", result.getId(), e);
+ }
}
});
}
diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingSubmissionService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingSubmissionService.java
index 9de4485f16b2..e1ae83935eec 100644
--- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingSubmissionService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingSubmissionService.java
@@ -67,7 +67,6 @@
import de.tum.cit.aet.artemis.programming.repository.ProgrammingSubmissionRepository;
import de.tum.cit.aet.artemis.programming.repository.SubmissionPolicyRepository;
import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationTriggerService;
-import de.tum.cit.aet.artemis.programming.service.hestia.ProgrammingExerciseGitDiffReportService;
import de.tum.cit.aet.artemis.programming.service.vcs.VersionControlService;
// TODO: this class has too many dependencies to other services. We should reduce this
diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/Action.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/Action.java
deleted file mode 100644
index be22f81ddad9..000000000000
--- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/Action.java
+++ /dev/null
@@ -1,86 +0,0 @@
-package de.tum.cit.aet.artemis.programming.service.aeolus;
-
-import java.util.List;
-import java.util.Map;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonInclude;
-
-/**
- * Base class for the actions that can be defined in a {@link Windfile}
- */
-// TODO: remove and convert subclasses into Records
-@JsonIgnoreProperties(ignoreUnknown = true)
-@JsonInclude(JsonInclude.Include.NON_EMPTY)
-public abstract class Action {
-
- private String name;
-
- private Map parameters;
-
- private Map environment;
-
- private List results;
-
- private String workdir;
-
- private boolean runAlways;
-
- private String platform;
-
- public Map getParameters() {
- return parameters;
- }
-
- public void setParameters(Map parameters) {
- this.parameters = parameters;
- }
-
- public Map getEnvironment() {
- return environment;
- }
-
- public void setEnvironment(Map environment) {
- this.environment = environment;
- }
-
- public boolean isRunAlways() {
- return runAlways;
- }
-
- public void setRunAlways(boolean runAlways) {
- this.runAlways = runAlways;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
-
- public List getResults() {
- return results;
- }
-
- public void setResults(List results) {
- this.results = results;
- }
-
- public String getWorkdir() {
- return workdir;
- }
-
- public void setWorkdir(String workdir) {
- this.workdir = workdir;
- }
-
- public String getPlatform() {
- return platform;
- }
-
- public void setPlatform(String platform) {
- this.platform = platform;
- }
-}
diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/ActionDeserializer.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/ActionDeserializer.java
index 65d42487e398..32463ba0d841 100644
--- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/ActionDeserializer.java
+++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/ActionDeserializer.java
@@ -8,6 +8,10 @@
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
+import de.tum.cit.aet.artemis.programming.dto.aeolus.Action;
+import de.tum.cit.aet.artemis.programming.dto.aeolus.PlatformAction;
+import de.tum.cit.aet.artemis.programming.dto.aeolus.ScriptAction;
+
/**
* Deserializer for {@link Action} that determines the type of the action based on the content of the JSON.
*/
diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusBuildPlanService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusBuildPlanService.java
index 9cb410de37fa..9dd35b363709 100644
--- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusBuildPlanService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusBuildPlanService.java
@@ -35,6 +35,8 @@
import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage;
import de.tum.cit.aet.artemis.programming.domain.VcsRepositoryUri;
import de.tum.cit.aet.artemis.programming.dto.AeolusGenerationResponseDTO;
+import de.tum.cit.aet.artemis.programming.dto.aeolus.AeolusRepository;
+import de.tum.cit.aet.artemis.programming.dto.aeolus.Windfile;
import de.tum.cit.aet.artemis.programming.service.InternalUrlService;
import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationService;
diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusBuildScriptGenerationService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusBuildScriptGenerationService.java
index 8830adb96389..57141ab1a84a 100644
--- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusBuildScriptGenerationService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusBuildScriptGenerationService.java
@@ -10,6 +10,8 @@
import de.tum.cit.aet.artemis.core.service.ProfileService;
import de.tum.cit.aet.artemis.programming.domain.AeolusTarget;
import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise;
+import de.tum.cit.aet.artemis.programming.dto.aeolus.Windfile;
+import de.tum.cit.aet.artemis.programming.dto.aeolus.WindfileMetadata;
import de.tum.cit.aet.artemis.programming.service.BuildScriptGenerationService;
import de.tum.cit.aet.artemis.programming.service.BuildScriptProviderService;
@@ -52,12 +54,12 @@ public String getScript(ProgrammingExercise programmingExercise) throws JsonProc
windfile = aeolusTemplateService.getDefaultWindfileFor(programmingExercise);
}
if (windfile != null) {
- WindfileMetadata oldMetadata = windfile.getMetadata();
+ WindfileMetadata oldMetadata = windfile.metadata();
// Creating a new instance of WindfileMetadata with placeholder values for id, name, and description,
// and copying the rest of the fields from oldMetadata
WindfileMetadata updatedMetadata = new WindfileMetadata("not-used", "not-used", "not-used", oldMetadata.author(), oldMetadata.gitCredentials(), oldMetadata.docker(),
oldMetadata.resultHook(), oldMetadata.resultHookCredentials());
- windfile.setMetadata(updatedMetadata);
+ windfile = new Windfile(windfile, updatedMetadata);
return aeolusBuildPlanService.generateBuildScript(windfile, AeolusTarget.CLI);
}
return null;
diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusTemplateService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusTemplateService.java
index 64321ad3b61d..f2e51ab78496 100644
--- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusTemplateService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/AeolusTemplateService.java
@@ -26,6 +26,10 @@
import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseBuildConfig;
import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage;
import de.tum.cit.aet.artemis.programming.domain.ProjectType;
+import de.tum.cit.aet.artemis.programming.dto.aeolus.Action;
+import de.tum.cit.aet.artemis.programming.dto.aeolus.DockerConfig;
+import de.tum.cit.aet.artemis.programming.dto.aeolus.Windfile;
+import de.tum.cit.aet.artemis.programming.dto.aeolus.WindfileMetadata;
import de.tum.cit.aet.artemis.programming.service.BuildScriptProviderService;
import de.tum.cit.aet.artemis.programming.web.localci.AeolusTemplateResource;
@@ -84,7 +88,7 @@ public void cacheOnBoot() {
script = buildScriptProviderService.replacePlaceholders(script, null, null, null);
}
Windfile windfile = readWindfile(script);
- this.addInstanceVariablesToWindfile(windfile, ProgrammingLanguage.valueOf(directory.toUpperCase()), optionalProjectType);
+ windfile = addInstanceVariablesToWindfile(windfile, ProgrammingLanguage.valueOf(directory.toUpperCase()), optionalProjectType);
templateCache.put(uniqueKey, windfile);
}
catch (IOException | IllegalArgumentException e) {
@@ -127,17 +131,15 @@ private static Windfile readWindfile(String yaml) throws IOException {
* @param projectType the project type for which the template file should be returned. If omitted, a default depending on the language will be used.
* @param staticAnalysis whether the static analysis template should be used
* @param sequentialRuns whether the sequential runs template should be used
- * @param testCoverage whether the test coverage template should be used
* @return the requested template as a {@link Windfile} object
* @throws IOException if the file does not exist
*/
- public Windfile getWindfileFor(ProgrammingLanguage programmingLanguage, Optional projectType, Boolean staticAnalysis, Boolean sequentialRuns, Boolean testCoverage)
- throws IOException {
+ public Windfile getWindfileFor(ProgrammingLanguage programmingLanguage, Optional projectType, Boolean staticAnalysis, Boolean sequentialRuns) throws IOException {
if (programmingLanguage.equals(ProgrammingLanguage.JAVA) && projectType.isEmpty()) {
// to be backwards compatible, we assume that java exercises without project type are plain maven projects
projectType = Optional.of(ProjectType.PLAIN_MAVEN);
}
- String templateFileName = buildScriptProviderService.buildTemplateName(projectType, staticAnalysis, sequentialRuns, testCoverage, "yaml");
+ String templateFileName = buildScriptProviderService.buildTemplateName(projectType, staticAnalysis, sequentialRuns, "yaml");
String uniqueKey = programmingLanguage.name().toLowerCase() + "_" + templateFileName;
if (templateCache.containsKey(uniqueKey)) {
return templateCache.get(uniqueKey);
@@ -151,7 +153,7 @@ public Windfile getWindfileFor(ProgrammingLanguage programmingLanguage, Optional
scriptCache = buildScriptProviderService.replacePlaceholders(scriptCache, null, null, null);
}
Windfile windfile = readWindfile(scriptCache);
- this.addInstanceVariablesToWindfile(windfile, programmingLanguage, projectType);
+ windfile = addInstanceVariablesToWindfile(windfile, programmingLanguage, projectType);
templateCache.put(uniqueKey, windfile);
return windfile;
}
@@ -184,7 +186,7 @@ public Windfile getDefaultWindfileFor(ProgrammingExercise exercise) {
try {
ProgrammingExerciseBuildConfig buildConfig = exercise.getBuildConfig();
return getWindfileFor(exercise.getProgrammingLanguage(), Optional.ofNullable(exercise.getProjectType()), exercise.isStaticCodeAnalysisEnabled(),
- buildConfig.hasSequentialTestRuns(), buildConfig.isTestwiseCoverageEnabled());
+ buildConfig.hasSequentialTestRuns());
}
catch (IOException e) {
log.info("No windfile for the settings of exercise {}", exercise.getId(), e);
@@ -202,23 +204,24 @@ public Windfile getDefaultWindfileFor(ProgrammingExercise exercise) {
* @param windfile the Windfile template to be updated with Docker configuration
* @param language the programming language used, which determines the Docker image and flags
* @param projectType an optional specifying the project type; influences the Docker configuration
+ * @return the updated Windfile instance with Docker configuration
*/
- private void addInstanceVariablesToWindfile(Windfile windfile, ProgrammingLanguage language, Optional projectType) {
+ private Windfile addInstanceVariablesToWindfile(Windfile windfile, ProgrammingLanguage language, Optional projectType) {
- WindfileMetadata metadata = windfile.getMetadata();
+ WindfileMetadata metadata = windfile.metadata();
if (metadata == null) {
metadata = new WindfileMetadata();
}
if (projectType.isPresent() && ProjectType.XCODE.equals(projectType.get())) {
// xcode does not support docker
metadata = new WindfileMetadata();
- windfile.setMetadata(metadata);
- return;
}
- String image = programmingLanguageConfiguration.getImage(language, projectType);
- DockerConfig dockerConfig = new DockerConfig(image, null, null, programmingLanguageConfiguration.getDefaultDockerFlags());
- metadata = new WindfileMetadata(metadata.name(), metadata.id(), metadata.description(), metadata.author(), metadata.gitCredentials(), dockerConfig, metadata.resultHook(),
- metadata.resultHookCredentials());
- windfile.setMetadata(metadata);
+ else {
+ String image = programmingLanguageConfiguration.getImage(language, projectType);
+ DockerConfig dockerConfig = new DockerConfig(image, null, null, programmingLanguageConfiguration.getDefaultDockerFlags());
+ metadata = new WindfileMetadata(metadata.name(), metadata.id(), metadata.description(), metadata.author(), metadata.gitCredentials(), dockerConfig,
+ metadata.resultHook(), metadata.resultHookCredentials());
+ }
+ return new Windfile(windfile, metadata);
}
}
diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/PlatformAction.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/PlatformAction.java
deleted file mode 100644
index 5c161ffe798b..000000000000
--- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/PlatformAction.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package de.tum.cit.aet.artemis.programming.service.aeolus;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonInclude;
-
-/**
- * Represents a CI action that is intended to run only on a specific target, can be used in a {@link Windfile}.
- */
-// TODO: convert into Record
-@JsonIgnoreProperties(ignoreUnknown = true)
-@JsonInclude(JsonInclude.Include.NON_EMPTY)
-public class PlatformAction extends Action {
-
- private String kind;
-
- private String type;
-
- public String getKind() {
- return kind;
- }
-
- public void setKind(String kind) {
- this.kind = kind;
- }
-
- public String getType() {
- return type;
- }
-
- public void setType(String type) {
- this.type = type;
- }
-}
diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/Windfile.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/Windfile.java
deleted file mode 100644
index a9f2382591d4..000000000000
--- a/src/main/java/de/tum/cit/aet/artemis/programming/service/aeolus/Windfile.java
+++ /dev/null
@@ -1,123 +0,0 @@
-package de.tum.cit.aet.artemis.programming.service.aeolus;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
-import com.fasterxml.jackson.annotation.JsonInclude;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.module.SimpleModule;
-
-/**
- * Represents a windfile, the definition file for an aeolus build plan that
- * can then be used to generate a Jenkinsfile.
- */
-// TODO convert into Record
-@JsonIgnoreProperties(ignoreUnknown = true)
-@JsonInclude(JsonInclude.Include.NON_EMPTY)
-public class Windfile {
-
- private static final ObjectMapper mapper = new ObjectMapper();
-
- private String api;
-
- private WindfileMetadata metadata;
-
- private List actions = new ArrayList<>();
-
- private Map repositories = new HashMap<>();
-
- public String getApi() {
- return api;
- }
-
- public void setApi(String api) {
- this.api = api;
- }
-
- public WindfileMetadata getMetadata() {
- return metadata;
- }
-
- public void setMetadata(WindfileMetadata metadata) {
- this.metadata = metadata;
- }
-
- public List getActions() {
- return actions;
- }
-
- public void setActions(List actions) {
- this.actions = actions;
- }
-
- /**
- * Gets the script actions of a windfile.
- *
- * @return the script actions of a windfile.
- */
- public List getScriptActions() {
- List scriptActions = new ArrayList<>();
- for (Action action : actions) {
- if (action instanceof ScriptAction) {
- scriptActions.add((ScriptAction) action);
- }
- }
- return scriptActions;
- }
-
- public void setRepositories(Map repositories) {
- this.repositories = repositories;
- }
-
- public Map getRepositories() {
- return repositories;
- }
-
- /**
- * Sets the pre-processing metadata for the windfile.
- *
- * @param id the id of the windfile.
- * @param name the name of the windfile.
- * @param gitCredentials the git credentials of the windfile.
- * @param resultHook the result hook of the windfile.
- * @param description the description of the windfile.
- * @param repositories the repositories of the windfile.
- * @param resultHookCredentials the credentials for the result hook of the windfile.
- */
- public void setPreProcessingMetadata(String id, String name, String gitCredentials, String resultHook, String description, Map repositories,
- String resultHookCredentials) {
- this.setMetadata(new WindfileMetadata(name, id, description, null, gitCredentials, null, resultHook, resultHookCredentials));
- this.setRepositories(repositories);
- }
-
- /**
- * Deserializes a windfile from a json string.
- *
- * @param json the json string to deserialize.
- * @return the deserialized windfile.
- * @throws JsonProcessingException if the json string is not valid.
- */
- public static Windfile deserialize(String json) throws JsonProcessingException {
- SimpleModule module = new SimpleModule();
- module.addDeserializer(Action.class, new ActionDeserializer());
- mapper.registerModule(module);
- return mapper.readValue(json, Windfile.class);
- }
-
- /**
- * Collects the results of all actions of a windfile.
- *
- * @return the results of all actions of this windfile
- */
- public List getResults() {
- List results = new ArrayList<>();
- for (Action action : actions.stream().filter(action -> action.getResults() != null && !action.getResults().isEmpty()).toList()) {
- results.addAll(action.getResults());
- }
- return results;
- }
-}
diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/AbstractContinuousIntegrationResultService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/AbstractContinuousIntegrationResultService.java
index 238d4ffca0fc..2f540cd869b7 100644
--- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/AbstractContinuousIntegrationResultService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/AbstractContinuousIntegrationResultService.java
@@ -11,13 +11,12 @@
import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise;
import de.tum.cit.aet.artemis.programming.domain.ProgrammingExerciseParticipation;
import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry;
-import de.tum.cit.aet.artemis.programming.dto.AbstractBuildResultNotificationDTO;
-import de.tum.cit.aet.artemis.programming.dto.BuildJobDTOInterface;
+import de.tum.cit.aet.artemis.programming.dto.BuildJobInterface;
+import de.tum.cit.aet.artemis.programming.dto.BuildResultNotification;
import de.tum.cit.aet.artemis.programming.repository.BuildLogStatisticsEntryRepository;
import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseBuildConfigRepository;
import de.tum.cit.aet.artemis.programming.repository.ProgrammingExerciseTestCaseRepository;
import de.tum.cit.aet.artemis.programming.service.ProgrammingExerciseFeedbackCreationService;
-import de.tum.cit.aet.artemis.programming.service.hestia.TestwiseCoverageService;
public abstract class AbstractContinuousIntegrationResultService implements ContinuousIntegrationResultService {
@@ -25,32 +24,29 @@ public abstract class AbstractContinuousIntegrationResultService implements Cont
protected final BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository;
- protected final TestwiseCoverageService testwiseCoverageService;
-
protected final ProgrammingExerciseFeedbackCreationService feedbackCreationService;
protected final ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository;
protected AbstractContinuousIntegrationResultService(ProgrammingExerciseTestCaseRepository testCaseRepository,
- BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, TestwiseCoverageService testwiseCoverageService,
- ProgrammingExerciseFeedbackCreationService feedbackCreationService, ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository) {
+ BuildLogStatisticsEntryRepository buildLogStatisticsEntryRepository, ProgrammingExerciseFeedbackCreationService feedbackCreationService,
+ ProgrammingExerciseBuildConfigRepository programmingExerciseBuildConfigRepository) {
this.testCaseRepository = testCaseRepository;
this.buildLogStatisticsEntryRepository = buildLogStatisticsEntryRepository;
- this.testwiseCoverageService = testwiseCoverageService;
this.feedbackCreationService = feedbackCreationService;
this.programmingExerciseBuildConfigRepository = programmingExerciseBuildConfigRepository;
}
@Override
- public Result createResultFromBuildResult(AbstractBuildResultNotificationDTO buildResult, ProgrammingExerciseParticipation participation) {
+ public Result createResultFromBuildResult(BuildResultNotification buildResult, ProgrammingExerciseParticipation participation) {
ProgrammingExercise exercise = participation.getProgrammingExercise();
final var result = new Result();
result.setAssessmentType(AssessmentType.AUTOMATIC);
result.setSuccessful(buildResult.isBuildSuccessful());
- result.setCompletionDate(buildResult.getBuildRunDate());
+ result.setCompletionDate(buildResult.buildRunDate());
// this only sets the score to a temporary value, the real score is calculated in the grading service
- result.setScore(buildResult.getBuildScore(), exercise.getCourseViaExerciseGroupOrCourseMember());
+ result.setScore(buildResult.buildScore(), exercise.getCourseViaExerciseGroupOrCourseMember());
result.setParticipation((Participation) participation);
addFeedbackToResult(result, buildResult);
@@ -63,8 +59,8 @@ public Result createResultFromBuildResult(AbstractBuildResultNotificationDTO bui
* @param result the result for which the feedback should be added
* @param buildResult The build result
*/
- private void addFeedbackToResult(Result result, AbstractBuildResultNotificationDTO buildResult) {
- final var jobs = buildResult.getBuildJobs();
+ private void addFeedbackToResult(Result result, BuildResultNotification buildResult) {
+ final var jobs = buildResult.jobs();
final var programmingExercise = (ProgrammingExercise) result.getParticipation().getExercise();
// 1) add feedback for failed and passed test cases
@@ -72,32 +68,28 @@ private void addFeedbackToResult(Result result, AbstractBuildResultNotificationD
// 2) process static code analysis feedback
addStaticCodeAnalysisFeedbackToResult(result, buildResult, programmingExercise);
-
- // 3) process testwise coverage analysis report
- addTestwiseCoverageReportToResult(result, buildResult, programmingExercise);
}
- private void addTestCaseFeedbacksToResult(Result result, List extends BuildJobDTOInterface> jobs, ProgrammingExercise programmingExercise) {
+ private void addTestCaseFeedbacksToResult(Result result, List extends BuildJobInterface> jobs, ProgrammingExercise programmingExercise) {
var activeTestCases = testCaseRepository.findByExerciseIdAndActive(programmingExercise.getId(), true);
- for (final var job : jobs) {
- for (final var failedTest : job.getFailedTests()) {
- result.addFeedback(
- feedbackCreationService.createFeedbackFromTestCase(failedTest.getName(), failedTest.getTestMessages(), false, programmingExercise, activeTestCases));
- }
- result.setTestCaseCount(result.getTestCaseCount() + job.getFailedTests().size());
+ jobs.forEach(job -> {
+ job.failedTests().forEach(failedTest -> {
+ result.addFeedback(feedbackCreationService.createFeedbackFromTestCase(failedTest.name(), failedTest.testMessages(), false, programmingExercise, activeTestCases));
+ });
+ result.setTestCaseCount(result.getTestCaseCount() + job.failedTests().size());
- for (final var successfulTest : job.getSuccessfulTests()) {
+ for (final var successfulTest : job.successfulTests()) {
result.addFeedback(
- feedbackCreationService.createFeedbackFromTestCase(successfulTest.getName(), successfulTest.getTestMessages(), true, programmingExercise, activeTestCases));
+ feedbackCreationService.createFeedbackFromTestCase(successfulTest.name(), successfulTest.testMessages(), true, programmingExercise, activeTestCases));
}
- result.setTestCaseCount(result.getTestCaseCount() + job.getSuccessfulTests().size());
- result.setPassedTestCaseCount(result.getPassedTestCaseCount() + job.getSuccessfulTests().size());
- }
+ result.setTestCaseCount(result.getTestCaseCount() + job.successfulTests().size());
+ result.setPassedTestCaseCount(result.getPassedTestCaseCount() + job.successfulTests().size());
+ });
}
- private void addStaticCodeAnalysisFeedbackToResult(Result result, AbstractBuildResultNotificationDTO buildResult, ProgrammingExercise programmingExercise) {
- final var staticCodeAnalysisReports = buildResult.getStaticCodeAnalysisReports();
+ private void addStaticCodeAnalysisFeedbackToResult(Result result, BuildResultNotification buildResult, ProgrammingExercise programmingExercise) {
+ final var staticCodeAnalysisReports = buildResult.staticCodeAnalysisReports();
if (Boolean.TRUE.equals(programmingExercise.isStaticCodeAnalysisEnabled()) && staticCodeAnalysisReports != null && !staticCodeAnalysisReports.isEmpty()) {
List scaFeedbackList = feedbackCreationService.createFeedbackFromStaticCodeAnalysisReports(staticCodeAnalysisReports);
result.addFeedbacks(scaFeedbackList);
@@ -105,18 +97,6 @@ private void addStaticCodeAnalysisFeedbackToResult(Result result, AbstractBuildR
}
}
- private void addTestwiseCoverageReportToResult(Result result, AbstractBuildResultNotificationDTO buildResult, ProgrammingExercise programmingExercise) {
- programmingExercise.setBuildConfig(programmingExerciseBuildConfigRepository.getProgrammingExerciseBuildConfigElseThrow(programmingExercise));
- if (Boolean.TRUE.equals(programmingExercise.getBuildConfig().isTestwiseCoverageEnabled())) {
- var report = buildResult.getTestwiseCoverageReports();
- if (report != null) {
- // since the test cases are not saved to the database yet, the test case is null for the entries
- var coverageFileReportsWithoutTestsByTestCaseName = testwiseCoverageService.createTestwiseCoverageFileReportsWithoutTestsByTestCaseName(report);
- result.setCoverageFileReportsByTestCaseName(coverageFileReportsWithoutTestsByTestCaseName);
- }
- }
- }
-
/**
* Find the ZonedDateTime of the first BuildLogEntry that contains the searchString in the log message.
*
diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationResultService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationResultService.java
index b81239c44f0e..a63fc4871d4c 100644
--- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationResultService.java
+++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/ContinuousIntegrationResultService.java
@@ -8,7 +8,7 @@
import de.tum.cit.aet.artemis.programming.domain.ProgrammingSubmission;
import de.tum.cit.aet.artemis.programming.domain.ProjectType;
import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry;
-import de.tum.cit.aet.artemis.programming.dto.AbstractBuildResultNotificationDTO;
+import de.tum.cit.aet.artemis.programming.dto.BuildResultNotification;
/**
* Abstract service for managing entities related to continuous integration.
@@ -21,7 +21,7 @@ public interface ContinuousIntegrationResultService {
* @param requestBody the object sent from the CI system to Artemis
* @return the DTO with all information in Java Object form
*/
- AbstractBuildResultNotificationDTO convertBuildResult(Object requestBody);
+ BuildResultNotification convertBuildResult(Object requestBody);
/**
* Generate an Artemis result object from the CI build result. Will use the test case results and issues in static code analysis as result feedback.
@@ -30,7 +30,7 @@ public interface ContinuousIntegrationResultService {
* @param participation to attach result to.
* @return the created Artemis result with a score, completion date, etc.
*/
- Result createResultFromBuildResult(AbstractBuildResultNotificationDTO buildResult, ProgrammingExerciseParticipation participation);
+ Result createResultFromBuildResult(BuildResultNotification buildResult, ProgrammingExerciseParticipation participation);
/**
* Extract the build log statistics from the BuildLogEntries and persist a BuildLogStatisticsEntry.
diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestCaseDTO.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestCaseDTO.java
index e4e15c9227bf..5ae570f5b2d5 100644
--- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestCaseDTO.java
+++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestCaseDTO.java
@@ -14,13 +14,13 @@
import com.fasterxml.jackson.annotation.Nulls;
import de.tum.cit.aet.artemis.assessment.domain.Feedback;
-import de.tum.cit.aet.artemis.programming.dto.TestCaseBaseDTO;
+import de.tum.cit.aet.artemis.programming.dto.TestCaseBase;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
public record TestCaseDTO(String name, String classname, double time, @JsonProperty("failures") @JsonSetter(nulls = Nulls.AS_EMPTY) List failures,
@JsonProperty("errors") @JsonSetter(nulls = Nulls.AS_EMPTY) List errors,
- @JsonProperty("successInfos") @JsonSetter(nulls = Nulls.AS_EMPTY) List successInfos) implements TestCaseBaseDTO {
+ @JsonProperty("successInfos") @JsonSetter(nulls = Nulls.AS_EMPTY) List successInfos) implements TestCaseBase {
@JsonIgnore
public boolean isSuccessful() {
@@ -28,12 +28,7 @@ public boolean isSuccessful() {
}
@Override
- public String getName() {
- return name;
- }
-
- @Override
- public List getTestMessages() {
+ public List testMessages() {
return extractMessage().map(Collections::singletonList).orElse(Collections.emptyList());
}
diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestResultsDTO.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestResultsDTO.java
index cd029f5adf5e..9bb2e13c588a 100644
--- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestResultsDTO.java
+++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ci/notification/dto/TestResultsDTO.java
@@ -4,7 +4,6 @@
import java.util.List;
import java.util.stream.Stream;
-import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
@@ -15,156 +14,64 @@
import de.tum.cit.aet.artemis.programming.domain.RepositoryType;
import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry;
-import de.tum.cit.aet.artemis.programming.dto.AbstractBuildResultNotificationDTO;
-import de.tum.cit.aet.artemis.programming.dto.BuildJobDTOInterface;
+import de.tum.cit.aet.artemis.programming.dto.BuildJobInterface;
+import de.tum.cit.aet.artemis.programming.dto.BuildResultNotification;
import de.tum.cit.aet.artemis.programming.dto.StaticCodeAnalysisReportDTO;
import de.tum.cit.aet.artemis.programming.service.ci.notification.BuildLogParseUtils;
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
// Note: due to limitations with inheritance, we cannot declare this as record, but we can use it in a similar way with final fields
-public class TestResultsDTO extends AbstractBuildResultNotificationDTO {
-
- private final int successful;
-
- private final int skipped;
-
- private final int errors;
-
- private final int failures;
-
- private final String fullName;
-
- private final List commits;
-
- private final List results;
-
- private final List staticCodeAnalysisReports;
-
- private final List