-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Bumped up Stage Instances API to v3 gocd#8681
* v3 of the API adds attributes 'created_time' and 'last_transition_time' * Deprecated v2 of the API
- Loading branch information
Showing
20 changed files
with
1,662 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
/* | ||
* Copyright 2020 ThoughtWorks, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
apply plugin: 'groovy' | ||
|
||
dependencies { | ||
implementation project(':api:api-base') | ||
|
||
testImplementation project(path: ':api:api-base', configuration: 'testOutput') | ||
|
||
testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: project.versions.junit5 | ||
testRuntimeOnly group: 'org.junit.jupiter', name: 'junit-jupiter-engine', version: project.versions.junit5 | ||
} |
211 changes: 211 additions & 0 deletions
211
...e-v3/src/main/java/com/thoughtworks/go/apiv3/stageinstance/StageInstanceControllerV3.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,211 @@ | ||
/* | ||
* Copyright 2020 ThoughtWorks, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.thoughtworks.go.apiv3.stageinstance; | ||
|
||
import com.thoughtworks.go.api.ApiController; | ||
import com.thoughtworks.go.api.ApiVersion; | ||
import com.thoughtworks.go.api.representers.JsonReader; | ||
import com.thoughtworks.go.api.spring.ApiAuthenticationHelper; | ||
import com.thoughtworks.go.api.util.GsonTransformer; | ||
import com.thoughtworks.go.api.util.HaltApiResponses; | ||
import com.thoughtworks.go.apiv3.stageinstance.representers.StageInstancesRepresenter; | ||
import com.thoughtworks.go.apiv3.stageinstance.representers.StageRepresenter; | ||
import com.thoughtworks.go.domain.JobInstance; | ||
import com.thoughtworks.go.domain.NullStage; | ||
import com.thoughtworks.go.domain.PipelineRunIdInfo; | ||
import com.thoughtworks.go.domain.Stage; | ||
import com.thoughtworks.go.presentation.pipelinehistory.StageInstanceModels; | ||
import com.thoughtworks.go.server.service.ScheduleService; | ||
import com.thoughtworks.go.server.service.StageService; | ||
import com.thoughtworks.go.server.service.result.HttpLocalizedOperationResult; | ||
import com.thoughtworks.go.server.service.result.HttpOperationResult; | ||
import com.thoughtworks.go.serverhealth.HealthStateScope; | ||
import com.thoughtworks.go.serverhealth.HealthStateType; | ||
import com.thoughtworks.go.spark.Routes; | ||
import com.thoughtworks.go.spark.spring.SparkSpringController; | ||
import org.springframework.beans.factory.annotation.Autowired; | ||
import org.springframework.stereotype.Component; | ||
import spark.Request; | ||
import spark.Response; | ||
|
||
import java.io.IOException; | ||
import java.util.List; | ||
import java.util.Optional; | ||
import java.util.Set; | ||
import java.util.stream.Collectors; | ||
|
||
import static spark.Spark.*; | ||
|
||
@Component | ||
public class StageInstanceControllerV3 extends ApiController implements SparkSpringController { | ||
private final static String JOB_NAMES_PROPERTY = "jobs"; | ||
private final ApiAuthenticationHelper apiAuthenticationHelper; | ||
private final StageService stageService; | ||
private final ScheduleService scheduleService; | ||
|
||
@Autowired | ||
public StageInstanceControllerV3(ApiAuthenticationHelper apiAuthenticationHelper, StageService stageService, ScheduleService scheduleService) { | ||
super(ApiVersion.v3); | ||
this.apiAuthenticationHelper = apiAuthenticationHelper; | ||
this.stageService = stageService; | ||
this.scheduleService = scheduleService; | ||
} | ||
|
||
@Override | ||
public String controllerBasePath() { | ||
return Routes.Stage.BASE; | ||
} | ||
|
||
@Override | ||
public void setupRoutes() { | ||
path(controllerBasePath(), () -> { | ||
before("/*", mimeType, this::setContentType); | ||
before("/*", mimeType, this::verifyContentType); | ||
|
||
before(Routes.Stage.TRIGGER_FAILED_JOBS_PATH, mimeType, apiAuthenticationHelper::checkPipelineGroupOperateOfPipelineOrGroupInURLUserAnd403); | ||
before(Routes.Stage.TRIGGER_SELECTED_JOBS_PATH, mimeType, apiAuthenticationHelper::checkPipelineGroupOperateOfPipelineOrGroupInURLUserAnd403); | ||
before(Routes.Stage.CANCEL_STAGE_PATH, mimeType, apiAuthenticationHelper::checkPipelineGroupOperateOfPipelineOrGroupInURLUserAnd403); | ||
before(Routes.Stage.INSTANCE_V2, mimeType, apiAuthenticationHelper::checkPipelineViewPermissionsAnd403); | ||
before(Routes.Stage.STAGE_HISTORY, mimeType, apiAuthenticationHelper::checkPipelineViewPermissionsAnd403); | ||
|
||
post(Routes.Stage.TRIGGER_FAILED_JOBS_PATH, mimeType, this::rerunFailedJobs); | ||
post(Routes.Stage.TRIGGER_SELECTED_JOBS_PATH, mimeType, this::rerunSelectedJobs); | ||
post(Routes.Stage.CANCEL_STAGE_PATH, mimeType, this::cancelStage); | ||
get(Routes.Stage.INSTANCE_V2, mimeType, this::instanceByCounter); | ||
get(Routes.Stage.STAGE_HISTORY, mimeType, this::history); | ||
}); | ||
} | ||
|
||
public String rerunSelectedJobs(Request req, Response res) throws IOException { | ||
HttpOperationResult result = new HttpOperationResult(); | ||
haltIfRequestBodyDoesNotContainPropertyJobs(req); | ||
|
||
JsonReader requestBody = GsonTransformer.getInstance().jsonReaderFrom(req.body()); | ||
List<String> requestedJobs = requestBody.readStringArrayIfPresent(JOB_NAMES_PROPERTY).get(); | ||
|
||
Optional<Stage> optionalStage = getStageFromRequestParam(req, result); | ||
optionalStage.ifPresent(stage -> { | ||
HealthStateType healthStateType = HealthStateType.general(HealthStateScope.forStage(stage.getIdentifier() | ||
.getPipelineName(), stage.getName())); | ||
|
||
Set<String> jobsInStage = stage.getJobInstances() | ||
.stream() | ||
.map(JobInstance::getName) | ||
.collect(Collectors.toSet()); | ||
|
||
List<String> unknownJobs = requestedJobs.stream() | ||
.filter(jobToRun -> !jobsInStage.contains(jobToRun)) | ||
.collect(Collectors.toList()); | ||
|
||
if (unknownJobs.isEmpty()) { | ||
scheduleService.rerunJobs(stage, requestedJobs, result); | ||
} else { | ||
String msg = String.format("Job(s) %s does not exist in stage '%s'.", unknownJobs, stage.getIdentifier().getStageLocator()); | ||
result.notFound(msg, "", healthStateType); | ||
} | ||
|
||
}); | ||
|
||
return renderHTTPOperationResult(result, req, res); | ||
} | ||
|
||
public String rerunFailedJobs(Request req, Response res) throws IOException { | ||
HttpOperationResult result = new HttpOperationResult(); | ||
|
||
Optional<Stage> optionalStage = getStageFromRequestParam(req, result); | ||
optionalStage.ifPresent(stage -> scheduleService.rerunFailedJobs(stage, result)); | ||
return renderHTTPOperationResult(result, req, res); | ||
} | ||
|
||
public String cancelStage(Request req, Response res) throws Exception { | ||
HttpOperationResult result = new HttpOperationResult(); | ||
Optional<Stage> optionalStage = getStageFromRequestParam(req, result); | ||
if (!result.isSuccess()) { | ||
return renderHTTPOperationResult(result, req, res); | ||
} | ||
|
||
HttpLocalizedOperationResult localizedOperationResult = new HttpLocalizedOperationResult(); | ||
scheduleService.cancelAndTriggerRelevantStages(optionalStage.get().getId(), currentUsername(), localizedOperationResult); | ||
return renderHTTPOperationResult(localizedOperationResult, req, res); | ||
} | ||
|
||
public String instanceByCounter(Request req, Response res) throws IOException { | ||
String pipelineName = req.params("pipeline_name"); | ||
String pipelineCounter = req.params("pipeline_counter"); | ||
String stageName = req.params("stage_name"); | ||
String stageCounter = req.params("stage_counter"); | ||
HttpOperationResult result = new HttpOperationResult(); | ||
|
||
Stage stageModel = stageService.findStageWithIdentifier(pipelineName, | ||
Integer.parseInt(pipelineCounter), | ||
stageName, | ||
stageCounter, | ||
currentUsername().getUsername().toString(), | ||
result); | ||
|
||
if (result.canContinue()) { | ||
return writerForTopLevelObject(req, res, writer -> StageRepresenter.toJSON(writer, stageModel)); | ||
} else { | ||
return renderHTTPOperationResult(result, req, res); | ||
} | ||
} | ||
|
||
public String history(Request request, Response response) throws IOException { | ||
String pipelineName = request.params("pipeline_name"); | ||
String stageName = request.params("stage_name"); | ||
Long after = getCursor(request, "after"); | ||
Long before = getCursor(request, "before"); | ||
int pageSize = getPageSize(request); | ||
|
||
StageInstanceModels stageInstanceModels = stageService.findStageHistoryViaCursor(currentUsername(), pipelineName, stageName, after, before, pageSize); | ||
PipelineRunIdInfo latestAndOldestPipelineIds = stageService.getOldestAndLatestStageInstanceId(currentUsername(), pipelineName, stageName); | ||
return writerForTopLevelObject(request, response, writer -> StageInstancesRepresenter.toJSON(writer, stageInstanceModels, latestAndOldestPipelineIds)); | ||
} | ||
|
||
private Optional<Stage> getStageFromRequestParam(Request request, HttpOperationResult operationResult) { | ||
String pipelineName = request.params("pipeline_name"); | ||
String pipelineCounter = request.params("pipeline_counter"); | ||
String stageName = request.params("stage_name"); | ||
String stageCounter = request.params("stage_counter"); | ||
|
||
Stage stage = stageService.findStageWithIdentifier(pipelineName, | ||
Integer.parseInt(pipelineCounter), | ||
stageName, | ||
stageCounter, | ||
currentUsername().getUsername().toString(), | ||
operationResult); | ||
|
||
if (!operationResult.isSuccess()) { | ||
return Optional.empty(); | ||
} | ||
|
||
if (stage == null || stage instanceof NullStage) { | ||
String message = String.format("Stage '%s' with counter '%s' not found. Please make sure specified stage or stage run with specified counter exists.", stageName, stageCounter); | ||
operationResult.notFound("Not Found", message, HealthStateType.general(HealthStateScope.GLOBAL)); | ||
return Optional.empty(); | ||
} | ||
|
||
return Optional.ofNullable(stage); | ||
} | ||
|
||
private void haltIfRequestBodyDoesNotContainPropertyJobs(Request req) { | ||
JsonReader requestBody = GsonTransformer.getInstance().jsonReaderFrom(req.body()); | ||
if (!requestBody.hasJsonObject(JOB_NAMES_PROPERTY)) { | ||
throw HaltApiResponses.haltBecauseOfReason("Could not read property '%s' in request body", JOB_NAMES_PROPERTY); | ||
} | ||
requestBody.readStringArrayIfPresent(JOB_NAMES_PROPERTY); | ||
} | ||
} |
34 changes: 34 additions & 0 deletions
34
.../java/com/thoughtworks/go/apiv3/stageinstance/representers/JobHistoryItemRepresenter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/* | ||
* Copyright 2020 ThoughtWorks, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.thoughtworks.go.apiv3.stageinstance.representers; | ||
|
||
import com.thoughtworks.go.api.base.OutputWriter; | ||
import com.thoughtworks.go.presentation.pipelinehistory.JobHistoryItem; | ||
|
||
public class JobHistoryItemRepresenter { | ||
public static void toJSON(OutputWriter jsonWriter, JobHistoryItem jobHistoryItem) { | ||
jsonWriter.add("name", jobHistoryItem.getName()); | ||
if (jobHistoryItem.getState() != null) { | ||
jsonWriter.add("state", jobHistoryItem.getState().toString()); | ||
} | ||
if (jobHistoryItem.getResult() != null) { | ||
jsonWriter.add("result", jobHistoryItem.getResult().toString()); | ||
} | ||
if (jobHistoryItem.getScheduledDate() != null) { | ||
jsonWriter.add("scheduled_date", jobHistoryItem.getScheduledDate().getTime()); | ||
} | ||
} | ||
} |
50 changes: 50 additions & 0 deletions
50
...ain/java/com/thoughtworks/go/apiv3/stageinstance/representers/JobInstanceRepresenter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
/* | ||
* Copyright 2020 ThoughtWorks, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.thoughtworks.go.apiv3.stageinstance.representers; | ||
|
||
import com.thoughtworks.go.api.base.OutputWriter; | ||
import com.thoughtworks.go.domain.JobInstance; | ||
|
||
public class JobInstanceRepresenter { | ||
public static void toJSON(OutputWriter jsonWriter, JobInstance jobInstance) { | ||
jsonWriter.add("name", jobInstance.getName()); | ||
if (jobInstance.getState() != null) { | ||
jsonWriter.add("state", jobInstance.getState().toString()); | ||
} | ||
if (jobInstance.getResult() != null) { | ||
jsonWriter.add("result", jobInstance.getResult().toString()); | ||
} | ||
if (jobInstance.getScheduledDate() != null) { | ||
jsonWriter.add("scheduled_date", jobInstance.getScheduledDate().getTime()); | ||
} | ||
jsonWriter.add("rerun", jobInstance.isRerun()); | ||
if (jobInstance.getOriginalJobId() == null) { | ||
jsonWriter.add("original_job_id", (String) null); | ||
} | ||
else { | ||
jsonWriter.add("original_job_id", jobInstance.getOriginalJobId()); | ||
} | ||
jsonWriter.addWithDefaultIfBlank("agent_uuid", jobInstance.getAgentUuid(), (String) null); | ||
jsonWriter.add("pipeline_name", (String) null); | ||
jsonWriter.add("pipeline_counter", (String) null); | ||
jsonWriter.add("stage_name", (String) null); | ||
jsonWriter.add("stage_counter", (String) null); | ||
jsonWriter.addChildList("job_state_transitions", jobStateTransitionsWriter -> jobInstance.getTransitions().forEach( | ||
jobStateTransition -> jobStateTransitionsWriter.addChild( | ||
jobStateTransitionWriter -> JobStateTransitionRepresenter.toJSON(jobStateTransitionWriter, jobStateTransition)))); | ||
|
||
} | ||
} |
30 changes: 30 additions & 0 deletions
30
...a/com/thoughtworks/go/apiv3/stageinstance/representers/JobStateTransitionRepresenter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
/* | ||
* Copyright 2020 ThoughtWorks, Inc. | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package com.thoughtworks.go.apiv3.stageinstance.representers; | ||
|
||
import com.thoughtworks.go.api.base.OutputWriter; | ||
import com.thoughtworks.go.domain.JobStateTransition; | ||
|
||
public class JobStateTransitionRepresenter { | ||
public static void toJSON(OutputWriter jsonWriter, JobStateTransition jobStateTransition) { | ||
if (jobStateTransition.getCurrentState() != null) { | ||
jsonWriter.add("state", jobStateTransition.getCurrentState().toString()); | ||
} | ||
if (jobStateTransition.getStateChangeTime() != null) { | ||
jsonWriter.add("state_change_time", jobStateTransition.getStateChangeTime().getTime()); | ||
} | ||
} | ||
} |
Oops, something went wrong.