diff --git "a/.github/workflows/\bcicd-dev.yml" "b/.github/workflows/\bcicd-dev.yml" new file mode 100644 index 0000000..b4c2487 --- /dev/null +++ "b/.github/workflows/\bcicd-dev.yml" @@ -0,0 +1,85 @@ +name: Deploy to Amazon ECS + +on: + push: + branches: [ "main" ] + +env: + AWS_REGION: ap-northeast-2 + ECR_REPOSITORY: edupi_gateway + ECS_SERVICE: gateway + ECS_CLUSTER: BE-fargate + ECS_TASK_DEFINITION: edupi_gateway.json + CONTAINER_NAME: gateway + +permissions: + contents: read + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + environment: production + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Copy Secret yml + env: + CREATE_SECRET: ${{secrets.EDUPI_GATEWAY_DEV_APPLICATION_YML}} + CREATE_SECRET_DIR: src/main/resources + CREATE_SECRET_DIR_FILE_NAME: application.yml + run: echo $CREATE_SECRET | base64 --decode > $CREATE_SECRET_DIR/$CREATE_SECRET_DIR_FILE_NAME + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle + run: ./gradlew build + + - name: Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY_ID }} + aws-region: ${{ env.AWS_REGION }} + + - name: Login to Amazon ECR + id: login-ecr + uses: aws-actions/amazon-ecr-login@v1 + + - name: Build, tag, and push image to Amazon ECR + id: build-image + env: + ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }} + IMAGE_TAG: latest + run: | + # Build a docker container and + # push it to ECR so that it can + # be deployed to ECS. + docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG . + docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG + echo "image=$ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG" >> $GITHUB_OUTPUT + + - name: Fill in the new image ID in the Amazon ECS task definition + id: task-def + uses: aws-actions/amazon-ecs-render-task-definition@v1 + with: + task-definition: ${{ env.ECS_TASK_DEFINITION }} + container-name: ${{ env.CONTAINER_NAME }} + image: ${{ steps.build-image.outputs.image }} + + - name: Deploy Amazon ECS task definition + uses: aws-actions/amazon-ecs-deploy-task-definition@v1 + with: + task-definition: ${{ steps.task-def.outputs.task-definition }} + service: ${{ env.ECS_SERVICE }} + cluster: ${{ env.ECS_CLUSTER }} + wait-for-service-stability: true diff --git a/.github/workflows/pull-request-build.yml b/.github/workflows/pull-request-build.yml new file mode 100644 index 0000000..ba281de --- /dev/null +++ b/.github/workflows/pull-request-build.yml @@ -0,0 +1,45 @@ +name: Pull Request build + +on: + pull_request: + branches: [ "develop" ] + +permissions: + contents: read + +jobs: + deploy: + name: Deploy + runs-on: ubuntu-latest + environment: production + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up JDK 17 + uses: actions/setup-java@v4 + with: + java-version: '17' + distribution: 'temurin' + + - name: Copy Secret yml + env: + CREATE_SECRET: ${{secrets.EDUPI_GATEWAY_DEV_APPLICATION_YML}} + CREATE_SECRET_DIR: src/main/resources + CREATE_SECRET_DIR_FILE_NAME: application.yml + run: echo $CREATE_SECRET | base64 --decode > $CREATE_SECRET_DIR/$CREATE_SECRET_DIR_FILE_NAME + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Test with Gradle + run: ./gradlew test + + - name: Cleanup Gradle Cache + if: ${{ always() }} + run: | + rm -f ~/.gradle/caches/modules-2/modules-2.lock + rm -f ~/.gradle/caches/modules-2/gc.properties + + diff --git a/.gitignore b/.gitignore index ed62916..e4a8b83 100644 --- a/.gitignore +++ b/.gitignore @@ -36,7 +36,7 @@ out/ ### VS Code ### .vscode/ -*.yml +src/main/resources/*.yml application.properties .DS_Store diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..ec9b4d2 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM openjdk:17 + +ARG JAR_FILE=build/libs/*.jar + +COPY ${JAR_FILE} app.jar + +ENTRYPOINT ["java","-jar","/app.jar"] \ No newline at end of file diff --git a/build.gradle b/build.gradle index bc785ef..2fe3a22 100644 --- a/build.gradle +++ b/build.gradle @@ -1,45 +1,50 @@ plugins { - id 'java' - id 'org.springframework.boot' version '3.3.2' - id 'io.spring.dependency-management' version '1.1.6' + id 'java' + id 'org.springframework.boot' version '3.3.2' + id 'io.spring.dependency-management' version '1.1.6' } group = 'soma.haeya' version = '0.0.1-SNAPSHOT' java { - toolchain { - languageVersion = JavaLanguageVersion.of(17) - } + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } } repositories { - mavenCentral() + mavenCentral() } ext { - set('springCloudVersion', "2023.0.3") + set('springCloudVersion', "2023.0.3") } dependencies { - // spring cloud gateway - implementation 'org.springframework.cloud:spring-cloud-starter-gateway' + implementation 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + // spring cloud gateway + implementation 'org.springframework.cloud:spring-cloud-starter-gateway' - // jjwt + // swagger + implementation 'org.springdoc:springdoc-openapi-starter-webflux-ui:2.0.2' + + // jjwt implementation 'io.jsonwebtoken:jjwt-api:0.12.3' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.3' runtimeOnly 'io.jsonwebtoken:jjwt-jackson:0.12.3' - testImplementation 'org.springframework.boot:spring-boot-starter-test' - testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } dependencyManagement { - imports { - mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" - } + imports { + mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" + } } tasks.named('test') { - useJUnitPlatform() + useJUnitPlatform() } diff --git a/edupi_gateway.json b/edupi_gateway.json new file mode 100644 index 0000000..e54a931 --- /dev/null +++ b/edupi_gateway.json @@ -0,0 +1,53 @@ +{ + "containerDefinitions": [ + { + "name": "gateway", + "image": "590184013289.dkr.ecr.ap-northeast-2.amazonaws.com/edupi_gateway:latest", + "cpu": 0, + "portMappings": [ + { + "name": "gateway-port", + "containerPort": 8080, + "hostPort": 8080, + "protocol": "tcp", + "appProtocol": "http" + } + ], + "essential": true, + "environment": [], + "environmentFiles": [], + "mountPoints": [], + "volumesFrom": [], + "ulimits": [], + "logConfiguration": { + "logDriver": "awslogs", + "options": { + "awslogs-group": "/ecs/edupi_gateway", + "mode": "non-blocking", + "awslogs-create-group": "true", + "max-buffer-size": "25m", + "awslogs-region": "ap-northeast-2", + "awslogs-stream-prefix": "ecs" + }, + "secretOptions": [] + }, + "systemControls": [] + } + ], + "family": "edupi_gateway", + "executionRoleArn": "arn:aws:iam::590184013289:role/ecsTaskExecutionRole", + "networkMode": "awsvpc", + "volumes": [], + "placementConstraints": [], + "requiresCompatibilities": [ + "EC2", + "FARGATE" + ], + "cpu": "512", + "memory": "1024", + "runtimePlatform": { + "cpuArchitecture": "X86_64", + "operatingSystemFamily": "LINUX" + }, + "tags": [] +} \ No newline at end of file diff --git a/src/main/java/soma/haeya/edupi_gateway/EdupiGatewayApplication.java b/src/main/java/soma/edupigateway/EdupiGatewayApplication.java similarity index 56% rename from src/main/java/soma/haeya/edupi_gateway/EdupiGatewayApplication.java rename to src/main/java/soma/edupigateway/EdupiGatewayApplication.java index 01f3584..06491dc 100644 --- a/src/main/java/soma/haeya/edupi_gateway/EdupiGatewayApplication.java +++ b/src/main/java/soma/edupigateway/EdupiGatewayApplication.java @@ -1,4 +1,4 @@ -package soma.haeya.edupi_gateway; +package soma.edupigateway; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @@ -6,8 +6,8 @@ @SpringBootApplication public class EdupiGatewayApplication { - public static void main(String[] args) { - SpringApplication.run(EdupiGatewayApplication.class, args); - } + public static void main(String[] args) { + SpringApplication.run(EdupiGatewayApplication.class, args); + } } diff --git a/src/main/java/soma/edupigateway/auth/TokenProvider.java b/src/main/java/soma/edupigateway/auth/TokenProvider.java new file mode 100644 index 0000000..86eb5d3 --- /dev/null +++ b/src/main/java/soma/edupigateway/auth/TokenProvider.java @@ -0,0 +1,43 @@ +package soma.edupigateway.auth; + +import io.jsonwebtoken.Claims; +import io.jsonwebtoken.JwtException; +import io.jsonwebtoken.Jwts; +import io.jsonwebtoken.io.Decoders; +import io.jsonwebtoken.security.Keys; +import java.util.Date; +import javax.crypto.SecretKey; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import soma.edupigateway.exception.UnAuthorizedException; + +@Component +public class TokenProvider { + + private final SecretKey key; + + public TokenProvider(@Value("${jwt.secret}") String secret) { + byte[] keyBytes = Decoders.BASE64.decode(secret); + this.key = Keys.hmacShaKeyFor(keyBytes); + } + + public Claims getClaimsJson(String token) { + Claims claims = extractClaimsFromJwt(token); + + if (claims.getExpiration().before(new Date())) { + throw new UnAuthorizedException(HttpStatus.UNAUTHORIZED, "토큰이 유효하지 않습니다."); + } + + return claims; + } + + private Claims extractClaimsFromJwt(String token) { + try { + return Jwts.parser().verifyWith(key).build().parseSignedClaims(token).getPayload(); + } catch (JwtException e) { + throw new UnAuthorizedException(HttpStatus.UNAUTHORIZED, "토큰이 유효하지 않습니다."); + } + } + +} diff --git a/src/main/java/soma/edupigateway/config/CorsConfig.java b/src/main/java/soma/edupigateway/config/CorsConfig.java new file mode 100644 index 0000000..88a386b --- /dev/null +++ b/src/main/java/soma/edupigateway/config/CorsConfig.java @@ -0,0 +1,43 @@ +package soma.edupigateway.config; + +import java.util.List; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.reactive.CorsConfigurationSource; +import org.springframework.web.cors.reactive.CorsWebFilter; +import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource; + +@Configuration +public class CorsConfig { + + @Bean + public CorsWebFilter corsWebFilter() { + return new CorsWebFilter(corsConfigurationSource()); + } + + @Bean + public CorsConfigurationSource corsConfigurationSource() { + CorsConfiguration configuration = new CorsConfiguration(); + configuration.setAllowedOrigins( + List.of( + "http://localhost:5000", + "http://d33notepxaalcd.cloudfront.net", + "https://d33notepxaalcd.cloudfront.net", + "http://edupi.co.kr", + "https://edupi.co.kr" + ) + ); + configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS")); + configuration.setAllowedHeaders( + List.of("Content-Type", "Authorization", "Accept", "Access-Control-Allow-Origin")); + configuration.setExposedHeaders(List.of("Content-Type")); + configuration.setAllowCredentials(true); + configuration.setMaxAge(3600L); + + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + source.registerCorsConfiguration("/**", configuration); + + return source; + } +} diff --git a/src/main/java/soma/edupigateway/config/RoutingConfig.java b/src/main/java/soma/edupigateway/config/RoutingConfig.java new file mode 100644 index 0000000..9c34a0e --- /dev/null +++ b/src/main/java/soma/edupigateway/config/RoutingConfig.java @@ -0,0 +1,79 @@ +package soma.edupigateway.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.gateway.route.RouteLocator; +import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import soma.edupigateway.filter.AuthenticationFilter; + +@Configuration +public class RoutingConfig { + + @Value("${server-url.assist.url}") + private String ASSIST_URL; + @Value("${server-url.assist.default-path}") + private String ASSIST_PATH; + + @Value("${server-url.visualize.url}") + private String VISUALIZE_URL; + @Value("${server-url.visualize.default-path}") + private String VISUALIZE_PATH; + + @Value("${server-url.meta.url}") + private String META_URL; + @Value("${server-url.meta.default-path}") + private String META_PATH; + + @Value("${server-url.user.url}") + private String USER_URL; + @Value("${server-url.user.default-path}") + private String USER_PATH; + + @Value("${server-url.lms.url}") + private String LMS_URL; + @Value("${server-url.lms.default-path}") + private String LMS_PATH; + + + @Bean + public RouteLocator gatewayRoutes(RouteLocatorBuilder builder, AuthenticationFilter authorizationFilter) { + return builder + .routes() + .route("root_route", r -> r.path("/health-check") + .filters(f -> f.setStatus(200)) + .uri("no://op") + ) // health check + + .route(predicate -> predicate + .path(ASSIST_PATH) + .uri(ASSIST_URL) + ) // edupi-assist + + .route(predicate -> predicate + .path(VISUALIZE_PATH) + .uri(VISUALIZE_URL) + ) // edupi-visualize + + .route(predicate -> predicate + .path(LMS_PATH) + .filters( + f -> f.filter(authorizationFilter.apply(config -> config.setRequiredRole("ROLE_USER"))) + ) + .uri(LMS_URL) + ) // edupi-lms + + .route(predicate -> predicate + .path(USER_PATH) + .uri(USER_URL) + ) // edupi-user + + .route(predicate -> predicate + .path(META_PATH) + .uri(META_URL) + ) // edupi-meta + + .build(); + } + +} diff --git a/src/main/java/soma/edupigateway/exception/GlobalExceptionHandler.java b/src/main/java/soma/edupigateway/exception/GlobalExceptionHandler.java new file mode 100644 index 0000000..4f613e0 --- /dev/null +++ b/src/main/java/soma/edupigateway/exception/GlobalExceptionHandler.java @@ -0,0 +1,20 @@ +package soma.edupigateway.exception; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; +import soma.edupigateway.models.ErrorResponse; + +@Slf4j +@RestControllerAdvice +public class GlobalExceptionHandler { + + @ExceptionHandler(UnAuthorizedException.class) + public ResponseEntity handleValidationExceptions(UnAuthorizedException ex) { + return ResponseEntity + .status(ex.getStatusCode()) + .body(new ErrorResponse(ex.getReason())); + } + +} diff --git a/src/main/java/soma/edupigateway/exception/UnAuthorizedException.java b/src/main/java/soma/edupigateway/exception/UnAuthorizedException.java new file mode 100644 index 0000000..da41d28 --- /dev/null +++ b/src/main/java/soma/edupigateway/exception/UnAuthorizedException.java @@ -0,0 +1,13 @@ +package soma.edupigateway.exception; + +import lombok.Getter; +import org.springframework.http.HttpStatus; +import org.springframework.web.server.ResponseStatusException; + +@Getter +public class UnAuthorizedException extends ResponseStatusException { + + public UnAuthorizedException(HttpStatus status, String message) { + super(status, message); + } +} diff --git a/src/main/java/soma/edupigateway/filter/AuthenticationFilter.java b/src/main/java/soma/edupigateway/filter/AuthenticationFilter.java new file mode 100644 index 0000000..9724696 --- /dev/null +++ b/src/main/java/soma/edupigateway/filter/AuthenticationFilter.java @@ -0,0 +1,64 @@ +package soma.edupigateway.filter; + +import io.jsonwebtoken.Claims; +import java.util.Optional; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cloud.gateway.filter.GatewayFilter; +import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory; +import org.springframework.http.HttpCookie; +import org.springframework.http.HttpStatus; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; +import org.springframework.util.MultiValueMap; +import soma.edupigateway.auth.TokenProvider; +import soma.edupigateway.exception.UnAuthorizedException; + +@Slf4j +@Component +public class AuthenticationFilter extends AbstractGatewayFilterFactory { + + private final TokenProvider jwtProvider; + + public AuthenticationFilter(TokenProvider jwtProvider) { + super(Config.class); + this.jwtProvider = jwtProvider; + } + + @Setter + @Getter + public static class Config { + + private String requiredRole; + + } + + @Override + public GatewayFilter apply(Config config) throws NullPointerException { + return ((exchange, chain) -> { + ServerHttpRequest request = exchange.getRequest(); + + MultiValueMap cookies = request.getCookies(); + + String token = extractToken(cookies); + + Claims claims = jwtProvider.getClaimsJson(token); + + ServerHttpRequest modifiedRequest = exchange.getRequest().mutate() + .header("X-Account-Id", claims.get("accountId", Long.class).toString()) + .build(); + + return chain.filter(exchange.mutate().request(modifiedRequest).build()); + }); + + } + + // cookies에서 token을 추출하는 메서드 + public String extractToken(MultiValueMap cookies) { + return Optional.ofNullable(cookies.getFirst("token")) + .map(HttpCookie::getValue) + .orElseThrow(() -> new UnAuthorizedException(HttpStatus.UNAUTHORIZED, "토큰이 없습니다.")); + } + +} diff --git a/src/main/java/soma/edupigateway/filter/RequestLoggingFilter.java b/src/main/java/soma/edupigateway/filter/RequestLoggingFilter.java new file mode 100644 index 0000000..e6ef6f8 --- /dev/null +++ b/src/main/java/soma/edupigateway/filter/RequestLoggingFilter.java @@ -0,0 +1,54 @@ +package soma.edupigateway.filter; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.cloud.gateway.filter.factory.rewrite.ModifyRequestBodyGatewayFilterFactory; +import org.springframework.core.Ordered; +import org.springframework.http.server.reactive.ServerHttpRequest; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +@Slf4j +@Component +@RequiredArgsConstructor +public class RequestLoggingFilter implements GlobalFilter, Ordered { + + private final ModifyRequestBodyGatewayFilterFactory modifyRequestBodyGatewayFilterFactory; + + private static void logRequest(final ServerHttpRequest request, final String body) { + + log.info("Request Id: {}, URI: {}, Headers: {}, QueryParams: {}, Body: {}", + request.getId(), + request.getURI(), + request.getHeaders(), + request.getQueryParams(), + body); + } + + @Override + public Mono filter(final ServerWebExchange exchange, final GatewayFilterChain chain) { + + return modifyRequestBodyGatewayFilterFactory + .apply(modifyRequestBodyGatewayFilterConfig()) + .filter(exchange, chain); + } + + @Override + public int getOrder() { + + return Ordered.HIGHEST_PRECEDENCE; + } + + private ModifyRequestBodyGatewayFilterFactory.Config modifyRequestBodyGatewayFilterConfig() { + + return new ModifyRequestBodyGatewayFilterFactory.Config() + .setRewriteFunction(String.class, String.class, (exchange, body) -> { + logRequest(exchange.getRequest(), body); + return Mono.justOrEmpty(body); + } + ); + } +} diff --git a/src/main/java/soma/edupigateway/filter/ServerConnectionExceptionFilter.java b/src/main/java/soma/edupigateway/filter/ServerConnectionExceptionFilter.java new file mode 100644 index 0000000..22fd240 --- /dev/null +++ b/src/main/java/soma/edupigateway/filter/ServerConnectionExceptionFilter.java @@ -0,0 +1,34 @@ +package soma.edupigateway.filter; + +import java.net.ConnectException; +import java.nio.charset.StandardCharsets; +import org.springframework.cloud.gateway.filter.GatewayFilterChain; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.core.io.buffer.DataBuffer; +import org.springframework.core.io.buffer.DataBufferFactory; +import org.springframework.http.HttpStatus; +import org.springframework.stereotype.Component; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +@Component +public class ServerConnectionExceptionFilter implements GlobalFilter { + + @Override + public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) { + return chain.filter(exchange) + .onErrorResume(ConnectException.class, throwable -> handleConnectException(exchange)); + } + + private Mono handleConnectException(ServerWebExchange exchange) { + // 응답 설정 + exchange.getResponse().setStatusCode(HttpStatus.SERVICE_UNAVAILABLE); + + String responseBody = "The backend server is unavailable."; + + DataBufferFactory dataBufferFactory = exchange.getResponse().bufferFactory(); + DataBuffer buffer = dataBufferFactory.wrap(responseBody.getBytes(StandardCharsets.UTF_8)); + + return exchange.getResponse().writeWith(Mono.just(buffer)); + } +} diff --git a/src/main/java/soma/edupigateway/models/ErrorResponse.java b/src/main/java/soma/edupigateway/models/ErrorResponse.java new file mode 100644 index 0000000..a485d1d --- /dev/null +++ b/src/main/java/soma/edupigateway/models/ErrorResponse.java @@ -0,0 +1,12 @@ +package soma.edupigateway.models; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class ErrorResponse { + + private final String message; + +} diff --git a/src/main/resources/.gitkeep b/src/main/resources/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/src/test/java/soma/haeya/edupi_gateway/EdupiGatewayApplicationTests.java b/src/test/java/soma/edupigateway/EdupiGatewayApplicationTests.java similarity index 69% rename from src/test/java/soma/haeya/edupi_gateway/EdupiGatewayApplicationTests.java rename to src/test/java/soma/edupigateway/EdupiGatewayApplicationTests.java index 19158b7..96fbaa8 100644 --- a/src/test/java/soma/haeya/edupi_gateway/EdupiGatewayApplicationTests.java +++ b/src/test/java/soma/edupigateway/EdupiGatewayApplicationTests.java @@ -1,4 +1,4 @@ -package soma.haeya.edupi_gateway; +package soma.edupigateway; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest; @@ -6,8 +6,8 @@ @SpringBootTest class EdupiGatewayApplicationTests { - @Test - void contextLoads() { - } + @Test + void contextLoads() { + } }