Verified Commit 13d3ce30 authored by Lukas Wiest's avatar Lukas Wiest 🚂
Browse files

refactor(rest): refactor UnitTestUpload to v2

- defined modocot URI for unittests and submissions
- directly expect a textfile, not an archive
- store config file and local repo copy in persistent place,
    instead of using Gitea

BREAKING CHANGE: file to be uploaded changed from zip to text
BREAKING CHANGE: no longer uses Gitea
parent 1547ce00
package de.hftstuttgart.rest.v1.unittest;
import de.hftstuttgart.config.ModocotProperties;
import de.hftstuttgart.utils.BackendUtil;
import de.hftstuttgart.utils.FileUtil;
import de.hftstuttgart.utils.GitTeaUtil;
import de.hftstuttgart.utils.JGitUtil;
import de.hftstuttgart.utils.UnzipUtil;
import io.gitea.model.Repository;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.eclipse.jgit.transport.PushResult;
import org.springframework.core.env.Environment;
import org.springframework.util.FileSystemUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
......@@ -17,13 +12,11 @@ import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.annotation.MultipartConfig;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.io.*;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Rest controller for anything related to the TEST files.
......@@ -34,93 +27,77 @@ import java.util.stream.Stream;
public class UnitTestUpload {
private static final Logger LOG = LogManager.getLogger(UnitTestUpload.class);
public final static String modocotTestConfigRegex = "^modocot::(.*)::(.*|none)::(.*|none)::(.*)$";
public final static String modocotDueConfigRegex = "^modocot::(.*)::(.*|none)::(.*|none)$";
private final ModocotProperties modocotProperties;
private final GitTeaUtil gitTeaUtil;
private final JGitUtil jGitUtil;
private final String assignmentBasePath;
public UnitTestUpload(ModocotProperties modocotProperties,
GitTeaUtil gitTeaUtil,
JGitUtil jGitUtil) {
this.modocotProperties = modocotProperties;
this.gitTeaUtil = gitTeaUtil;
public UnitTestUpload(Environment env, JGitUtil jGitUtil) {
this.jGitUtil = jGitUtil;
this.assignmentBasePath =
modocotProperties.getModocotParentDirectory()
+ File.separator
+ modocotProperties.getModocotAssignmentFolderPrefix();
Path p = Paths.get(env.getProperty("modocot.dir"), env.getProperty("modocot.dir.test.folder.name"));
this.assignmentBasePath = p.toAbsolutePath().toString();
}
/**
* Create a subfolder for the specific assignment.
* This is called when the teacher creates an assignment and uploads the JUnit test files
*
* @param unitTestFileRef The zip file which contains the JUnit tests
* @param unitTestFileRef The text file which contains the JUnit tests meta data
* @param assignmentId ID of the created assignment. Generated by Moodle
*/
@RequestMapping(method = RequestMethod.POST)
public void uploadUnitTestFile(@RequestParam("unitTestFile") MultipartFile unitTestFileRef, @RequestParam("assignmentId") String assignmentId) throws IOException {
// path to the directory in which we will be working
String subFolderPath = this.assignmentBasePath + assignmentId;
LOG.info("work-directory: " + subFolderPath);
// creating the work-directory
File workDirectory = new File(subFolderPath);
workDirectory.mkdirs();
// creating the file which the unitTestFileRef will be transferred into
File file = new File(subFolderPath, String.valueOf(UUID.randomUUID()));
// transferring MultipartFile into temporary file
public void uploadUnitTestFile(
@RequestParam("unitTestFile") MultipartFile unitTestFileRef,
@RequestParam("assignmentId") String assignmentId
) throws IOException {
LOG.info("received new assignment");
File file = Paths.get(
this.assignmentBasePath,
assignmentId + ".txt")
.toFile();
file.mkdirs();
// save assignment config
unitTestFileRef.transferTo(file);
// unzipping temporary file to work-directory
List<File> zipFiles = UnzipUtil.unzip(file);
// unzipping temporary file (not needed anymore)
if (file.exists()) file.delete();
// check if any extracted files are named repo.txt
if (zipFiles.stream().anyMatch(zipFile -> zipFile.getName().equalsIgnoreCase("repo.txt"))) {
// reading all from the repo.txt
List<String> lines = BackendUtil.extractLinesFromRepoFile(zipFiles, "repo.txt");
// either set the first line of repo.txt or ""
String repoUrl = (lines.size() > 0 && !lines.get(0).equals("")) ?
lines.get(0) : "";
LOG.info("repoUrl: " + repoUrl);
// either set the second line of repo.txt or null
String credentials = ((lines.size() > 1) && !lines.get(1).equals("")) ?
lines.get(1) : null;
LOG.info("credentials: " + credentials);
// cloning repository repoUrl into work-directory, using credentials from second line in repo.txt
this.jGitUtil.cloneRepository(repoUrl, workDirectory, credentials, credentials != null,true);
}
Stream.of(Objects.requireNonNull(workDirectory.listFiles()))
.forEach(fi -> System.out.println(fi.getAbsolutePath()));
if(this.gitTeaUtil.repositoryExists(assignmentId)) {
this.gitTeaUtil.deleteRepository(assignmentId);
LOG.debug(String.format("saved config file to: %s", file.getAbsolutePath()));
Pattern pattern = Pattern.compile(this.modocotTestConfigRegex);
Matcher config = null;
LOG.debug("reading test configuration file");
// open saved config in a try-with
try (BufferedReader br = new BufferedReader(
new InputStreamReader(
new FileInputStream(file)))) {
String line;
// search for a modocot URI while none is found and there are lines left
while (config == null && (line = br.readLine()) != null) {
Matcher matcher = pattern.matcher(line);
if (matcher.matches()) {
LOG.debug(String.format("found modocot test line: %s", line));
config = matcher;
}
}
} catch (IOException e) {
LOG.error("Error while reading repo config", e);
}
// creating repository on internal giTea, name = assignmentId
Repository repo = this.gitTeaUtil.createRepository(assignmentId);
System.out.println(repo);
// giTea runs in docker and returns CloneUrl as localhost, replacing localhost with docker-host ip
repo.setCloneUrl(repo.getCloneUrl().replace("localhost", modocotProperties.getDockerHostIp()));
// committing everything in work-directory and push to created repository
Iterable<PushResult> pushResults = this.jGitUtil.commitAllAndPush(workDirectory, repo, false);
if (pushResults != null) {
for (PushResult pushResult : pushResults) {
LOG.info("Push-Result: " + pushResult.getMessages());
finally {
if (config == null) {
throw new RuntimeException("couldn't find repo config for unittest clone");
}
}
// deleting created work-directory
FileUtil.deleteFolderRecursively(workDirectory);
LOG.info("Uploaded unit test file: " + workDirectory);
LOG.debug("calling test repo clone");
// cloning assignment repo to persistent space
jGitUtil.cloneRepository(
config,
Paths.get(this.assignmentBasePath, assignmentId).toAbsolutePath().toString());
LOG.info(String.format("stored new assignment: %s", file.getAbsolutePath()));
}
/**
......@@ -133,10 +110,21 @@ public class UnitTestUpload {
*/
@RequestMapping(method = RequestMethod.DELETE)
public void deleteUnitTestFiles(@RequestParam("assignmentId") String assignmentId) {
String path = this.assignmentBasePath + assignmentId;
File dir = new File(path);
if (dir.exists()) {
FileUtil.deleteFolderRecursively(dir);
}
LOG.info(String.format("received deletion order for assignment %s", assignmentId));
// deleting config file
File file = Paths.get(
this.assignmentBasePath,
assignmentId + ".txt")
.toFile();
file.delete();
// deleting local copy of repository
file = Paths.get(
this.assignmentBasePath,
assignmentId).toFile();
FileSystemUtils.deleteRecursively(file);
LOG.info(String.format("assignment %s deletion complete", assignmentId));
}
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment