Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ci cd #3

Merged
merged 9 commits into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading