Skip to content

Commit

Permalink
Merge pull request #7937 from ita-social-projects/feature/7929/git-in…
Browse files Browse the repository at this point in the history
…fo-endpoint

Feature/7929/git info endpoint
  • Loading branch information
LazarenkoDmytro authored Dec 23, 2024
2 parents fdd296c + fa01dc4 commit d3d13d9
Show file tree
Hide file tree
Showing 12 changed files with 410 additions and 2 deletions.
4 changes: 3 additions & 1 deletion core/src/main/java/greencity/config/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public class SecurityConfig {
private static final String NOTIFICATION_ID = "/{notificationId}";
private static final String HABIT_INVITE = "/habit/invite";
private static final String INVITATION_ID = "/{invitationId}";
private static final String COMMIT_INFO = "/commit-info";
private final JwtTool jwtTool;
private final UserService userService;
private final AuthenticationConfiguration authenticationConfiguration;
Expand Down Expand Up @@ -206,7 +207,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
"/habit/assign/confirm/{habitAssignId}",
"/database/backup",
"/database/backupFiles",
"/ai/**")
"/ai/**",
COMMIT_INFO)
.permitAll()
.requestMatchers(HttpMethod.POST,
SUBSCRIPTIONS,
Expand Down
61 changes: 61 additions & 0 deletions core/src/main/java/greencity/controller/CommitInfoController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package greencity.controller;

import greencity.constant.HttpStatuses;
import greencity.dto.commitinfo.CommitInfoDto;
import greencity.service.CommitInfoService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
* Controller for fetching Git commit information.
*/
@RestController
@RequestMapping("/commit-info")
@RequiredArgsConstructor
public class CommitInfoController {
private final CommitInfoService commitInfoService;

/**
* Endpoint to fetch the latest Git commit hash and date.
*
* @return {@link CommitInfoDto}
*/
@Operation(summary = "Get the latest commit hash and date.")
@ApiResponse(
responseCode = "200",
description = HttpStatuses.OK,
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(
value = """
{
"commitHash": "d6e70c46b39857846f3f13ca9756c39448ab3d6f",
"commitDate": "16/12/2024 10:55:00"
}""")))
@ApiResponse(
responseCode = "404",
description = HttpStatuses.NOT_FOUND,
content = @Content(
mediaType = "application/json",
examples = @ExampleObject(
value = """
{
"timestamp": "2024-12-19T14:42:06.469+00:00",
"status": 404,
"error": "Not Found",
"trace": "greencity.exception.exceptions.ResourceNotFoundException",
"message": "Git repository not initialized. Commit info is unavailable.",
"path": "/commit-info"
}""")))
@GetMapping
public ResponseEntity<CommitInfoDto> getCommitInfo() {
return ResponseEntity.ok(commitInfoService.getLatestCommitInfo());
}
}
4 changes: 3 additions & 1 deletion core/src/main/resources/application.properties
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,6 @@ spring.messaging.stomp.websocket.allowed-origins=\
http://localhost:4200/, \
http://localhost:4205/*

server.tomcat.relaxed-query-chars=<, >, [, \,, ], ^, `, {, |, }
server.tomcat.relaxed-query-chars=<, >, [, \,, ], ^, `, {, |, }

spring.jackson.time-zone=Europe/Kyiv
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package greencity.controller;

import greencity.dto.commitinfo.CommitInfoDto;
import greencity.exception.exceptions.ResourceNotFoundException;
import greencity.service.CommitInfoService;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;

import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;

import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;

@ExtendWith(MockitoExtension.class)
class CommitInfoControllerTest {
@InjectMocks
private CommitInfoController commitInfoController;

@Mock
private CommitInfoService commitInfoService;

private MockMvc mockMvc;

@BeforeEach
void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(commitInfoController).build();
}

private static final String COMMIT_INFO_URL = "/commit-info";
private static final String COMMIT_HASH = "abc123";
private static final String COMMIT_DATE = "16/12/2024 12:06:32";

@Test
void getCommitInfoReturnsSuccessTest() throws Exception {
CommitInfoDto commitInfoDto = new CommitInfoDto(COMMIT_HASH, COMMIT_DATE);
when(commitInfoService.getLatestCommitInfo()).thenReturn(commitInfoDto);

mockMvc.perform(get(COMMIT_INFO_URL).accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$.commitHash").value(COMMIT_HASH))
.andExpect(jsonPath("$.commitDate").value(COMMIT_DATE));

verify(commitInfoService, times(1)).getLatestCommitInfo();
}

@Test
void getCommitInfoReturnsErrorTest() throws Exception {
when(commitInfoService.getLatestCommitInfo()).thenThrow(new ResourceNotFoundException());

mockMvc.perform(get(COMMIT_INFO_URL))
.andExpect(status().isNotFound());

verify(commitInfoService, times(1)).getLatestCommitInfo();
}
}
1 change: 1 addition & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
<jjwt.version>0.12.3</jjwt.version>
<commons.collections4.version>4.4</commons.collections4.version>
<commons.lang3.version>3.13.0</commons.lang3.version>
<org.eclipse.jgit.version>7.1.0.202411261347-r</org.eclipse.jgit.version>
</properties>

<profiles>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -217,4 +217,9 @@ public class ErrorMessage {
public static final String INVALID_DURATION_BETWEEN_START_AND_FINISH = "Invalid duration between start and finish";
public static final String PAGE_NOT_FOUND_MESSAGE = "Requested page %d exceeds total pages %d.";
public static final String OPEN_AI_IS_NOT_RESPONDING = "Could not get a response from OpenAI.";
public static final String WARNING_GIT_DIRECTORY_NOT_FOUND =
"WARNING: .git directory not found. Git commit info will be unavailable.";
public static final String GIT_REPOSITORY_NOT_INITIALIZED =
"Git repository not initialized. Commit info is unavailable.";
public static final String FAILED_TO_FETCH_COMMIT_INFO = "Failed to fetch commit info due to I/O error: ";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package greencity.dto.commitinfo;

import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;

/**
* DTO for commit information response.
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class CommitInfoDto {
@NotNull
private String commitHash;
@NotNull
private String commitDate;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package greencity.exception.exceptions;

import lombok.experimental.StandardException;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;

@StandardException
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
}
15 changes: 15 additions & 0 deletions service-api/src/main/java/greencity/service/CommitInfoService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package greencity.service;

import greencity.dto.commitinfo.CommitInfoDto;

/**
* Interface for fetching Git commit information.
*/
public interface CommitInfoService {
/**
* Fetches the latest Git commit hash and date.
*
* @return {@link CommitInfoDto}
*/
CommitInfoDto getLatestCommitInfo();
}
5 changes: 5 additions & 0 deletions service/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -123,5 +123,10 @@
<artifactId>commons-collections4</artifactId>
<version>${commons.collections4.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jgit</groupId>
<artifactId>org.eclipse.jgit</artifactId>
<version>${org.eclipse.jgit.version}</version>
</dependency>
</dependencies>
</project>
62 changes: 62 additions & 0 deletions service/src/main/java/greencity/service/CommitInfoServiceImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package greencity.service;

import greencity.constant.AppConstant;
import greencity.constant.ErrorMessage;
import greencity.dto.commitinfo.CommitInfoDto;
import greencity.exception.exceptions.ResourceNotFoundException;
import java.io.File;
import java.io.IOException;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import org.springframework.stereotype.Service;

/**
* Service implementation for fetching Git commit information.
*/
@Service
@Slf4j
public class CommitInfoServiceImpl implements CommitInfoService {
private Repository repository;

private static final String COMMIT_REF = "HEAD";

public CommitInfoServiceImpl() {
try {
repository = new FileRepositoryBuilder()
.setGitDir(new File(".git"))
.readEnvironment()
.findGitDir()
.build();
} catch (IOException e) {
repository = null;
log.warn(ErrorMessage.WARNING_GIT_DIRECTORY_NOT_FOUND);
}
}

/**
* {@inheritDoc}
*/
@Override
public CommitInfoDto getLatestCommitInfo() {
if (repository == null) {
throw new ResourceNotFoundException(ErrorMessage.GIT_REPOSITORY_NOT_INITIALIZED);
}

try (RevWalk revWalk = new RevWalk(repository)) {
RevCommit latestCommit = revWalk.parseCommit(repository.resolve(COMMIT_REF));
String latestCommitHash = latestCommit.name();
String latestCommitDate = DateTimeFormatter.ofPattern(AppConstant.DATE_FORMAT)
.withZone(ZoneId.of(AppConstant.UKRAINE_TIMEZONE))
.format(latestCommit.getAuthorIdent().getWhenAsInstant());

return new CommitInfoDto(latestCommitHash, latestCommitDate);
} catch (IOException e) {
throw new ResourceNotFoundException(ErrorMessage.FAILED_TO_FETCH_COMMIT_INFO + e.getMessage());
}
}
}
Loading

0 comments on commit d3d13d9

Please sign in to comment.