Skip to content

Commit

Permalink
Merge pull request #3 from asavershin/ci-cd
Browse files Browse the repository at this point in the history
Ci cd
  • Loading branch information
asavershin authored Jun 7, 2024
2 parents 14cc2e7 + ddde0e0 commit 9828a5a
Show file tree
Hide file tree
Showing 111 changed files with 2,083 additions and 477 deletions.
114 changes: 114 additions & 0 deletions .github/workflows/JavaMavenCI.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
name: Java CI with Maven

on:
pull_request:

jobs:
build:

runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: maven
- name: Compile with maven
run: mvn clean compile

- name: Update dependency graph
uses: advanced-security/maven-dependency-submission-action@571e99aab1055c2e71a1e2309b9691de18d6b7d6

checkstyle:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: maven
- name: Compile with maven
run: mvn checkstyle:check

testing:
runs-on: ubuntu-latest
permissions:
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: maven
- name: Compile maven project
run: mvn clean compile
- name: Test maven project
run: mvn test
- name: Add coverage to PR
id: jacoco
uses: madrapps/[email protected]
with:
paths: |
${{ github.workspace }}/**/target/site/jacoco/*.xml
token: ${{ secrets.TEST_SECRET }}
min-coverage-overall: 50
min-coverage-changed-files: 50
- name: Upload coverage report
uses: actions/upload-artifact@v4
with:
name: jacoco-report
path: ${{ github.workspace }}/**/target/site/jacoco/

- name: Fail PR if overall coverage is less than 50%
if: ${{ steps.jacoco.outputs.coverage-overall < 50.0 }}
uses: actions/github-script@v6
with:
script: |
core.setFailed('Overall coverage is less than 50%!')
docker:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '21'
distribution: 'temurin'
cache: maven
-
name: Package
run: mvn clean package
-
name: Set up QEMU
uses: docker/setup-qemu-action@v3
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
-
name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
-
name: Build and push
uses: docker/build-push-action@v5
with:
context: ./api/.
push: true
tags: asavershin/api:latest
2 changes: 1 addition & 1 deletion api/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM openjdk:22
FROM openjdk:21

WORKDIR /app

Expand Down
5 changes: 3 additions & 2 deletions api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
<artifactId>api</artifactId>

<properties>
<maven.compiler.source>22</maven.compiler.source>
<maven.compiler.target>22</maven.compiler.target>
<maven.compiler.source>21</maven.compiler.source>
<maven.compiler.target>21</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jooq.version>3.19.3</jooq.version>
<springdoc.starter.version>2.1.0</springdoc.starter.version>
Expand Down Expand Up @@ -256,4 +256,5 @@
</plugin>
</plugins>
</build>

</project>
13 changes: 12 additions & 1 deletion api/src/main/java/com/github/asavershin/api/ApiMain.java
Original file line number Diff line number Diff line change
@@ -1,11 +1,22 @@
package com.github.asavershin.api;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
public class ApiMain {
public static void main(String[] args) {
/**
* The main method that starts the Spring Boot application.
*
* @param args the command-line arguments
*/
public static void main(final String[] args) {
SpringApplication.run(ApiMain.class, args);
}

private void foo() {
throw new UnsupportedOperationException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,29 @@
import org.springframework.web.multipart.MultipartFile;

public interface ImageService {
/**
* Stores an image for a given user.
*
* @param userId the user who owns the image
* @param multipartFile the image file to be stored
* @return the unique identifier of the stored image
*/
ImageId storeImage(UserId userId, MultipartFile multipartFile);
/**
* Deletes an image by its unique identifier. Validate that
* image belongs to user.
*
* @param userId the user who owns the image
* @param imageId the unique identifier of the image to be deleted
*/
void deleteImageByImageId(UserId userId, ImageId imageId);
/**
* Downloads an image by its unique identifier. Validate that
* image belongs to user.
*
* @param imageId the unique identifier of the image to be downloaded
* @param userId the user who owns the image
* @return the byte array representation of the image
*/
byte[] downloadImage(ImageId imageId, UserId userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,60 +2,107 @@

import com.github.asavershin.api.application.in.services.image.ImageService;
import com.github.asavershin.api.application.out.MinioService;
import com.github.asavershin.api.common.Validator;
import com.github.asavershin.api.domain.image.*;
import com.github.asavershin.api.domain.image.DeleteImageOfUser;
import com.github.asavershin.api.domain.image.GetImageOfUser;
import com.github.asavershin.api.domain.image.Image;
import com.github.asavershin.api.domain.image.ImageId;
import com.github.asavershin.api.domain.image.ImageNameWithExtension;
import com.github.asavershin.api.domain.image.MetaData;
import com.github.asavershin.api.domain.image.StoreImageOfUser;
import com.github.asavershin.api.domain.user.UserId;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.Arrays;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
import java.util.UUID;

@Service
@RequiredArgsConstructor
@Slf4j
public class ImageServiceImpl implements ImageService {
public class ImageServiceImpl implements ImageService, Serializable {
/**
* The MinioService is used to interact with the Minio storage service.
*/
private final MinioService minioService;

/**
* The GetImageOfUser service is used to retrieve an image
* by its ID and user ID.
*/
private final GetImageOfUser getImageOfUser;

/**
* The DeleteImageOfUser service is used to delete an image
* by its ID and user ID.
*/
private final DeleteImageOfUser deleteImageOfUser;

/**
* The StoreImageOfUser service is used to store an image
* with its metadata and associate it with a user.
*/
private final StoreImageOfUser storeImageOfUser;

/**
* Method not marked as final to allow Spring
* to create a proxy first for transactional purposes.
* @param userId the user who owns the image
* @param multipartFile the image file to be stored
* @return ID of new stored image
*/
@Override
@Transactional
public ImageId storeImage(UserId userId, MultipartFile multipartFile) {
log.info("Store image: {}", multipartFile.getOriginalFilename());
var metaInfo = new MetaInfo(
public ImageId storeImage(final UserId userId,
final MultipartFile multipartFile) {
var metaInfo = new MetaData(
ImageNameWithExtension
.fromOriginalFileName(multipartFile.getOriginalFilename()),
.fromOriginalFileName(
multipartFile.getOriginalFilename()
),
multipartFile.getSize()
);
log.info("Store image: check on ex");
var imageId = new ImageId(UUID.fromString(minioService.saveFile(multipartFile)));
var imageId = ImageId.nextIdentity();
storeImageOfUser.storeImageOfUser(
new Image(
imageId,
metaInfo,
userId
)
);
minioService.saveFile(multipartFile, imageId.value().toString());
return imageId;
}

/**
* Method not marked as final to allow Spring
* * to create a proxy first for transactional purposes.
* @param userId the user who owns the image
* @param imageId the unique identifier of the image to be deleted
*/
@Override
@Transactional
public void deleteImageByImageId(UserId userId, ImageId imageId) {
public void deleteImageByImageId(final UserId userId,
final ImageId imageId) {
deleteImageOfUser.removeImageOfUser(imageId, userId);
minioService.deleteFiles(List.of(imageId.value().toString()));
}

/**
* Method not marked as final to allow Spring make CGLIB proxy.
* @param imageId the unique identifier of the image to be downloaded
* @param userId the user who owns the image
* @return bytes of the image
*/
@Override
public byte[] downloadImage(ImageId imageId, UserId userId) {
public byte[] downloadImage(final ImageId imageId,
final UserId userId) {
return minioService.getFile(
getImageOfUser.getImageOfUser(userId, imageId).imageId().value().toString()
getImageOfUser.getImageOfUser(
userId,
imageId
)
.imageId().value().toString()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* This package contains implementation application services for images.
* @author asavershin
*/
package com.github.asavershin.api.application.in.services.image.impl;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
/**
* This package contains application services for images.
* @author asavershin
*/
package com.github.asavershin.api.application.in.services.image;
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,26 @@

@Getter
public class ApplicationCredentials {
/**
* The access token for user.
*/
private final String accessToken;

/**
* The refresh token for user.
*/
private final String refreshToken;

public ApplicationCredentials(String accessToken, String refreshToken) {
this.accessToken = accessToken;
this.refreshToken = refreshToken;
/**
* Constructs an instance of {@link ApplicationCredentials}
* with the provided access token and refresh token.
*
* @param aAccessToken The access token for user.
* @param aRefreshToken The refresh token for user.
*/
public ApplicationCredentials(final String aAccessToken,
final String aRefreshToken) {
this.accessToken = aAccessToken;
this.refreshToken = aRefreshToken;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,11 @@

@FunctionalInterface
public interface GetNewCredentials {
/**
* Generates new credentials for the user based on the provided credentials.
*
* @param credentials The current credentials of the user.
* @return The new credentials for the user.
*/
ApplicationCredentials get(Credentials credentials);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,11 @@

@FunctionalInterface
public interface GetNewCredentialsUsingRefreshToken {
/**
* Generates new credentials using the provided refresh token.
*
* @param credentials The credentials that contain the refresh token.
* @return The new credentials generated using the refresh token.
*/
ApplicationCredentials get(Credentials credentials);
}
Loading

0 comments on commit 9828a5a

Please sign in to comment.