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

ADM-709:[backend][docs]:Verify buildkite and obtain buildkite data with new API #889

Merged
merged 27 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
02da8c1
ADM-709:[backend]feat: add verify token and get info for buildKite co…
Jan 5, 2024
2cbd26b
ADM-709:[backend]feat: add test for buildKite service
Jan 5, 2024
e7efec8
ADM-650:[docs]fix: fix docs
Jan 5, 2024
41f013d
ADM-709:[backend]fix: fix verify buildKite method
Jan 8, 2024
5cf2d13
ADM-709:[backend]fix: fix test
Jan 8, 2024
417165e
ADM-709:[backend]fix: fix error
Jan 8, 2024
4b48e0b
ADM-709:[backend]fix: add BuildKiteController Test
Jan 8, 2024
3c94296
ADM-709:[docs]fix: fix error message
Jan 8, 2024
74b50ff
Merge branch 'main' into ADM-709
Andrea2000728 Jan 8, 2024
110cb36
ADM-709:[backend]fix: fix assert
Jan 8, 2024
4c18f49
Merge remote-tracking branch 'origin/ADM-709' into ADM-709
Jan 8, 2024
f646eea
ADM-709:[backend]feat: add PipelineTypeEnum and rename
Jan 9, 2024
4fa9164
ADM-709:[docs]fix: fix docs
Jan 9, 2024
637eb8a
Merge branch 'main' into ADM-709
Jan 9, 2024
cb96143
ADM-709:[backend]fix: fix tests
Jan 9, 2024
cff1849
ADM-709:[backend]fix: format
Jan 9, 2024
e4372dc
Merge branch 'main' into ADM-709
Andrea2000728 Jan 9, 2024
53e4916
ADM-709:[backend]fix: rename PipelineType and add test
Jan 9, 2024
1bca7c1
Merge remote-tracking branch 'origin/ADM-709' into ADM-709
Jan 9, 2024
ebc3e81
ADM-709:[backend]fix: fix test
Jan 9, 2024
f81b9ea
ADM-709:[backend]fix: fix test
Jan 9, 2024
bfdc8fd
Merge branch 'main' into ADM-709
Andrea2000728 Jan 10, 2024
3680271
ADM-709:[backend]fix: fix buildKite type
Jan 10, 2024
354de53
ADM-709:[backend]fix: fix test
Jan 11, 2024
29695c7
ADM-709:[backend]fix: fix buildkite type
Jan 11, 2024
c1126fc
ADM-709:[backend]fix: fix buildkite type
Jan 11, 2024
7007521
Merge branch 'main' into ADM-709
Andrea2000728 Jan 11, 2024
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
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package heartbeat.controller.pipeline;

import heartbeat.controller.pipeline.dto.request.PipelineType;
import heartbeat.controller.pipeline.dto.request.TokenParam;
import heartbeat.controller.pipeline.dto.request.PipelineParam;
import heartbeat.controller.pipeline.dto.request.PipelineStepsParam;
import heartbeat.controller.pipeline.dto.response.BuildKiteResponseDTO;
Expand Down Expand Up @@ -29,12 +31,34 @@ public class PipelineController {

private final BuildKiteService buildKiteService;

@Deprecated
@PostMapping("/{pipelineType}")
public BuildKiteResponseDTO getBuildKiteInfo(@PathVariable String pipelineType,
@Valid @RequestBody PipelineParam pipelineParam) {
return buildKiteService.fetchPipelineInfo(pipelineParam);
}

@PostMapping("/{pipelineType}/verify")
public ResponseEntity<Void> verifyBuildKiteToken(@PathVariable @NotBlank String pipelineType,
@Valid @RequestBody TokenParam tokenParam) {
PipelineType.fromValue(pipelineType);
buildKiteService.verifyToken(tokenParam.getToken());
return ResponseEntity.noContent().build();
}

@PostMapping("/{pipelineType}/info")
public ResponseEntity<BuildKiteResponseDTO> fetchBuildKiteInfo(@PathVariable @NotBlank String pipelineType,
@Valid @RequestBody PipelineParam pipelineParam) {
PipelineType.fromValue(pipelineType);
BuildKiteResponseDTO buildKiteResponse = buildKiteService.getBuildKiteInfo(pipelineParam);
if (buildKiteResponse.getPipelineList().isEmpty()) {
return ResponseEntity.noContent().build();
}
else {
return ResponseEntity.ok(buildKiteResponse);
}
}

@GetMapping("/{pipelineType}/{organizationId}/pipelines/{buildId}/steps")
public ResponseEntity<PipelineStepsDTO> getPipelineSteps(
@RequestHeader("Authorization") @NotBlank(message = "Token must not be blank") String token,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package heartbeat.controller.pipeline.dto.request;

import lombok.extern.log4j.Log4j2;

@Log4j2
public enum PipelineType {

BUILDKITE("buildkite");

public final String pipelineType;

PipelineType(String pipelineType) {
this.pipelineType = pipelineType;
}

public static PipelineType fromValue(String type) {
return switch (type) {
case "buildkite" -> BUILDKITE;
default -> {
log.error("Failed to match Pipeline type: {} ", type);
throw new IllegalArgumentException("Pipeline type does not find!");
}
};
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package heartbeat.controller.pipeline.dto.request;

import jakarta.validation.Valid;
import jakarta.validation.constraints.NotBlank;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class TokenParam {

@Valid
@NotBlank(message = "Token cannot be empty.")
private String token;

}
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,6 @@
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.net.URLEncoder;

@Service
@RequiredArgsConstructor
Expand All @@ -58,20 +57,21 @@ public void shutdownExecutor() {
customTaskExecutor.shutdown();
}

@Deprecated
public BuildKiteResponseDTO fetchPipelineInfo(PipelineParam pipelineParam) {
try {
String buildKiteToken = "Bearer " + pipelineParam.getToken();
log.info("Start to query token permissions by token");
BuildKiteTokenInfo buildKiteTokenInfo = buildKiteFeignClient.getTokenInfo(buildKiteToken);
log.info("Successfully query token permissions by token, token info scopes: {}",
buildKiteTokenInfo.getScopes());
verifyToken(buildKiteTokenInfo);
verifyTokenScopes(buildKiteTokenInfo);
log.info("Start to query BuildKite organizations by token");
List<BuildKiteOrganizationsInfo> buildKiteOrganizationsInfo = buildKiteFeignClient
.getBuildKiteOrganizationsInfo(buildKiteToken);
log.info("Successfully query BuildKite organizations by token, slug: {}", buildKiteOrganizationsInfo);

log.info("Start to query buildKite pipelineInfo by organizations slug: {}", buildKiteOrganizationsInfo);
log.info("Start to query BuildKite pipelineInfo by organizations slug: {}", buildKiteOrganizationsInfo);
List<Pipeline> buildKiteInfoList = buildKiteOrganizationsInfo.stream()
.flatMap(org -> buildKiteFeignClient
.getPipelineInfo(buildKiteToken, org.getSlug(), "1", "100", pipelineParam.getStartTime(),
Expand All @@ -80,7 +80,7 @@ public BuildKiteResponseDTO fetchPipelineInfo(PipelineParam pipelineParam) {
.map(pipeline -> PipelineTransformer.fromBuildKitePipelineDto(pipeline, org.getSlug(),
org.getName())))
.collect(Collectors.toList());
log.info("Successfully get buildKite pipelineInfo, slug:{}, pipelineInfoList size:{}",
log.info("Successfully get BuildKite pipelineInfo, slug:{}, pipelineInfoList size:{}",
buildKiteOrganizationsInfo, buildKiteInfoList.size());

return BuildKiteResponseDTO.builder().pipelineList(buildKiteInfoList).build();
Expand All @@ -98,7 +98,7 @@ public BuildKiteResponseDTO fetchPipelineInfo(PipelineParam pipelineParam) {
}
}

private void verifyToken(BuildKiteTokenInfo buildKiteTokenInfo) {
private void verifyTokenScopes(BuildKiteTokenInfo buildKiteTokenInfo) {
for (String permission : permissions) {
if (!buildKiteTokenInfo.getScopes().contains(permission)) {
log.error("Failed to call BuildKite, because of insufficient permission, current permissions: {}",
Expand Down Expand Up @@ -300,4 +300,59 @@ private List<DeployInfo> getBuildsByState(List<BuildKiteBuildInfo> buildInfos,
.toList();
}

public void verifyToken(String token) {
try {
String buildKiteToken = "Bearer " + token;
log.info("Start to query token permissions by token");
BuildKiteTokenInfo buildKiteTokenInfo = buildKiteFeignClient.getTokenInfo(buildKiteToken);
log.info("Successfully query token permissions by token, token info scopes: {}",
buildKiteTokenInfo.getScopes());
verifyTokenScopes(buildKiteTokenInfo);
}
catch (RuntimeException e) {
Throwable cause = Optional.ofNullable(e.getCause()).orElse(e);
log.error("Failed to call BuildKite, e: {}", cause.getMessage());
if (cause instanceof BaseException baseException) {
throw baseException;
}
throw new InternalServerErrorException(
String.format("Failed to call BuildKite, cause is %s", cause.getMessage()));
}
}

public BuildKiteResponseDTO getBuildKiteInfo(PipelineParam pipelineParam) {
try {
String buildKiteToken = "Bearer " + pipelineParam.getToken();
log.info("Start to query BuildKite organizations by token");
List<BuildKiteOrganizationsInfo> buildKiteOrganizationsInfo = buildKiteFeignClient
.getBuildKiteOrganizationsInfo(buildKiteToken);
log.info("Successfully query BuildKite organizations by token, slug: {}", buildKiteOrganizationsInfo);

log.info("Start to query BuildKite pipelineInfo by organizations slug: {}", buildKiteOrganizationsInfo);
List<Pipeline> buildKiteInfoList = buildKiteOrganizationsInfo.stream()
.flatMap(org -> buildKiteFeignClient
.getPipelineInfo(buildKiteToken, org.getSlug(), "1", "100", pipelineParam.getStartTime(),
pipelineParam.getEndTime())
.stream()
.map(pipeline -> PipelineTransformer.fromBuildKitePipelineDto(pipeline, org.getSlug(),
org.getName())))
.collect(Collectors.toList());
log.info("Successfully get BuildKite pipelineInfo, slug:{}, pipelineInfoList size:{}",
buildKiteOrganizationsInfo, buildKiteInfoList.size());

return BuildKiteResponseDTO.builder().pipelineList(buildKiteInfoList).build();
}
catch (RuntimeException e) {
Throwable cause = Optional.ofNullable(e.getCause()).orElse(e);
log.error("Failed to call BuildKite, start time: {}, e: {}", pipelineParam.getStartTime(),
cause.getMessage());
if (cause instanceof BaseException baseException) {
throw baseException;
}
throw new InternalServerErrorException(
String.format("Failed to call BuildKite, cause is %s", cause.getMessage()));

}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
Expand All @@ -12,13 +13,15 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jayway.jsonpath.JsonPath;
import heartbeat.controller.pipeline.dto.request.PipelineParam;
import heartbeat.controller.pipeline.dto.request.TokenParam;
import heartbeat.controller.pipeline.dto.response.BuildKiteResponseDTO;
import heartbeat.controller.pipeline.dto.response.Pipeline;
import heartbeat.controller.pipeline.dto.response.PipelineStepsDTO;
import heartbeat.service.pipeline.buildkite.BuildKiteService;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import lombok.val;
Expand All @@ -38,6 +41,14 @@
@AutoConfigureJsonTesters
public class BuildKiteControllerTest {

public static final String TEST_TOKEN = "test_token";

public static final String TEST_START_TIME = "16737733";

public static final String TEST_END_TIME = "17657557";

public static final String BUILD_KITE = "buildkite";

@MockBean
private BuildKiteService buildKiteService;

Expand All @@ -53,16 +64,18 @@ void shouldReturnCorrectPipelineInfoWhenCallBuildKiteMockServer() throws Excepti
BuildKiteResponseDTO buildKiteResponseDTO = BuildKiteResponseDTO.builder().pipelineList(pipelines).build();
when(buildKiteService.fetchPipelineInfo(any())).thenReturn(buildKiteResponseDTO);
PipelineParam pipelineParam = PipelineParam.builder()
.token("test_token")
.startTime("16737733")
.endTime("17657557")
.token(TEST_TOKEN)
.startTime(TEST_START_TIME)
.endTime(TEST_END_TIME)
.build();

MockHttpServletResponse response = mockMvc
.perform(post("/pipelines/buildKite").contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(pipelineParam)))
.andExpect(status().isOk())
.andReturn()
.getResponse();

final var resultId = JsonPath.parse(response.getContentAsString()).read("$.pipelineList[0].id").toString();
assertThat(resultId).contains("payment-selector-ui");
final var resultName = JsonPath.parse(response.getContentAsString()).read("$.pipelineList[0].name").toString();
Expand All @@ -88,6 +101,7 @@ void shouldReturnCorrectPipelineStepsWhenCalBuildKiteMockServer() throws Excepti
.andExpect(status().isOk())
.andReturn()
.getResponse();

val resultStep = JsonPath.parse(response.getContentAsString()).read("$.steps[0]");
assertThat(resultStep).isEqualTo(":docker: publish image to cloudsmith");
}
Expand All @@ -96,7 +110,6 @@ void shouldReturnCorrectPipelineStepsWhenCalBuildKiteMockServer() throws Excepti
void shouldReturnNoContentIfNoStepsWhenCallBuildKite() throws Exception {
List<String> steps = new ArrayList<>();
PipelineStepsDTO emptyPipelineSteps = PipelineStepsDTO.builder().steps(steps).build();

when(buildKiteService.fetchPipelineSteps(anyString(), anyString(), anyString(), any()))
.thenReturn(emptyPipelineSteps);

Expand All @@ -116,4 +129,70 @@ void shouldReturnNoContentIfNoStepsWhenCallBuildKite() throws Exception {
assertThat(response.getContentAsString()).isEqualTo("");
}

@Test
void shouldReturnNoContentWhenCorrectTokenCallBuildKite() throws Exception {
ObjectMapper mapper = new ObjectMapper();
TokenParam tokenParam = TokenParam.builder().token(TEST_TOKEN).build();
doNothing().when(buildKiteService).verifyToken(any());

MockHttpServletResponse response = mockMvc
.perform(post("/pipelines/{pipelineType}/verify", BUILD_KITE).contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(tokenParam)))
.andExpect(status().isNoContent())
.andReturn()
.getResponse();

assertThat(response.getContentAsString()).isEqualTo("");
}

@Test
void shouldReturnPipelineInfoWhenCorrectTokenCallBuildKite() throws Exception {
ObjectMapper mapper = new ObjectMapper();
List<Pipeline> pipelines = mapper.readValue(
new File("src/test/java/heartbeat/controller/pipeline/pipelineInfoData.json"), new TypeReference<>() {
});
BuildKiteResponseDTO buildKiteResponseDTO = BuildKiteResponseDTO.builder().pipelineList(pipelines).build();
PipelineParam pipelineParam = PipelineParam.builder()
.token(TEST_TOKEN)
.startTime(TEST_START_TIME)
.endTime(TEST_END_TIME)
.build();
when(buildKiteService.getBuildKiteInfo(any())).thenReturn(buildKiteResponseDTO);

MockHttpServletResponse response = mockMvc
.perform(post("/pipelines/{pipelineType}/info", BUILD_KITE).contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(pipelineParam)))
.andExpect(status().isOk())
.andReturn()
.getResponse();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

given a new line between given when then
refactor other test cases


final var resultId = JsonPath.parse(response.getContentAsString()).read("$.pipelineList[0].id").toString();
assertThat(resultId).contains("payment-selector-ui");
final var resultName = JsonPath.parse(response.getContentAsString()).read("$.pipelineList[0].name").toString();
assertThat(resultName).contains("payment-selector-ui");
}

@Test
void shouldReturnNoContentGivenPipelineInfoIsNullWhenCallingBuildKite() throws Exception {
ObjectMapper mapper = new ObjectMapper();
BuildKiteResponseDTO buildKiteResponseDTO = BuildKiteResponseDTO.builder()
.pipelineList(Collections.emptyList())
.build();
PipelineParam pipelineParam = PipelineParam.builder()
.token(TEST_TOKEN)
.startTime(TEST_START_TIME)
.endTime(TEST_END_TIME)
.build();
when(buildKiteService.getBuildKiteInfo(any())).thenReturn(buildKiteResponseDTO);

MockHttpServletResponse response = mockMvc
.perform(post("/pipelines/{pipelineType}/info", BUILD_KITE).contentType(MediaType.APPLICATION_JSON)
.content(mapper.writeValueAsString(pipelineParam)))
.andExpect(status().isNoContent())
.andReturn()
.getResponse();

assertThat(response.getContentAsString()).isEqualTo("");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package heartbeat.controller.pipeline.dto.response;

import heartbeat.controller.pipeline.dto.request.PipelineType;
import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertEquals;

public class PipelineTypeTest {

@Test
public void shouldConvertValueToType() {
PipelineType buildKiteType = PipelineType.fromValue("buildkite");

assertEquals(buildKiteType, PipelineType.BUILDKITE);
}

@Test
public void shouldThrowExceptionWhenDateTypeNotSupported() {
assertThatThrownBy(() -> PipelineType.fromValue("unknown")).isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("Pipeline type does not find!");
}

}
Loading
Loading