diff --git a/src/main/java/at/ac/uibk/gitsearch/es/model/ArtemisExerciseInfo.java b/src/main/java/at/ac/uibk/gitsearch/es/model/ArtemisExerciseInfo.java index 4a74607e2734053d2cfa0feb774c7fc8494d7daf..1211e6228868d1bac033cb115665df61f3f65024 100644 --- a/src/main/java/at/ac/uibk/gitsearch/es/model/ArtemisExerciseInfo.java +++ b/src/main/java/at/ac/uibk/gitsearch/es/model/ArtemisExerciseInfo.java @@ -63,19 +63,36 @@ public class ArtemisExerciseInfo extends ExerciseInfo { exerciseInfo.setType(this.getType()); exerciseInfo.setTitle(this.getTitle()); exerciseInfo.setLicense(this.getLicense()); - exerciseInfo.setKeyword(this.getKeyword()); - exerciseInfo.setFormat(this.getFormat()); - exerciseInfo.setStructure(this.getStructure()); - exerciseInfo.setProgrammingLanguage(this.getProgrammingLanguage()); - exerciseInfo.setLanguage(this.getLanguage()); - exerciseInfo.setDifficulty(this.getDifficulty()); + if (this.getDescription() != null) { + exerciseInfo.setDescription(this.getDescription()); + } + if (this.getKeyword() != null) { + exerciseInfo.setKeyword(this.getKeyword()); + } + if (this.getFormat() != null) { + exerciseInfo.setFormat(this.getFormat()); + } + if (this.getStructure() != null) { + exerciseInfo.setStructure(this.getStructure()); + } + if (this.getProgrammingLanguage() != null) { + exerciseInfo.setProgrammingLanguage(this.getProgrammingLanguage()); + } + if (this.getLanguage() != null) { + exerciseInfo.setLanguage(this.getLanguage()); + } + if (this.getDifficulty() != null) { + exerciseInfo.setDifficulty(this.getDifficulty()); + } if (this.getCreator() != null) { exerciseInfo.setCreator(List.copyOf(this.getCreator())); } if (this.getContributor() != null) { exerciseInfo.setContributor(List.copyOf(this.getContributor())); } - exerciseInfo.setSource(this.getSource()); + if (this.getSource() != null) { + exerciseInfo.setSource(this.getSource()); + } return exerciseInfo; } diff --git a/src/main/java/at/ac/uibk/gitsearch/es/model/ExerciseInfo.java b/src/main/java/at/ac/uibk/gitsearch/es/model/ExerciseInfo.java index e1c79930d2ea56a46347f3d84136e767d1291af0..a60454b3dd61ac8cc28529115d502c0cc554ce82 100644 --- a/src/main/java/at/ac/uibk/gitsearch/es/model/ExerciseInfo.java +++ b/src/main/java/at/ac/uibk/gitsearch/es/model/ExerciseInfo.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.annotation.*; import java.io.IOException; import java.util.List; +@JsonInclude(JsonInclude.Include.NON_NULL) public class ExerciseInfo { private String metadataVersion; @@ -16,6 +17,8 @@ public class ExerciseInfo { private String license; + private String description; + private List<String> keyword; private List<String> format; @@ -71,6 +74,14 @@ public class ExerciseInfo { this.license = license; } + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + public List<String> getKeyword() { return keyword; } @@ -164,22 +175,35 @@ public class ExerciseInfo { '}'; } - @JsonFormat + @JsonFormat(with = JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES) public enum ExerciseType {PROGRAMMING_EXERCISE("programming exercise"), EXERCISE("exercise"), COLLECTION("collection"), OTHER("other"); ExerciseType(String s) { } + @JsonValue + public String jacksonCustomMapping() { + return name().toLowerCase().replace("_", " "); + } + @Override public String toString() { return name().toLowerCase().replace("_", " "); } } + @JsonFormat(with = JsonFormat.Feature.ACCEPT_CASE_INSENSITIVE_PROPERTIES) public enum ExerciseDifficulty { - SIMPLE, - MEDIUM, - ADVANCED, - @JsonEnumDefaultValue NONE; + SIMPLE("simple"), + MEDIUM("medium"), + ADVANCED("advanced"), + @JsonEnumDefaultValue NONE("none"); + + ExerciseDifficulty(String s) {} + + @JsonValue + public String jacksonCustomMapping() { + return name().toLowerCase(); + } @Override public String toString() { diff --git a/src/main/java/at/ac/uibk/gitsearch/service/ExerciseService.java b/src/main/java/at/ac/uibk/gitsearch/service/ExerciseService.java index a5906e4e71fafaef5b1645c20295637c1eb9b7d3..c4047893e97cbe5f7d3070b627a1a68e2a9cae78 100644 --- a/src/main/java/at/ac/uibk/gitsearch/service/ExerciseService.java +++ b/src/main/java/at/ac/uibk/gitsearch/service/ExerciseService.java @@ -4,8 +4,11 @@ import at.ac.uibk.gitsearch.config.ApplicationProperties; import at.ac.uibk.gitsearch.es.model.ArtemisExerciseInfo; import at.ac.uibk.gitsearch.es.model.ExerciseInfo; import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator; +import org.apache.commons.io.FileUtils; import org.eclipse.jgit.api.errors.GitAPIException; import org.eclipse.jgit.lib.Repository; import org.gitlab4j.api.GitLabApiException; @@ -63,7 +66,13 @@ public class ExerciseService { } /** - * Import exercise to Gitlab + * Import exercise to Gitlab. + * Sequentially executes following processes: + * - Apply user defined exercise metadata + * - Create repository in Gitlab's group defined by the user + * - Locally checking out the new created repository + * - Copy, commit and push files to the new repository + * * @param exerciseInfo of the exercise to import * @param exerciseToken of the stored exercise (in '/tmp' directory) to import */ @@ -74,6 +83,8 @@ public class ExerciseService { String repoUrl = gitlabService.createRepository(group, exerciseInfo.getTitle()); Repository repo = gitlabService.checkoutRepo(repoUrl); gitlabService.commitAndPushToRepo(repo, new File("tmp", exerciseToken)); + + FileUtils.deleteDirectory(new File("tmp", exerciseToken)); } /** @@ -83,7 +94,8 @@ public class ExerciseService { */ public ArtemisExerciseInfo getArtemisExerciseInfo(String exerciseToken) throws IOException { ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); - mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING); + mapper.enable(DeserializationFeature.READ_ENUMS_USING_TO_STRING) + .enable(MapperFeature.ACCEPT_CASE_INSENSITIVE_ENUMS); ArtemisExerciseInfo artemisExerciseInfo = mapper.readValue(new File("tmp/" + exerciseToken, "artemis.yaml"), ArtemisExerciseInfo.class); ExerciseInfo exerciseInfo = mapper.readValue(new File("tmp/" + exerciseToken, "metadata.yaml"), ExerciseInfo.class); artemisExerciseInfo.appendExerciseInfo(exerciseInfo); @@ -132,7 +144,7 @@ public class ExerciseService { fos.close(); } } - fileService.scheduleForDeletion(tmpDir.toPath(), 5); + fileService.scheduleForDeletion(tmpDir.toPath(), 60); } /** @@ -142,8 +154,10 @@ public class ExerciseService { * @throws IOException */ private void applyExerciseInfoChanges(ArtemisExerciseInfo exerciseInfo, String exerciseToken) throws IOException { - ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); - mapper.writeValue(new File("/tmp/" + exerciseToken, "metadata.yaml"), exerciseInfo.getExerciseInfoOnly()); + ObjectMapper mapper = new ObjectMapper(new YAMLFactory() + .enable(YAMLGenerator.Feature.INDENT_ARRAYS) + .disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER)); + mapper.writeValue(new File("tmp/" + exerciseToken, "metadata.yaml"), exerciseInfo.getExerciseInfoOnly()); } /** diff --git a/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java b/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java index 78b671fbf9786fa5600990f837891908e2abb660..43da23e0dfa9dee02194591bf4bfc3f384ada225 100644 --- a/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java +++ b/src/main/java/at/ac/uibk/gitsearch/service/GitlabService.java @@ -54,12 +54,15 @@ public class GitlabService { protected final TokenProvider tokenProvider; + private final FileService fileService; + private final Logger log = LoggerFactory.getLogger(GitlabService.class); - public GitlabService(GitLabRepository gitLabRepository, MetaDataRepository metaDataRepository, TokenProvider tokenProvider) { + public GitlabService(GitLabRepository gitLabRepository, MetaDataRepository metaDataRepository, TokenProvider tokenProvider, FileService fileService) { this.gitLabRepository = gitLabRepository; this.metaDataRepository = metaDataRepository; this.tokenProvider = tokenProvider; + this.fileService = fileService; } public Boolean repositoryExists(String projectID) { @@ -235,6 +238,8 @@ public class GitlabService { Git git = Git.cloneRepository().setURI(repoUrl).setDirectory(dst).call(); Repository repo = git.getRepository(); git.close(); + + fileService.scheduleForDeletion(repo.getDirectory().toPath(), 60); return repo; } catch (GitAPIException e) { diff --git a/src/main/java/at/ac/uibk/gitsearch/web/rest/ExerciseResource.java b/src/main/java/at/ac/uibk/gitsearch/web/rest/ExerciseResource.java index 35bcd0ce3e7343683477fa4b1543e30aef0b2064..fde06679e6d52b37ada7a3a5c99832ab59428498 100644 --- a/src/main/java/at/ac/uibk/gitsearch/web/rest/ExerciseResource.java +++ b/src/main/java/at/ac/uibk/gitsearch/web/rest/ExerciseResource.java @@ -8,8 +8,8 @@ import java.net.URISyntaxException; import java.net.URL; import java.util.Optional; -import at.ac.uibk.gitsearch.config.ApplicationProperties; import at.ac.uibk.gitsearch.es.model.ArtemisExerciseInfo; +import at.ac.uibk.gitsearch.security.SecurityUtils; import at.ac.uibk.gitsearch.service.ArtemisImportError; import at.ac.uibk.gitsearch.service.ExerciseService; import org.eclipse.jgit.api.errors.GitAPIException; @@ -30,8 +30,6 @@ import at.ac.uibk.gitsearch.web.util.HeaderUtil; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.core.Response; -import static javax.ws.rs.core.Response.ok; - /** * this resource should give access to various aspects of an exercise or course * @author Michael Breu @@ -113,7 +111,11 @@ public class ExerciseResource { */ @GetMapping("/exercise/imported-exercise-info/{exerciseToken}") public ResponseEntity<ArtemisExerciseInfo> getImportedExerciseInfo(@PathVariable("exerciseToken") String exerciseToken) throws IOException { - return ResponseEntity.ok(exerciseService.getArtemisExerciseInfo(exerciseToken)); + if (SecurityUtils.isAuthenticated()) { + return ResponseEntity.ok(exerciseService.getArtemisExerciseInfo(exerciseToken)); + } else { + return ResponseEntity.status(401).body(null); + } } /** @@ -131,10 +133,19 @@ public class ExerciseResource { public Response importExercise(@RequestBody() ArtemisExerciseInfo exerciseInfo, @PathVariable("exerciseToken") String exerciseToken, @PathVariable("gitlabGroupId") Integer gitlabGroupId) throws GitLabApiException, GitAPIException, IOException { - exerciseService.importExercise(exerciseInfo, exerciseToken, gitlabGroupId); - return Response.ok().build(); + if (SecurityUtils.isAuthenticated()) { + exerciseService.importExercise(exerciseInfo, exerciseToken, gitlabGroupId); + return Response.ok().build(); + } else { + return Response.status(401).build(); + } } + /** + * Utility method used to retrieve the Base URL from a HTTP request + * @param request the request to extract the Url from + * @return String of the extracted Base URL + */ private static String getBaseUrl(HttpServletRequest request) { String scheme = request.getScheme() + "://"; String serverName = request.getServerName();