Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…enges-backend into feature#468v1
  • Loading branch information
Nuria-Fernandez committed Jul 23, 2024
2 parents f83a98f + 0d2bab6 commit 6d55d0e
Show file tree
Hide file tree
Showing 15 changed files with 774 additions and 206 deletions.

Large diffs are not rendered by default.

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions itachallenge-score/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-consul-discovery:4.0.2'
implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-bootstrap', version: '4.0.2'
implementation 'org.springframework.boot:spring-boot-starter-actuator:3.0.6'
implementation 'org.testcontainers:testcontainers:1.17.6'
implementation 'org.apache.httpcomponents:httpclient:4.5.13'
implementation 'org.testcontainers:junit-jupiter:1.16.3'
// Borrado docker-java ya que no es necesario con la versión actual de testcontainers

//testing
testImplementation platform('org.junit:junit-bom:5.9.1')
Expand All @@ -52,6 +56,7 @@ dependencies {
testImplementation group: 'io.projectreactor', name: 'reactor-test', version: '3.1.0.RELEASE'
testImplementation group: 'org.testcontainers', name: 'mongodb', version: '1.17.6'
testImplementation group: 'org.testcontainers', name: 'junit-jupiter', version: '1.17.6'

}


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package com.itachallenge.score.helper;

import lombok.NoArgsConstructor;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

@NoArgsConstructor
public class CodeValidator {

private static final String[] forbiddenLibraries = {
"java\\.lang\\.System",
"java\\.io\\.PrintStream",
"java\\.io\\.File",
"java\\.io\\.FileReader",
"java\\.io\\.FileWriter",
"java\\.io\\.BufferedReader",
"java\\.io\\.BufferedWriter",
// se puede agregar más librerías aquí
};

public static boolean isLibraryImportAllowed(String code) {
for (String lib : forbiddenLibraries) {
String importPattern = "import\\s+" + lib + ";";
Pattern pattern = Pattern.compile(importPattern);
Matcher matcher = pattern.matcher(code);
if (matcher.find()) {
return false; // Importación prohibida encontrada
}
}
return true;
}
}


Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.itachallenge.score.helper;

import lombok.NoArgsConstructor;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.images.builder.Transferable;
import org.testcontainers.utility.DockerImageName;

import java.io.IOException;

@NoArgsConstructor
public class DockerContainerHelper {

public static GenericContainer<?> createContainer(String image) {
GenericContainer<?> container = new GenericContainer<>(DockerImageName.parse(image));
container.start();
return container;
}

public static void stopContainer(GenericContainer<?> container) {
if (container != null) {
container.stop();
}
}

public static void executeCommand(GenericContainer<?> container, String... command) throws IOException, InterruptedException {
container.execInContainer(command);
}

public static void copyFileToContainer(GenericContainer<?> container, String content, String containerPath) {
container.copyFileToContainer(Transferable.of(content.getBytes()), containerPath);
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
package com.itachallenge.score.docker;

import com.itachallenge.score.helper.CodeValidator;
import com.itachallenge.score.helper.DockerContainerHelper;
import org.junit.jupiter.api.Test;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.junit.jupiter.Testcontainers;

import java.io.IOException;

import static org.junit.Assert.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

@Testcontainers
class DockerIntegrationTest {

@Test
void testJavaContainerSortNumbers() {

String codeSort = """
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
String numbers = "3,1,4,1,5,9";
int[] numArray = Arrays.stream(numbers.split(",")).mapToInt(Integer::parseInt).toArray();
Arrays.sort(numArray);
System.out.println(Arrays.toString(numArray));
}
}
""";

GenericContainer<?> containerJavaSort = DockerContainerHelper.createContainer("openjdk:11");

try {
DockerContainerHelper.copyFileToContainer(containerJavaSort, codeSort, "/app/Main.java");
DockerContainerHelper.executeCommand(containerJavaSort, "javac", "/app/Main.java");
String output = containerJavaSort.execInContainer("java", "-cp", "/app", "Main").getStdout().trim();


assertTrue(output.contains("[1, 1, 3, 4, 5, 9]"));
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
} finally {
DockerContainerHelper.stopContainer(containerJavaSort);
}
}

@Test
void testJavaContainerCompileError() {

String codeError = """
public class Main {
public static void main(String[] args) {
System.out.println("Esto no compila porque falta un paréntesis";
}
}
""";

GenericContainer<?> containerJavaError = DockerContainerHelper.createContainer("openjdk:11");

try {
DockerContainerHelper.copyFileToContainer(containerJavaError, codeError, "/app/Main.java");
DockerContainerHelper.executeCommand(containerJavaError, "javac", "/app/Main.java");
} catch (IOException | InterruptedException e) {
String errorMessage = e.getMessage();
assertTrue(errorMessage.contains("error: ';' expected"));
} finally {
DockerContainerHelper.stopContainer(containerJavaError);
}
}

@Test
void testRestrictedLibraryImport() {
String code = """
import java.lang.System;
public class Main {
public static void main(String[] args) {
System.out.println("Hola! Estoy intentado importar System");
}
}"
""";

String code2 = """
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("Hola! Estoy intentado importar Scanner");
}
}"
""";
assertFalse(CodeValidator.isLibraryImportAllowed(code)); // Debería devolver false al intentar importar java.lang.System
assertTrue(CodeValidator.isLibraryImportAllowed(code2)); // Debería devolver true al intentar importar java.util.Scanner

// Si el código no importa java.lang.System, entonces se intenta compilar
if (CodeValidator.isLibraryImportAllowed(code)) {
GenericContainer<?> container = DockerContainerHelper.createContainer("openjdk:11");

try {
DockerContainerHelper.copyFileToContainer(container, code, "/app/Main.java");
DockerContainerHelper.executeCommand(container, "javac", "/app/Main.java");

fail("Expected a compilation error, but the code compiled successfully");
} catch (IOException | InterruptedException e) {
String errorMessage = e.getMessage();
assertTrue(errorMessage.contains("error: package java.lang.System does not exist"));
} finally {
DockerContainerHelper.stopContainer(container);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.itachallenge.user.service.IServiceChallengeStatistics;
import com.itachallenge.user.service.IUserSolutionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.ArraySchema;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
Expand Down Expand Up @@ -174,4 +175,24 @@ public Mono<ResponseEntity<Map<String, String>>> getVersion() {
}


@GetMapping(path = "/{idUser}/challenges/solutions")
@Operation(
summary = "Retrieves all user challenges solutions and their status.",
description = "Retrieves all user-contributed solutions for all challenges and their status (whether they've finished completing them or not).",
responses = {
@ApiResponse(responseCode = "200", description = "Challenges retrieved successfully", content = {@Content(array = @ArraySchema(schema = @Schema(implementation = UserSolutionDto.class)), mediaType = "application/json")}),
@ApiResponse(responseCode = "400", description = "Invalid UUID for user"),
@ApiResponse(responseCode = "404", description = "User not found")
}
)
public Mono<ResponseEntity<List<UserSolutionDto>>> getAllSolutionsByIdUser(
@PathVariable("idUser") @GenericUUIDValid(message = "Invalid UUID for user") String idUser) {
UUID userUuid = UUID.fromString(idUser);

return userScoreService.showAllUserSolutions(userUuid)
.collectList()
.map(ResponseEntity::ok)
.defaultIfEmpty(ResponseEntity.notFound().build());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.itachallenge.user.document.UserSolutionDocument;
import com.itachallenge.user.dtos.UserScoreDto;
import com.itachallenge.user.dtos.UserSolutionDto;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;

Expand All @@ -22,4 +23,14 @@ private UserScoreDto toUserScoreDto(UserSolutionDocument userScoreDocument) {
.solutions(userScoreDocument.getSolutionDocument())
.build();
}

public Flux<UserSolutionDto> fromUserSolutionDocumentToUserSolutionDto(UserSolutionDocument document) {
return Flux.just(UserSolutionDto.builder()
.userId(document.getUserId().toString())
.challengeId(document.getChallengeId().toString())
.languageId(document.getLanguageId().toString())
.status(document.getStatus().toString())
.solutionText(document.getSolutionDocument().get(0).getSolutionText())
.build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,17 @@
import com.itachallenge.user.dtos.UserScoreDto;
import com.itachallenge.user.dtos.UserSolutionDto;
import com.itachallenge.user.dtos.UserSolutionScoreDto;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.UUID;

public interface IUserSolutionService {

Mono<SolutionUserDto<UserScoreDto>> getChallengeById(String id, String idChallenge, String idLanguage);
Mono<UserSolutionScoreDto> addSolution(UserSolutionDto userSolutionDto);
Mono<UserSolutionDocument> markAsBookmarked(String uuidChallenge, String uuidLanguage, String uuidUser, boolean bookmarked);
Flux<UserSolutionDto> showAllUserSolutions(UUID userUuid);

}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public Mono<SolutionUserDto<UserScoreDto>> getChallengeById(String idUser, Strin
.collectList()
.map(userScoreDtos -> {
SolutionUserDto<UserScoreDto> solutionUserDto = new SolutionUserDto<>();
solutionUserDto.setInfo(0, 1, 0, userScoreDtos.toArray(new UserScoreDto[0]));
int count = userScoreDtos.size();
solutionUserDto.setInfo(0, 1, count, userScoreDtos.toArray(new UserScoreDto[0]));
return solutionUserDto;
});
}
Expand Down Expand Up @@ -143,5 +144,10 @@ private ChallengeStatus determineChallengeStatus(String status) {
return challengeStatus;
}

public Flux<UserSolutionDto> showAllUserSolutions(UUID userUuid) {
return userSolutionRepository.findByUserId(userUuid)
.flatMap(converter::fromUserSolutionDocumentToUserSolutionDto);
}

}

2 changes: 1 addition & 1 deletion itachallenge-user/src/main/resources/application.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
spring:
application:
name: itachallenge-user
version: 2.1.0-RELEASE
version: 2.2.0-RELEASE
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
Expand Down
30 changes: 30 additions & 0 deletions itachallenge-user/src/test/java/com/itachallenge/user/controller/UserControllerTest.java
100755 → 100644
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.web.reactive.server.WebTestClient;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Arrays;
Expand Down Expand Up @@ -330,6 +331,35 @@ void getVersionTest() {
.jsonPath("$.version").isEqualTo("1.0-SNAPSHOT");
}

@Test
void getAllSolutionsByIdUser() {
String URI_TEST = "/{idUser}/challenges/solutions";
UUID userId = UUID.randomUUID();

UserSolutionDto userSolutionDto = UserSolutionDto.builder()
.userId(userId.toString())
.challengeId(UUID.randomUUID().toString())
.languageId(UUID.randomUUID().toString())
.status("STARTED")
.solutionText("Sample Solution")
.build();

when(userSolutionService.showAllUserSolutions(userId)).thenReturn(Flux.just(userSolutionDto));

webTestClient.get()
.uri(CONTROLLER_URL + URI_TEST, userId.toString())
.exchange()
.expectStatus().isOk()
.expectBodyList(UserSolutionDto.class)
.value(solutions -> {
assertNotNull(solutions);
assertEquals(1, solutions.size());
UserSolutionDto solution = solutions.get(0);
assertEquals(userId.toString(), solution.getUserId());
assertEquals("Sample Solution", solution.getSolutionText());
});
}

}


Expand Down
Loading

0 comments on commit 6d55d0e

Please sign in to comment.