diff --git a/.github/workflows/cd-dev.yml b/.github/workflows/cd-dev.yml index 9b0e9fa9..7ccad43f 100644 --- a/.github/workflows/cd-dev.yml +++ b/.github/workflows/cd-dev.yml @@ -6,14 +6,14 @@ on: branches: [ develop ] jobs: - build-and-push-and-deploy: + build-and-push: runs-on: ubuntu-22.04 steps: - - name: Checkout + - name: βœ… Checkout uses: actions/checkout@v3 - - name: Set up JDK 17 + - name: βš™οΈ Set up JDK 17 uses: actions/setup-java@v3 with: java-version: 17 @@ -27,16 +27,6 @@ jobs: aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} aws-region: ${{ secrets.AWS_REGION }} - - name: Grant execute permission for gradlew - run: chmod +x ./gradlew - shell: bash - - - name: Create Property - run: | - - mkdir -p ./operation-api/src/main/resources/static - echo "${{ secrets.APPLE_KEY }}" | base64 --decode > ./operation-api/src/main/resources/static/${{ secrets.APPLE_KEY_NAME }} - - name: 🧱 Build Image and Push to ECR env: AWS_ACCOUNT_ID: ${{ secrets.AWS_ACCOUNT_ID }} @@ -47,36 +37,73 @@ jobs: docker tag $AWS_ECR_REPO:latest public.ecr.aws/$AWS_ACCOUNT_ID/$AWS_ECR_REPO:latest docker push public.ecr.aws/$AWS_ACCOUNT_ID/$AWS_ECR_REPO:latest - - name: πŸ“ Copy Script File + deploy: + needs: build-and-push + runs-on: ubuntu-latest + + steps: + - name: βœ… Checkout + uses: actions/checkout@v3 + + - name: πŸ”’ Configure AWS credentials + uses: aws-actions/configure-aws-credentials@v1 + with: + aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY }} + aws-secret-access-key: ${{ secrets.AWS_SECRET_KEY }} + aws-region: ${{ secrets.AWS_REGION }} + + - name: πŸ“ Copy Files from S3 env: REGION: ${{ secrets.AWS_REGION }} + APPLE_KEY_NAME: ${{ secrets.APPLE_KEY_NAME }} S3_BUCKET: ${{ secrets.AWS_BUCKET_NAME }} run: | - mkdir ./script aws s3 cp --region $REGION \ - s3://$S3_BUCKET/dev/script/deploy.sh ./script/deploy.sh + s3://$S3_BUCKET/dev/script/deploy.sh ./deploy.sh aws s3 cp --region $REGION \ - s3://$S3_BUCKET/dev/script/switch.sh ./script/switch.sh + s3://$S3_BUCKET/dev/script/switch.sh ./switch.sh aws s3 cp --region $REGION \ - s3://$S3_BUCKET/dev/script/valid.sh ./script/valid.sh - + s3://$S3_BUCKET/dev/script/valid.sh ./valid.sh + + aws s3 cp --region $REGION \ + s3://$S3_BUCKET/dev/dev.env ./application.env aws s3 cp --region $REGION \ - s3://$S3_BUCKET/dev/dev.env application.env + s3://$S3_BUCKET/dev/static/$APPLE_KEY ./$APPLE_KEY_NAME - - name: Make zip file - run: zip -r ./$GITHUB_SHA.zip ./script ./appspec.yml ./application.env - shell: bash + - name: πŸ”„ Transfer Files to Server + uses: appleboy/scp-action@master + with: + host: ${{ secrets.HOST_DEV }} + username: ubuntu + key: ${{ secrets.PEM_KEY_DEV }} + port: 22 + source: "deploy.sh,switch.sh,valid.sh,application.env,${{ secrets.APPLE_KEY_NAME }}" + target: /home/ubuntu/deploy-temp/op - - name: Upload Property Zip to S3 and Create Code Deploy - env: - REGION: ${{ secrets.AWS_REGION }} - DEPLOY_APPLICATION: ${{ secrets.AWS_DEPLOY_APPLICATION }} - DEPLOY_GROUP: ${{ secrets.AWS_DEPLOY_GROUP_DEV }} - S3_BUCKET: ${{ secrets.AWS_BUCKET_NAME }} - run: | - aws s3 cp --region $REGION ./$GITHUB_SHA.zip s3://$S3_BUCKET/dev/deploy/$GITHUB_SHA.zip - - aws deploy create-deployment --application-name $DEPLOY_APPLICATION \ - --deployment-config-name CodeDeployDefault.AllAtOnce \ - --deployment-group-name $DEPLOY_GROUP \ - --s3-location bucket=$S3_BUCKET,bundleType=zip,key=dev/deploy/$GITHUB_SHA.zip \ No newline at end of file + - name: πŸš€SSH command deploy + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.HOST_DEV }} + username: ubuntu + key: ${{ secrets.PEM_KEY_DEV }} + port: 22 + script: | + mkdir -p /home/ubuntu/script/op + mkdir -p /home/ubuntu/property/op + mkdir -p /home/ubuntu/env/op + + sudo mv /home/ubuntu/deploy-temp/op/deploy.sh /home/ubuntu/script/op/deploy.sh + sudo mv /home/ubuntu/deploy-temp/op/switch.sh /home/ubuntu/script/op/switch.sh + sudo mv /home/ubuntu/deploy-temp/op/valid.sh /home/ubuntu/script/op/valid.sh + sudo mv /home/ubuntu/deploy-temp/op/application.env /home/ubuntu/env/op/application.env + sudo mv /home/ubuntu/deploy-temp/op/${{ secrets.APPLE_KEY_NAME }} /home/ubuntu/property/op/key/${{ secrets.APPLE_KEY_NAME }} + + sudo chmod +x /home/ubuntu/script/op/deploy.sh + sudo chmod +x /home/ubuntu/script/op/switch.sh + sudo chmod +x /home/ubuntu/script/op/valid.sh + sudo chmod +r /home/ubuntu/env/op/application.env + sudo chmod +r /home/ubuntu/property/op/key/${{ secrets.APPLE_KEY }} + + /home/ubuntu/script/op/deploy.sh + /home/ubuntu/script/op/switch.sh + /home/ubuntu/script/op/valid.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 321cbbac..b8553f67 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -23,28 +23,6 @@ jobs: run: chmod +x ./gradlew shell: bash - - name: Create application.yml - run: | - pwd - cd ./operation-api/src/main/resources - touch ./application-dev.yml - echo "${{ secrets.APPLICATION_DEV }}" >> ./application-dev.yml - cat ./application-dev.yml - - - name: Create application-test.yml - run: | - cd ./operation-domain/src/test - mkdir -p resources - touch ./resources/application-test.yml - echo "${{ secrets.APPLICATION_DOMAIN_TEST }}" >> ./application-test.yml - cat ./application-test.yml - - - name: 'Get key from Github Secrets' - run: | - pwd - mkdir -p ./operation-api/src/main/resources/static - echo "${{ secrets.APPLE_KEY }}" | base64 --decode > ./operation-api/src/main/resources/static/${{ secrets.APPLE_KEY_NAME }} - - - name: Build with Gradle - run: ./gradlew build + - name: 🧱 Build with Gradle + run: docker build -t app-ci . shell: bash \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index b6b4226c..3300b65e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,7 @@ FROM openjdk:17-jdk-slim as builder +ARG PROFILE=test + # mkdir /app-build && cd /app-build WORKDIR /app-build @@ -7,7 +9,7 @@ WORKDIR /app-build COPY . /app-build # create .jar -RUN ./gradlew build -x test +RUN echo "Build with PROFILE=${PROFILE}" && ./gradlew build -Pprofile=${PROFILE} --no-daemon # Run-Time Image Setting FROM openjdk:17-jdk-slim as production diff --git a/operation-api/build.gradle b/operation-api/build.gradle index 9a8c28a3..47a515af 100644 --- a/operation-api/build.gradle +++ b/operation-api/build.gradle @@ -19,6 +19,19 @@ tasks.named("bootJar") { }) } +def profile = project.findProperty("profile") ?: "test" +println("Build Profile: $profile") + +tasks.register("processProfileYaml", Copy) { + from("src/main/resources/application-${profile}.yaml") + into("build/resources/main") // λΉŒλ“œ μ‹œ μ‚¬μš©ν•  λ¦¬μ†ŒμŠ€ 경둜 + rename { "application.yaml" } // λͺ¨λ“  ν”„λ‘œνŒŒμΌ νŒŒμΌμ„ application.yaml둜 λ³€κ²½ +} + +// processResources μž‘μ—… 후에 μ‹€ν–‰λ˜λ„λ‘ μ˜μ‘΄μ„± μΆ”κ°€ +tasks.named("processResources") { + dependsOn("processProfileYaml") +} dependencies { // module implementation project(path: ':operation-auth') diff --git a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApiController.java b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApiController.java index 0232a8cf..d18d9d23 100644 --- a/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApiController.java +++ b/operation-api/src/main/java/org/sopt/makers/operation/web/banner/api/BannerApiController.java @@ -72,10 +72,10 @@ public ResponseEntity> getExternalBanners( @Override @GetMapping("/img/pre-signed") public ResponseEntity> getIssuedPreSignedUrlForPutImage( - @RequestParam("content-name") String contentName, - @RequestParam("image-type") String imageType, - @RequestParam("image-extension") String imageExtension, - @RequestParam("content-type") String contentType + @RequestParam("content_name") String contentName, + @RequestParam("image_type") String imageType, + @RequestParam("image_extension") String imageExtension, + @RequestParam("content_type") String contentType ) { val response = bannerService.getIssuedPreSignedUrlForPutImage(contentName, imageType, imageExtension, contentType); diff --git a/operation-api/src/test/java/org/sopt/makers/operation/web/banner/api/BannerApiControllerTest.java b/operation-api/src/test/java/org/sopt/makers/operation/web/banner/api/BannerApiControllerTest.java index c58d10a7..1790714f 100644 --- a/operation-api/src/test/java/org/sopt/makers/operation/web/banner/api/BannerApiControllerTest.java +++ b/operation-api/src/test/java/org/sopt/makers/operation/web/banner/api/BannerApiControllerTest.java @@ -1,17 +1,16 @@ package org.sopt.makers.operation.web.banner.api; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.DisplayName; +import com.fasterxml.jackson.databind.*; +import org.junit.jupiter.api.*; import org.sopt.makers.operation.code.success.web.BannerSuccessCode; import org.sopt.makers.operation.filter.JwtAuthenticationFilter; import org.sopt.makers.operation.filter.JwtExceptionFilter; import org.sopt.makers.operation.jwt.JwtTokenProvider; +import org.sopt.makers.operation.web.banner.dto.request.BannerRequest.*; import org.sopt.makers.operation.web.banner.dto.response.BannerResponse; import org.sopt.makers.operation.web.banner.service.BannerService; -import org.springframework.http.MediaType; import org.springframework.test.web.servlet.MockMvc; import org.springframework.context.annotation.FilterType; import org.springframework.context.annotation.ComponentScan; @@ -23,12 +22,15 @@ import java.security.Principal; import java.time.LocalDate; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import static org.springframework.http.MediaType.APPLICATION_JSON; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; @@ -45,6 +47,8 @@ class BannerApiControllerTest { private BannerService bannerService; @Autowired private MockMvc mockMvc; + @Autowired + ObjectMapper objectMapper; @BeforeEach void setMockBanner() { @@ -89,7 +93,7 @@ void getBannerDetail() throws Exception { @DisplayName("(DELETE) Banner Delete") void deleteBanner() throws Exception { //given - BannerResponse.BannerDetail mockBannerDetail = bannerService.getBannerDetail(MOCK_BANNER_ID); + doNothing().when(bannerService).deleteBanner(MOCK_BANNER_ID); this.mockMvc.perform( //when @@ -100,7 +104,6 @@ void deleteBanner() throws Exception { //then .andExpect(status().isNoContent()) .andExpect(jsonPath("$.success").value("true")); - } @Test @@ -121,4 +124,39 @@ void getExternalBanners() throws Exception { .andExpect(status().isOk()) .andExpect(jsonPath("$.success").value("true")); } + + @Nested + class CreateBannerTests { + @Test + @DisplayName("(POST) New Banner") + void createNewBanner() throws Exception { + // given + BannerCreate bannerCreate = new BannerCreate("pg_community", "product", "publisher", + LocalDate.of(2024, 1, 1), LocalDate.of(2024, 12, 31), "link", "image-url-pc", "image-url-mobile"); + BannerResponse.BannerDetail mockBannerDetail = new BannerResponse.BannerDetail( + MOCK_BANNER_ID, "in_progress", "pg_community", "product", "publisher", "link", + LocalDate.of(2024, 1, 1), LocalDate.of(2024, 12, 31), "image-url-pc", "image-url-mobile"); + String request = objectMapper.writeValueAsString(bannerCreate); + when(bannerService.createBanner(any(BannerCreate.class))).thenReturn(mockBannerDetail); + + // when + mockMvc.perform(post("/api/v1/banners") + .contentType(APPLICATION_JSON) + .content(request)) + // then + .andExpect(status().isCreated()) + .andExpect(jsonPath("$.success").value(true)) + .andExpect(jsonPath("$.message").value(BannerSuccessCode.SUCCESS_CREATE_BANNER.getMessage())) + .andExpect(jsonPath("$.data.id").value(mockBannerDetail.bannerId())) + .andExpect(jsonPath("$.data.status").value(mockBannerDetail.bannerStatus())) + .andExpect(jsonPath("$.data.location").value(mockBannerDetail.bannerLocation())) + .andExpect(jsonPath("$.data.content_type").value(mockBannerDetail.bannerType())) + .andExpect(jsonPath("$.data.publisher").value(mockBannerDetail.publisher())) + .andExpect(jsonPath("$.data.link").value(mockBannerDetail.link())) + .andExpect(jsonPath("$.data.start_date").value(mockBannerDetail.startDate().toString())) + .andExpect(jsonPath("$.data.end_date").value(mockBannerDetail.endDate().toString())) + .andExpect(jsonPath("$.data.image_url_pc").value(mockBannerDetail.pcImageUrl())) + .andExpect(jsonPath("$.data.image_url_mobile").value(mockBannerDetail.mobileImageUrl())); + } + } } diff --git a/operation-domain/src/test/java/org/sopt/makers/operation/banner/BannerTest.java b/operation-domain/src/test/java/org/sopt/makers/operation/banner/BannerTest.java index ea0d7322..3c09b8d0 100644 --- a/operation-domain/src/test/java/org/sopt/makers/operation/banner/BannerTest.java +++ b/operation-domain/src/test/java/org/sopt/makers/operation/banner/BannerTest.java @@ -48,9 +48,11 @@ public class BannerTest { private static final String TEST_BANNER_PC_IMAGE_URL = "image-url-for-pc"; private static final String TEST_BANNER_MOBILE_IMAGE_URL = "image-url-for-mobile"; private static final PublishPeriod TEST_PUBLISH_PERIOD = PublishPeriod.builder() - .startDate(TEST_BANNER_START_DATE).endDate(TEST_BANNER_END_DATE).build(); + .startDate(TEST_BANNER_START_DATE) + .endDate(TEST_BANNER_END_DATE).build(); private static final BannerImage TEST_BANNER_IMAGE = BannerImage.builder() - .pcImageUrl(TEST_BANNER_PC_IMAGE_URL).mobileImageUrl(TEST_BANNER_MOBILE_IMAGE_URL).build(); + .pcImageUrl(TEST_BANNER_PC_IMAGE_URL) + .mobileImageUrl(TEST_BANNER_MOBILE_IMAGE_URL).build(); private static final Banner TEST_BANNER = Banner.builder() .location(TEST_BANNER_LOCATION) diff --git a/operation-domain/src/test/java/org/sopt/makers/operation/banner/PublishPeriodTest.java b/operation-domain/src/test/java/org/sopt/makers/operation/banner/PublishPeriodTest.java index 1825ec4f..07c7db80 100644 --- a/operation-domain/src/test/java/org/sopt/makers/operation/banner/PublishPeriodTest.java +++ b/operation-domain/src/test/java/org/sopt/makers/operation/banner/PublishPeriodTest.java @@ -18,7 +18,8 @@ public class PublishPeriodTest { private static final LocalDate TEST_START_DATE = LocalDate.of(2024,1,1); private static final LocalDate TEST_END_DATE = LocalDate.of(2024,12,31); private final PublishPeriod givenPeriod = PublishPeriod.builder() - .startDate(TEST_START_DATE).endDate(TEST_END_DATE).build(); + .startDate(TEST_START_DATE) + .endDate(TEST_END_DATE).build(); @ParameterizedTest(name = "({index}) date : {0} -> result : {1}") @MethodSource("argsForCalculateStatus")