Skip to content
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
4 changes: 2 additions & 2 deletions openapi/openapi-awx-v2.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9706,7 +9706,7 @@ definitions:
related:
readOnly: true
title: Related
type: string
type: object
scm_branch:
title: Scm branch
type: string
Expand All @@ -9724,7 +9724,7 @@ definitions:
summary_fields:
readOnly: true
title: Summary fields
type: string
type: object
timeout:
title: Timeout
type: integer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.opendevstack.component_provisioner.client.awx.v2.ApiClient;
import org.opendevstack.component_provisioner.client.awx.v2.api.JobsApi;
import org.opendevstack.component_provisioner.client.awx.v2.api.WorkflowJobNodesApi;
import org.opendevstack.component_provisioner.client.awx.v2.api.WorkflowJobTemplatesApi;
import org.opendevstack.component_provisioner.client.awx.v2.auth.HttpBasicAuth;
import org.springframework.beans.factory.annotation.Qualifier;
Expand Down Expand Up @@ -46,4 +47,10 @@ public WorkflowJobTemplatesApi workflowJobTemplatesApi(@Qualifier("awxApiClient"
public JobsApi jobsApi(@Qualifier("awxApiClient") ApiClient awxApiClient) {
return new JobsApi(awxApiClient);
}

@Bean(name= "awxWorkflowJobNodesApi")
@Primary
public WorkflowJobNodesApi workflowJobsNodesApi(@Qualifier("awxApiClient") ApiClient awxApiClient) {
return new WorkflowJobNodesApi(awxApiClient);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.opendevstack.component_provisioner.server.model.*;
import org.opendevstack.component_provisioner.server.services.awx.AwxWorkflowJob;
import org.opendevstack.component_provisioner.server.services.awx.AwxWorkflowJobLaunch;
import org.opendevstack.component_provisioner.server.services.model.AwxResultNames;

import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -184,7 +185,7 @@ public ProvisionerMessageDefinition asProvisionerMessageDefinition(CatalogItemUs
return MAPPER.map(itemUserActionMsgDef, ProvisionerMessageDefinition.class);
}

public ProjectComponentProvisionStatus asProjectComponentProvisionStatus(String projectKey, ProjectComponentExtendedInfo projectComponentInfo, JobDetail jobDetail) {
public ProjectComponentProvisionStatus asProjectComponentProvisionStatus(String projectKey, ProjectComponentExtendedInfo projectComponentInfo, JobDetail workflowJob) {
var parameters = Optional.ofNullable(projectComponentInfo.getParameters())
.orElseGet(Collections::emptyList)
.stream()
Expand All @@ -198,9 +199,9 @@ public ProjectComponentProvisionStatus asProjectComponentProvisionStatus(String
.catalogItemRef(projectComponentInfo.getCatalogItemRef())
.status(projectComponentInfo.getStatus())
.componentUrl(projectComponentInfo.getComponentUrl())
.workflowJobId(Optional.ofNullable(jobDetail).map(JobDetail::getId).map(Object::toString).orElse("N/A"))
.errorTask(Optional.ofNullable(jobDetail).map(JobDetail::getArtifacts).map(artifacts -> artifacts.getOrDefault("result_output", "N/A")).orElse("N/A"))
.errorMessage(Optional.ofNullable(jobDetail).map(JobDetail::getArtifacts).map(artifacts -> artifacts.getOrDefault("result_code", "N/A")).orElse("N/A"))
.workflowJobId(Optional.ofNullable(workflowJob).map(JobDetail::getId).map(Object::toString).orElse("N/A"))
.errorTask(Optional.ofNullable(workflowJob).map(JobDetail::getArtifacts).map(artifacts -> artifacts.getOrDefault(AwxResultNames.RESULT_OUTPUT.getValue(), "N/A")).orElse("N/A"))
.errorMessage(Optional.ofNullable(workflowJob).map(JobDetail::getArtifacts).map(artifacts -> artifacts.getOrDefault(AwxResultNames.RESULT_CODE.getValue(), "N/A")).orElse("N/A"))
.parameters(parameters)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,27 @@
package org.opendevstack.component_provisioner.server.services;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.opendevstack.component_provisioner.client.awx.v2.api.JobsApi;
import org.opendevstack.component_provisioner.client.awx.v2.api.WorkflowJobNodesApi;
import org.opendevstack.component_provisioner.client.awx.v2.api.WorkflowJobTemplatesApi;
import org.opendevstack.component_provisioner.client.awx.v2.model.ApiWorkflowJobNodesList200Response;
import org.opendevstack.component_provisioner.client.awx.v2.model.JobDetail;
import org.opendevstack.component_provisioner.client.awx.v2.model.WorkflowJobTemplate;
import org.opendevstack.component_provisioner.client.awx.v2.model.WorkflowJobNodeList;
import org.opendevstack.component_provisioner.server.mappers.EntitiesMapper;
import org.opendevstack.component_provisioner.server.services.awx.AwxWorkflowJob;
import org.opendevstack.component_provisioner.server.services.awx.AwxWorkflowJobLaunch;
import org.opendevstack.component_provisioner.server.services.exceptions.AwxClientException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.tuple.Pair;
import org.opendevstack.component_provisioner.server.services.model.AwxResultNames;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpStatusCode;
import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestClientException;

import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.Optional;

@Service
Expand All @@ -27,11 +33,14 @@ public class AwxService {
private final WorkflowJobTemplatesApi workflowJobTemplatesApi;
@Qualifier("awxJobsApi")
private final JobsApi jobsApi;
Comment thread
jordivx marked this conversation as resolved.
@Qualifier("awxWorkflowJobNodesApi")
private final WorkflowJobNodesApi workflowJobNodesApi;
private final EntitiesMapper entitiesMapper;

public AwxService(WorkflowJobTemplatesApi workflowJobTemplatesApi, JobsApi jobsApi, EntitiesMapper entitiesMapper) {
public AwxService(WorkflowJobTemplatesApi workflowJobTemplatesApi, JobsApi jobsApi, WorkflowJobNodesApi workflowJobNodesApi, EntitiesMapper entitiesMapper) {
this.workflowJobTemplatesApi = workflowJobTemplatesApi;
this.jobsApi = jobsApi;
this.workflowJobNodesApi = workflowJobNodesApi;
this.entitiesMapper = entitiesMapper;
}

Expand Down Expand Up @@ -82,11 +91,37 @@ public Optional<JobDetail> getWorkflowJobById(String jobId) {
log.info("Getting workflow job with id: {}", jobId);

try {
var jobDetail = jobsApi.apiJobsRead(AWX_API_VERSION, jobId);
var workflowNodesList = workflowJobNodesApi.apiWorkflowJobsWorkflowNodesList(AWX_API_VERSION, jobId, null, null, null);

log.debug("WorkflowNodesList: {}", workflowNodesList);

log.debug("Job detail: {}", jobDetail);
var innerNodesList = Optional.ofNullable(workflowNodesList)
.map(ApiWorkflowJobNodesList200Response::getResults).stream()
.flatMap(java.util.Collection::stream)
.map(WorkflowJobNodeList::getJob)
.toList();

return Optional.ofNullable(jobDetail);
for (Integer nodeId: innerNodesList) {
var jobDetail = jobsApi.apiJobsRead(AWX_API_VERSION, nodeId.toString());

boolean someArtifactIsAnAwxResult =
Optional.ofNullable(jobDetail.getArtifacts())
.map(Map::keySet)
.orElse(Collections.emptySet())
.stream()
.anyMatch(key ->
Arrays.stream(AwxResultNames.values())
.anyMatch(e -> e.getValue().equals(key))
);

if (someArtifactIsAnAwxResult) {
log.debug("Found job detail with artifacts for node id: {}, job detail: {}", nodeId, jobDetail);

return Optional.of(jobDetail);
}
}

return Optional.empty();
} catch (HttpStatusCodeException e) {
var errMsg = String.format(
"Error getting workflow job with id: %s, status code: %s",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package org.opendevstack.component_provisioner.server.services.model;

import lombok.Getter;

@Getter
public enum AwxResultNames {

RESULT_OUTPUT("result_output"),
RESULT_CODE("result_code");

private final String value;

AwxResultNames(String value) {
this.value = value;
}

@Override
public String toString() {
return String.valueOf(value);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.opendevstack.component_provisioner.client.awx.v2.model;

import java.util.Collections;
import java.util.List;

public class ApiWorkflowJobNodesList200ResponseMother {

public static ApiWorkflowJobNodesList200Response of() {
return of(Collections.emptyList());
}

public static ApiWorkflowJobNodesList200Response of(List<WorkflowJobNodeList> results) {
return ApiWorkflowJobNodesList200Response.builder()
.results(results)
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package org.opendevstack.component_provisioner.client.awx.v2.model;

import org.opendevstack.component_provisioner.server.services.model.AwxResultNames;

import java.util.Map;

public class JobDetailMother {

public static JobDetail of() {
return of(12345, Map.of("key1", "value1", "key2", "value2"));
return of(12345, Map.of(
"key1", "value1",
"key2", "value2",
AwxResultNames.RESULT_CODE.getValue(), "PROVISION_SUCCESS"
));
}

public static JobDetail of(Integer id, Map<String, String> artifacts) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package org.opendevstack.component_provisioner.client.awx.v2.model;

public class WorkflowJobNodeListMother {

public static WorkflowJobNodeList of() {
return of(12345);
}

public static WorkflowJobNodeList of(Integer jobId) {
return WorkflowJobNodeList.builder()
.job(jobId)
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.opendevstack.component_provisioner.client.awx.v2.api.JobsApi;
import org.opendevstack.component_provisioner.client.awx.v2.api.WorkflowJobNodesApi;
import org.opendevstack.component_provisioner.client.awx.v2.api.WorkflowJobTemplatesApi;
import org.opendevstack.component_provisioner.client.awx.v2.model.ApiWorkflowJobNodesList200ResponseMother;
import org.opendevstack.component_provisioner.client.awx.v2.model.JobDetailMother;
import org.opendevstack.component_provisioner.client.awx.v2.model.WorkflowJob;
import org.opendevstack.component_provisioner.client.awx.v2.model.WorkflowJobLaunch;
import org.opendevstack.component_provisioner.client.awx.v2.model.WorkflowJobNodeList;
import org.opendevstack.component_provisioner.client.awx.v2.model.WorkflowJobNodeListMother;
import org.opendevstack.component_provisioner.server.mappers.EntitiesMapper;
import org.opendevstack.component_provisioner.server.services.awx.AwxWorkflowJob;
import org.opendevstack.component_provisioner.server.services.awx.AwxWorkflowJobLaunch;
Expand All @@ -19,6 +23,9 @@
import org.springframework.web.client.HttpStatusCodeException;
import org.springframework.web.client.RestClientException;

import java.util.Collections;
import java.util.List;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
Expand All @@ -35,6 +42,9 @@ class AwxServiceTest {
@Mock
private WorkflowJobTemplatesApi workflowJobTemplatesApi;

@Mock
private WorkflowJobNodesApi workflowJobNodesApi;

@Mock
private JobsApi jobsApi;

Expand Down Expand Up @@ -117,12 +127,17 @@ void triggerWorkflowJob_throwsRuntimeException_whenRestClientExceptionOccurs() {
@Test
void givenJobId_whenGetWorkflowJobByIdSucceeds_thenReturnsJobDetail() {
// given
var workflowJobId = "workflow-job-id";
var jobId = "12345";
var jobDetail = JobDetailMother.of();
List<WorkflowJobNodeList> results = Collections.singletonList(WorkflowJobNodeListMother.of(Integer.valueOf(jobId)));
var workflowJobNodesResponse = ApiWorkflowJobNodesList200ResponseMother.of(results);

when(jobsApi.apiJobsRead(AWX_API_VERSION, "12345")).thenReturn(jobDetail);
when(workflowJobNodesApi.apiWorkflowJobsWorkflowNodesList(AWX_API_VERSION, workflowJobId, null, null, null)).thenReturn(workflowJobNodesResponse);
when(jobsApi.apiJobsRead(AWX_API_VERSION, jobId)).thenReturn(jobDetail);

// when
var result = awxService.getWorkflowJobById("12345");
var result = awxService.getWorkflowJobById(workflowJobId);

// then
assertTrue(result.isPresent());
Expand All @@ -134,7 +149,7 @@ void givenJobId_whenGetWorkflowJobByIdReturnsNull_thenReturnsEmptyOptional() {
// given
String jobId = "job-123";

when(jobsApi.apiJobsRead(AWX_API_VERSION, jobId)).thenReturn(null);
when(workflowJobNodesApi.apiWorkflowJobsWorkflowNodesList(AWX_API_VERSION, jobId, null, null, null)).thenReturn(null);

// when
var result = awxService.getWorkflowJobById(jobId);
Expand All @@ -149,7 +164,7 @@ void givenJobId_whenHttpStatusCodeExceptionOccurs_thenReturnsEmptyOptional() {
String jobId = "job-123";
HttpStatusCodeException exception = mock(HttpStatusCodeException.class);

when(jobsApi.apiJobsRead(AWX_API_VERSION, jobId)).thenThrow(exception);
when(workflowJobNodesApi.apiWorkflowJobsWorkflowNodesList(AWX_API_VERSION, jobId, null, null, null)).thenThrow(exception);
when(exception.getStatusCode()).thenReturn(HttpStatus.NOT_FOUND);

// when
Expand All @@ -165,7 +180,7 @@ void givenJobId_whenRestClientExceptionOccurs_thenThrowsAwxClientException() {
String jobId = "job-123";
RestClientException exception = new RestClientException("Connection error");

when(jobsApi.apiJobsRead(AWX_API_VERSION, jobId)).thenThrow(exception);
when(workflowJobNodesApi.apiWorkflowJobsWorkflowNodesList(AWX_API_VERSION, jobId, null, null, null)).thenThrow(exception);

// when & then
assertThrows(AwxClientException.class, () -> awxService.getWorkflowJobById(jobId));
Expand Down
Loading