diff --git a/multiapps-controller-api/pom.xml b/multiapps-controller-api/pom.xml
index 69c7fc8c07..464ab07c3e 100644
--- a/multiapps-controller-api/pom.xml
+++ b/multiapps-controller-api/pom.xml
@@ -120,7 +120,27 @@
mtarest
MTA REST API
- This is the API of the Cloud Foundry MultiApps Controller
+ This is the API of the Cloud Foundry MultiApps Controller.
+
+## Typical deploy sequence
+
+Step 1 (optional) — Obtain a CSRF token:
+ GET /api/v1/csrf-token
+
+Step 2 — Upload the MTA archive:
+ POST /api/v1/spaces/{spaceGuid}/files
+
+Step 3 — Start a deploy operation:
+ POST /api/v1/spaces/{spaceGuid}/operations
+ Request body: Operation object with processType=DEPLOY and mtaId.
+
+Step 4 — Poll operation status:
+ GET /api/v1/spaces/{spaceGuid}/operations/{operationId}
+ Repeat until state is one of: FINISHED, ERROR, ABORTED, ACTION_REQUIRED.
+
+Step 5 (optional) — Execute an action:
+ POST /api/v1/spaces/{spaceGuid}/operations/{operationId}?actionId=abort|retry
+ Use when state is ACTION_REQUIRED or to abort a running operation.
1.4.0
@@ -151,6 +171,37 @@
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+
+
+ quote-yaml-response-codes
+ compile
+
+ run
+
+
+
+
+
+
+
+
+
+
diff --git a/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/Constants.java b/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/Constants.java
index 6462e97470..e77bb356c0 100644
--- a/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/Constants.java
+++ b/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/Constants.java
@@ -71,4 +71,24 @@ private Endpoints() {
}
+ public static class HttpResponses {
+
+ private HttpResponses() {
+ }
+
+ public static final String OK = "OK";
+ public static final String CREATED = "Created";
+ public static final String NO_CONTENT = "No Content";
+ public static final String ACCEPTED = "Accepted";
+ public static final String BAD_REQUEST = "Bad Request";
+ public static final String UNAUTHORIZED = "Unauthorized";
+ public static final String FORBIDDEN = "Forbidden";
+ public static final String NOT_FOUND = "Not Found";
+ public static final String CONFLICT = "Conflict";
+ public static final String REQUEST_ENTITY_TOO_LARGE = "Request Entity Too Large";
+ public static final String UNSUPPORTED_MEDIA_TYPE = "Unsupported Media Type";
+ public static final String INTERNAL_SERVER_ERROR = "Internal Server Error";
+
+ }
+
}
diff --git a/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/CsrfTokenApi.java b/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/CsrfTokenApi.java
index d84179ba2d..7658e5f22f 100644
--- a/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/CsrfTokenApi.java
+++ b/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/CsrfTokenApi.java
@@ -2,8 +2,9 @@
import jakarta.inject.Inject;
-import org.cloudfoundry.multiapps.controller.api.CsrfTokenApiService;
+import org.cloudfoundry.multiapps.controller.api.Constants.HttpResponses;
import org.cloudfoundry.multiapps.controller.api.Constants.Resources;
+import org.cloudfoundry.multiapps.controller.api.CsrfTokenApiService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
@@ -24,10 +25,13 @@ public class CsrfTokenApi {
private CsrfTokenApiService delegate;
@GetMapping
- @ApiOperation(value = "", notes = "Retrieves a csrf-token header ", authorizations = { @Authorization(value = "oauth2", scopes = {
+ @ApiOperation(value = "Retrieves a CSRF token header", notes = "Retrieves a csrf-token header", authorizations = { @Authorization(value = "oauth2", scopes = {
}) }, tags = {})
- @ApiResponses(value = { @ApiResponse(code = 204, message = "No Content") })
+ @ApiResponses(value = { @ApiResponse(code = 204, message = HttpResponses.NO_CONTENT),
+ @ApiResponse(code = 401, message = HttpResponses.UNAUTHORIZED),
+ @ApiResponse(code = 403, message = HttpResponses.FORBIDDEN),
+ @ApiResponse(code = 500, message = HttpResponses.INTERNAL_SERVER_ERROR) })
public ResponseEntity getCsrfToken() {
return delegate.getCsrfToken();
}
diff --git a/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/FilesApi.java b/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/FilesApi.java
index beb6dc1a84..db1f9365a4 100644
--- a/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/FilesApi.java
+++ b/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/FilesApi.java
@@ -5,6 +5,7 @@
import jakarta.inject.Inject;
import org.cloudfoundry.multiapps.controller.api.Constants.Endpoints;
+import org.cloudfoundry.multiapps.controller.api.Constants.HttpResponses;
import org.cloudfoundry.multiapps.controller.api.Constants.PathVariables;
import org.cloudfoundry.multiapps.controller.api.Constants.RequestVariables;
import org.cloudfoundry.multiapps.controller.api.Constants.Resources;
@@ -39,11 +40,14 @@ public class FilesApi {
private FilesApiService delegate;
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
- @ApiOperation(value = "", nickname = "getMtaFiles", notes = "Retrieves all Multi-Target Application files ", response = FileMetadata.class, responseContainer = "List", authorizations = {
+ @ApiOperation(value = "Retrieve all uploaded MTA archive files in a space", nickname = "getMtaFiles", notes = "Retrieves all Multi-Target Application files", response = FileMetadata.class, responseContainer = "List", authorizations = {
@Authorization(value = "oauth2", scopes = {
}) }, tags = {})
- @ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = FileMetadata.class, responseContainer = "List") })
+ @ApiResponses(value = { @ApiResponse(code = 200, message = HttpResponses.OK, response = FileMetadata.class, responseContainer = "List"),
+ @ApiResponse(code = 401, message = HttpResponses.UNAUTHORIZED),
+ @ApiResponse(code = 403, message = HttpResponses.FORBIDDEN),
+ @ApiResponse(code = 500, message = HttpResponses.INTERNAL_SERVER_ERROR) })
public ResponseEntity>
getFiles(@ApiParam(value = "GUID of space with mtas") @PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
@ApiParam(value = "Filter mtas by namespace") @RequestParam(name = RequestVariables.NAMESPACE, required = false) String namespace) {
@@ -51,11 +55,17 @@ public class FilesApi {
}
@PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- @ApiOperation(value = "", nickname = "uploadMtaFile", notes = "Uploads a Multi Target Application archive or an Extension Descriptor ", response = FileMetadata.class, authorizations = {
+ @ApiOperation(value = "Upload an MTA archive or Extension Descriptor", nickname = "uploadMtaFile", notes = "Uploads a Multi Target Application archive or an Extension Descriptor", response = FileMetadata.class, authorizations = {
@Authorization(value = "oauth2", scopes = {
}) }, tags = {})
- @ApiResponses(value = { @ApiResponse(code = 201, message = "Created", response = FileMetadata.class) })
+ @ApiResponses(value = { @ApiResponse(code = 201, message = HttpResponses.CREATED, response = FileMetadata.class),
+ @ApiResponse(code = 400, message = HttpResponses.BAD_REQUEST),
+ @ApiResponse(code = 401, message = HttpResponses.UNAUTHORIZED),
+ @ApiResponse(code = 403, message = HttpResponses.FORBIDDEN),
+ @ApiResponse(code = 413, message = HttpResponses.REQUEST_ENTITY_TOO_LARGE),
+ @ApiResponse(code = 415, message = HttpResponses.UNSUPPORTED_MEDIA_TYPE),
+ @ApiResponse(code = 500, message = HttpResponses.INTERNAL_SERVER_ERROR) })
public ResponseEntity
uploadFile(MultipartHttpServletRequest request,
@ApiParam(value = "GUID of space you wish to deploy in") @PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
@@ -64,11 +74,16 @@ public class FilesApi {
}
@PostMapping(path = Endpoints.ASYNC_UPLOAD, consumes = MediaType.APPLICATION_JSON_VALUE)
- @ApiOperation(value = "", nickname = "startUploadFromUrl", notes = "Uploads a Multi Target Application archive or an Extension Descriptor from a remote endpoint", authorizations = {
+ @ApiOperation(value = "Start an asynchronous MTA archive upload from a URL", nickname = "startUploadFromUrl", notes = "Uploads a Multi Target Application archive or an Extension Descriptor from a remote endpoint", authorizations = {
@Authorization(value = "oauth2", scopes = {
}) }, tags = {})
- @ApiResponses(value = { @ApiResponse(code = 202, message = "Accepted") })
+ @ApiResponses(value = { @ApiResponse(code = 202, message = HttpResponses.ACCEPTED),
+ @ApiResponse(code = 400, message = HttpResponses.BAD_REQUEST),
+ @ApiResponse(code = 401, message = HttpResponses.UNAUTHORIZED),
+ @ApiResponse(code = 403, message = HttpResponses.FORBIDDEN),
+ @ApiResponse(code = 413, message = HttpResponses.REQUEST_ENTITY_TOO_LARGE),
+ @ApiResponse(code = 500, message = HttpResponses.INTERNAL_SERVER_ERROR) })
public ResponseEntity
startUploadFromUrl(@ApiParam(value = "GUID of space you wish to deploy in") @PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
@ApiParam(value = "file namespace") @RequestParam(name = RequestVariables.NAMESPACE, required = false) String namespace,
@@ -77,12 +92,16 @@ public class FilesApi {
}
@GetMapping(path = Endpoints.ASYNC_UPLOAD_JOB, produces = MediaType.APPLICATION_JSON_VALUE)
- @ApiOperation(value = "", nickname = "getAsyncUploadJob", notes = "Gets the status of an async upload job", response = AsyncUploadResult.class, authorizations = {
+ @ApiOperation(value = "Get the status of an asynchronous URL upload job", nickname = "getAsyncUploadJob", notes = "Gets the status of an async upload job", response = AsyncUploadResult.class, authorizations = {
@Authorization(value = "oauth2", scopes = {
}) }, tags = {})
- @ApiResponses(value = { @ApiResponse(code = 200, message = "OK"),
- @ApiResponse(code = 201, message = "Created", response = AsyncUploadResult.class) })
+ @ApiResponses(value = { @ApiResponse(code = 200, message = HttpResponses.OK),
+ @ApiResponse(code = 201, message = HttpResponses.CREATED, response = AsyncUploadResult.class),
+ @ApiResponse(code = 401, message = HttpResponses.UNAUTHORIZED),
+ @ApiResponse(code = 403, message = HttpResponses.FORBIDDEN),
+ @ApiResponse(code = 404, message = HttpResponses.NOT_FOUND),
+ @ApiResponse(code = 500, message = HttpResponses.INTERNAL_SERVER_ERROR) })
public ResponseEntity
getUploadFromUrlJob(@ApiParam(value = "GUID of space you wish to deploy in") @PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
@ApiParam(value = "file namespace") @RequestParam(name = RequestVariables.NAMESPACE, required = false) String namespace,
diff --git a/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/InfoApi.java b/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/InfoApi.java
index 9570a32eac..1a52de5dea 100644
--- a/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/InfoApi.java
+++ b/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/InfoApi.java
@@ -3,6 +3,7 @@
import jakarta.inject.Inject;
import org.cloudfoundry.multiapps.controller.api.InfoApiService;
+import org.cloudfoundry.multiapps.controller.api.Constants.HttpResponses;
import org.cloudfoundry.multiapps.controller.api.Constants.Resources;
import org.cloudfoundry.multiapps.controller.api.model.Info;
import org.springframework.http.MediaType;
@@ -26,11 +27,14 @@ public class InfoApi {
private InfoApiService delegate;
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
- @ApiOperation(value = "", notes = "Retrieve information about the Deploy Service application ", response = Info.class, authorizations = {
+ @ApiOperation(value = "Retrieve information about the Deploy Service application", notes = "Retrieve information about the Deploy Service application", response = Info.class, authorizations = {
@Authorization(value = "oauth2", scopes = {
}) }, tags = {})
- @ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = Info.class) })
+ @ApiResponses(value = { @ApiResponse(code = 200, message = HttpResponses.OK, response = Info.class),
+ @ApiResponse(code = 401, message = HttpResponses.UNAUTHORIZED),
+ @ApiResponse(code = 403, message = HttpResponses.FORBIDDEN),
+ @ApiResponse(code = 500, message = HttpResponses.INTERNAL_SERVER_ERROR) })
public ResponseEntity getInfo() {
return delegate.getInfo();
}
diff --git a/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/MtasApi.java b/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/MtasApi.java
index 8a7433f537..1a38c30570 100644
--- a/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/MtasApi.java
+++ b/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/MtasApi.java
@@ -6,6 +6,7 @@
import org.cloudfoundry.multiapps.controller.api.MtasApiService;
import org.cloudfoundry.multiapps.controller.api.Constants.Endpoints;
+import org.cloudfoundry.multiapps.controller.api.Constants.HttpResponses;
import org.cloudfoundry.multiapps.controller.api.Constants.PathVariables;
import org.cloudfoundry.multiapps.controller.api.Constants.RequestVariables;
import org.cloudfoundry.multiapps.controller.api.Constants.Resources;
@@ -33,22 +34,29 @@ public class MtasApi {
private MtasApiService delegate;
@GetMapping(path = Endpoints.MTA, produces = MediaType.APPLICATION_JSON_VALUE)
- @ApiOperation(value = "", notes = "Retrieves Multi-Target Application in a space ", response = Mta.class, authorizations = {
+ @ApiOperation(value = "Retrieve a specific deployed MTA by ID", notes = "Retrieves Multi-Target Application in a space", response = Mta.class, authorizations = {
@Authorization(value = "oauth2", scopes = {
}) }, tags = {})
- @ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = Mta.class) })
+ @ApiResponses(value = { @ApiResponse(code = 200, message = HttpResponses.OK, response = Mta.class),
+ @ApiResponse(code = 401, message = HttpResponses.UNAUTHORIZED),
+ @ApiResponse(code = 403, message = HttpResponses.FORBIDDEN),
+ @ApiResponse(code = 404, message = HttpResponses.NOT_FOUND),
+ @ApiResponse(code = 500, message = HttpResponses.INTERNAL_SERVER_ERROR) })
public ResponseEntity getMta(@ApiParam(value = "GUID of space with mtas") @PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
@ApiParam(value = "mtaID of requested mta") @PathVariable(RequestVariables.MTA_ID) String mtaId) {
return delegate.getMta(spaceGuid, mtaId);
}
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
- @ApiOperation(value = "", notes = "Retrieves all Multi-Target Applications in a space ", response = Mta.class, responseContainer = "List", authorizations = {
+ @ApiOperation(value = "Retrieve all deployed MTAs in a space", notes = "Retrieves all Multi-Target Applications in a space", response = Mta.class, responseContainer = "List", authorizations = {
@Authorization(value = "oauth2", scopes = {
}) }, tags = {})
- @ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = Mta.class, responseContainer = "List") })
+ @ApiResponses(value = { @ApiResponse(code = 200, message = HttpResponses.OK, response = Mta.class, responseContainer = "List"),
+ @ApiResponse(code = 401, message = HttpResponses.UNAUTHORIZED),
+ @ApiResponse(code = 403, message = HttpResponses.FORBIDDEN),
+ @ApiResponse(code = 500, message = HttpResponses.INTERNAL_SERVER_ERROR) })
public ResponseEntity>
getMtas(@ApiParam(value = "GUID of space with mtas") @PathVariable(PathVariables.SPACE_GUID) String spaceGuid) {
return delegate.getMtas(spaceGuid);
diff --git a/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/OperationsApi.java b/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/OperationsApi.java
index f94e5e130f..fb0d628604 100644
--- a/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/OperationsApi.java
+++ b/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v1/OperationsApi.java
@@ -11,6 +11,7 @@
import jakarta.inject.Inject;
import jakarta.servlet.http.HttpServletRequest;
import org.cloudfoundry.multiapps.controller.api.Constants.Endpoints;
+import org.cloudfoundry.multiapps.controller.api.Constants.HttpResponses;
import org.cloudfoundry.multiapps.controller.api.Constants.PathVariables;
import org.cloudfoundry.multiapps.controller.api.Constants.QueryVariables;
import org.cloudfoundry.multiapps.controller.api.Constants.RequestVariables;
@@ -37,85 +38,114 @@ public class OperationsApi {
private OperationsApiService delegate;
@PostMapping(path = Endpoints.OPERATION)
- @ApiOperation(value = "", notes = "Executes a particular action over Multi-Target Application operation ", authorizations = {
+ @ApiOperation(value = "Execute an action on an MTA operation", notes = "Executes a particular action over Multi-Target Application operation", authorizations = {
@Authorization(value = "oauth2", scopes = {
}) }, tags = {})
- @ApiResponses(value = { @ApiResponse(code = 202, message = "Accepted") })
- public ResponseEntity executeOperationAction(@PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
- @PathVariable(PathVariables.OPERATION_ID) String operationId,
- @RequestParam(PathVariables.ACTION_ID) String actionId) {
+ @ApiResponses(value = { @ApiResponse(code = 202, message = HttpResponses.ACCEPTED),
+ @ApiResponse(code = 400, message = HttpResponses.BAD_REQUEST),
+ @ApiResponse(code = 401, message = HttpResponses.UNAUTHORIZED),
+ @ApiResponse(code = 403, message = HttpResponses.FORBIDDEN),
+ @ApiResponse(code = 404, message = HttpResponses.NOT_FOUND),
+ @ApiResponse(code = 500, message = HttpResponses.INTERNAL_SERVER_ERROR) })
+ public ResponseEntity executeOperationAction(@ApiParam(value = "GUID of the CF space containing the operation") @PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
+ @ApiParam(value = "Process ID of the MTA operation") @PathVariable(PathVariables.OPERATION_ID) String operationId,
+ @ApiParam(value = "Action to perform, e.g. abort or retry") @RequestParam(PathVariables.ACTION_ID) String actionId) {
return delegate.executeOperationAction(spaceGuid, operationId, actionId);
}
@GetMapping(path = Endpoints.OPERATION, produces = MediaType.APPLICATION_JSON_VALUE)
- @ApiOperation(value = "", nickname = "getMtaOperation", notes = "Retrieves Multi-Target Application operation ", response = Operation.class, authorizations = {
+ @ApiOperation(value = "Retrieve a specific MTA operation", nickname = "getMtaOperation", notes = "Retrieves Multi-Target Application operation", response = Operation.class, authorizations = {
@Authorization(value = "oauth2", scopes = {
}) }, tags = {})
- @ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = Operation.class) })
+ @ApiResponses(value = { @ApiResponse(code = 200, message = HttpResponses.OK, response = Operation.class),
+ @ApiResponse(code = 401, message = HttpResponses.UNAUTHORIZED),
+ @ApiResponse(code = 403, message = HttpResponses.FORBIDDEN),
+ @ApiResponse(code = 404, message = HttpResponses.NOT_FOUND),
+ @ApiResponse(code = 500, message = HttpResponses.INTERNAL_SERVER_ERROR) })
public ResponseEntity
- getOperation(@PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
- @PathVariable(PathVariables.OPERATION_ID) String operationId,
- @ApiParam(value = "Adds the specified property in the response body ") @RequestParam(name = "embed", required = false) String embed) {
+ getOperation(@ApiParam(value = "GUID of the CF space containing the operation") @PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
+ @ApiParam(value = "Process ID of the MTA operation") @PathVariable(PathVariables.OPERATION_ID) String operationId,
+ @ApiParam(value = "Adds the specified property in the response body") @RequestParam(name = "embed", required = false) String embed) {
return delegate.getOperation(spaceGuid, operationId, embed);
}
@GetMapping(path = Endpoints.OPERATION_LOGS, produces = MediaType.APPLICATION_JSON_VALUE)
- @ApiOperation(value = "", nickname = "getMtaOperationLogs", notes = "Retrieves the logs Multi-Target Application operation ", response = Log.class, responseContainer = "List", authorizations = {
+ @ApiOperation(value = "List log files for an MTA operation", nickname = "getMtaOperationLogs", notes = "Retrieves the logs Multi-Target Application operation", response = Log.class, responseContainer = "List", authorizations = {
@Authorization(value = "oauth2", scopes = {
}) }, tags = {})
- @ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = Log.class, responseContainer = "List") })
- public ResponseEntity> getOperationLogs(@PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
- @PathVariable(PathVariables.OPERATION_ID) String operationId) {
+ @ApiResponses(value = { @ApiResponse(code = 200, message = HttpResponses.OK, response = Log.class, responseContainer = "List"),
+ @ApiResponse(code = 401, message = HttpResponses.UNAUTHORIZED),
+ @ApiResponse(code = 403, message = HttpResponses.FORBIDDEN),
+ @ApiResponse(code = 404, message = HttpResponses.NOT_FOUND),
+ @ApiResponse(code = 500, message = HttpResponses.INTERNAL_SERVER_ERROR) })
+ public ResponseEntity> getOperationLogs(@ApiParam(value = "GUID of the CF space containing the operation") @PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
+ @ApiParam(value = "Process ID of the MTA operation") @PathVariable(PathVariables.OPERATION_ID) String operationId) {
return delegate.getOperationLogs(spaceGuid, operationId);
}
@GetMapping(path = Endpoints.OPERATION_LOG_CONTENT, produces = MediaType.TEXT_PLAIN_VALUE)
- @ApiOperation(value = "", nickname = "getMtaOperationLogContent", notes = "Retrieves the log content for Multi-Target Application operation ", response = String.class, authorizations = {
+ @ApiOperation(value = "Retrieve the content of a specific operation log", nickname = "getMtaOperationLogContent", notes = "Retrieves the log content for Multi-Target Application operation", response = String.class, authorizations = {
@Authorization(value = "oauth2", scopes = {
}) }, tags = {})
- @ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = String.class) })
- public ResponseEntity getOperationLogContent(@PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
- @PathVariable(PathVariables.OPERATION_ID) String operationId,
- @PathVariable(PathVariables.LOG_ID) String logId) {
+ @ApiResponses(value = { @ApiResponse(code = 200, message = HttpResponses.OK, response = String.class),
+ @ApiResponse(code = 401, message = HttpResponses.UNAUTHORIZED),
+ @ApiResponse(code = 403, message = HttpResponses.FORBIDDEN),
+ @ApiResponse(code = 404, message = HttpResponses.NOT_FOUND),
+ @ApiResponse(code = 500, message = HttpResponses.INTERNAL_SERVER_ERROR) })
+ public ResponseEntity getOperationLogContent(@ApiParam(value = "GUID of the CF space containing the operation") @PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
+ @ApiParam(value = "Process ID of the MTA operation") @PathVariable(PathVariables.OPERATION_ID) String operationId,
+ @ApiParam(value = "ID of the log file to retrieve") @PathVariable(PathVariables.LOG_ID) String logId) {
return delegate.getOperationLogContent(spaceGuid, operationId, logId);
}
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
- @ApiOperation(value = "", nickname = "getMtaOperations", notes = "Retrieves Multi-Target Application operations ", response = Operation.class, responseContainer = "List", authorizations = {
+ @ApiOperation(value = "List MTA operations in a space", nickname = "getMtaOperations", notes = "Retrieves Multi-Target Application operations", response = Operation.class, responseContainer = "List", authorizations = {
@Authorization(value = "oauth2", scopes = {
}) }, tags = {})
- @ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = Operation.class, responseContainer = "List") })
- public ResponseEntity> getOperations(@PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
- @RequestParam(name = RequestVariables.MTA_ID, required = false) String mtaId,
- @RequestParam(name = QueryVariables.LAST, required = false) Integer last,
- @RequestParam(name = QueryVariables.STATE, required = false) List states) {
+ @ApiResponses(value = { @ApiResponse(code = 200, message = HttpResponses.OK, response = Operation.class, responseContainer = "List"),
+ @ApiResponse(code = 401, message = HttpResponses.UNAUTHORIZED),
+ @ApiResponse(code = 403, message = HttpResponses.FORBIDDEN),
+ @ApiResponse(code = 500, message = HttpResponses.INTERNAL_SERVER_ERROR) })
+ public ResponseEntity> getOperations(@ApiParam(value = "GUID of the CF space") @PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
+ @ApiParam(value = "Filter operations by MTA ID") @RequestParam(name = RequestVariables.MTA_ID, required = false) String mtaId,
+ @ApiParam(value = "Return only the last N operations") @RequestParam(name = QueryVariables.LAST, required = false) Integer last,
+ @ApiParam(value = "Filter operations by state, e.g. RUNNING, FINISHED, ERROR, ABORTED") @RequestParam(name = QueryVariables.STATE, required = false) List states) {
return delegate.getOperations(spaceGuid, mtaId, states, last);
}
@GetMapping(path = Endpoints.OPERATION_ACTIONS, produces = MediaType.APPLICATION_JSON_VALUE)
- @ApiOperation(value = "", notes = "Retrieves available actions for Multi-Target Application operation ", response = String.class, responseContainer = "List", authorizations = {
+ @ApiOperation(value = "List available actions for an MTA operation", notes = "Retrieves available actions for Multi-Target Application operation", response = String.class, responseContainer = "List", authorizations = {
@Authorization(value = "oauth2", scopes = {
}) }, tags = {})
- @ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = String.class, responseContainer = "List") })
- public ResponseEntity> getOperationActions(@PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
- @PathVariable(PathVariables.OPERATION_ID) String operationId) {
+ @ApiResponses(value = { @ApiResponse(code = 200, message = HttpResponses.OK, response = String.class, responseContainer = "List"),
+ @ApiResponse(code = 401, message = HttpResponses.UNAUTHORIZED),
+ @ApiResponse(code = 403, message = HttpResponses.FORBIDDEN),
+ @ApiResponse(code = 404, message = HttpResponses.NOT_FOUND),
+ @ApiResponse(code = 500, message = HttpResponses.INTERNAL_SERVER_ERROR) })
+ public ResponseEntity> getOperationActions(@ApiParam(value = "GUID of the CF space containing the operation") @PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
+ @ApiParam(value = "Process ID of the MTA operation") @PathVariable(PathVariables.OPERATION_ID) String operationId) {
return delegate.getOperationActions(spaceGuid, operationId);
}
@PostMapping(consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
- @ApiOperation(value = "", nickname = "startMtaOperation", notes = "Starts execution of a Multi-Target Application operation ", authorizations = {
+ @ApiOperation(value = "Start a new MTA operation (deploy, undeploy, blue-green deploy)", nickname = "startMtaOperation", notes = "Starts execution of a Multi-Target Application operation", authorizations = {
@Authorization(value = "oauth2", scopes = {
}) }, tags = {})
- @ApiResponses(value = { @ApiResponse(code = 202, message = "Accepted") })
- public ResponseEntity startOperation(@PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
- @RequestBody Operation operation, HttpServletRequest httpServletRequest) {
+ @ApiResponses(value = { @ApiResponse(code = 202, message = HttpResponses.ACCEPTED),
+ @ApiResponse(code = 400, message = HttpResponses.BAD_REQUEST),
+ @ApiResponse(code = 401, message = HttpResponses.UNAUTHORIZED),
+ @ApiResponse(code = 403, message = HttpResponses.FORBIDDEN),
+ @ApiResponse(code = 409, message = HttpResponses.CONFLICT),
+ @ApiResponse(code = 500, message = HttpResponses.INTERNAL_SERVER_ERROR) })
+ public ResponseEntity startOperation(@ApiParam(value = "GUID of the CF space to deploy into") @PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
+ @ApiParam(value = "Operation descriptor specifying the process type and MTA parameters") @RequestBody Operation operation, HttpServletRequest httpServletRequest) {
return delegate.startOperation(spaceGuid, operation, httpServletRequest);
}
diff --git a/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v2/MtasApiV2.java b/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v2/MtasApiV2.java
index e89fff5469..592d5e4b9c 100644
--- a/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v2/MtasApiV2.java
+++ b/multiapps-controller-api/src/main/java/org/cloudfoundry/multiapps/controller/api/v2/MtasApiV2.java
@@ -4,6 +4,7 @@
import jakarta.inject.Inject;
+import org.cloudfoundry.multiapps.controller.api.Constants.HttpResponses;
import org.cloudfoundry.multiapps.controller.api.Constants.PathVariables;
import org.cloudfoundry.multiapps.controller.api.Constants.RequestVariables;
import org.cloudfoundry.multiapps.controller.api.Constants.Resources;
@@ -33,11 +34,14 @@ public class MtasApiV2 {
private MtasApiService delegate;
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
- @ApiOperation(value = "", notes = "Retrieves all Multi-Target Applications in a given space and namespace", response = Mta.class, responseContainer = "List", authorizations = {
+ @ApiOperation(value = "Retrieve all deployed MTAs in a space, filtered by namespace or name", notes = "Retrieves all Multi-Target Applications in a given space and namespace", response = Mta.class, responseContainer = "List", authorizations = {
@Authorization(value = "oauth2", scopes = {
}) }, tags = {})
- @ApiResponses(value = { @ApiResponse(code = 200, message = "OK", response = Mta.class, responseContainer = "List") })
+ @ApiResponses(value = { @ApiResponse(code = 200, message = HttpResponses.OK, response = Mta.class, responseContainer = "List"),
+ @ApiResponse(code = 401, message = HttpResponses.UNAUTHORIZED),
+ @ApiResponse(code = 403, message = HttpResponses.FORBIDDEN),
+ @ApiResponse(code = 500, message = HttpResponses.INTERNAL_SERVER_ERROR) })
public ResponseEntity>
getMtas(@ApiParam(value = "GUID of space with mtas") @PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
@ApiParam(value = "Filter mtas by namespace") @RequestParam(name = RequestVariables.NAMESPACE, required = false) String namespace,
diff --git a/multiapps-controller-api/src/main/resources/mtarest.yaml b/multiapps-controller-api/src/main/resources/mtarest.yaml
index 8a6de8c701..88e1e79a31 100644
--- a/multiapps-controller-api/src/main/resources/mtarest.yaml
+++ b/multiapps-controller-api/src/main/resources/mtarest.yaml
@@ -1,7 +1,15 @@
---
swagger: "2.0"
info:
- description: "This is the API of the Cloud Foundry MultiApps Controller"
+ description: "This is the API of the Cloud Foundry MultiApps Controller.\n\n## Typical\
+ \ deploy sequence\n\nStep 1 (optional) — Obtain a CSRF token:\n GET /api/v1/csrf-token\n\
+ \nStep 2 — Upload the MTA archive:\n POST /api/v1/spaces/{spaceGuid}/files\n\n\
+ Step 3 — Start a deploy operation:\n POST /api/v1/spaces/{spaceGuid}/operations\n\
+ \ Request body: Operation object with processType=DEPLOY and mtaId.\n\nStep 4\
+ \ — Poll operation status:\n GET /api/v1/spaces/{spaceGuid}/operations/{operationId}\n\
+ \ Repeat until state is one of: FINISHED, ERROR, ABORTED, ACTION_REQUIRED.\n\n\
+ Step 5 (optional) — Execute an action:\n POST /api/v1/spaces/{spaceGuid}/operations/{operationId}?actionId=abort|retry\n\
+ \ Use when state is ACTION_REQUIRED or to abort a running operation."
version: "1.4.0"
title: "MTA REST API"
contact: {}
@@ -13,34 +21,46 @@ schemes:
paths:
/api/v1/csrf-token:
get:
- summary: ""
- description: "Retrieves a csrf-token header "
+ summary: "Retrieves a CSRF token header"
+ description: "Retrieves a csrf-token header"
operationId: "getCsrfToken"
parameters: []
responses:
- 204:
+ "204":
description: "No Content"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "500":
+ description: "Internal Server Error"
security:
- oauth2: []
/api/v1/info:
get:
- summary: ""
- description: "Retrieve information about the Deploy Service application "
+ summary: "Retrieve information about the Deploy Service application"
+ description: "Retrieve information about the Deploy Service application"
operationId: "getInfo"
produces:
- "application/json"
parameters: []
responses:
- 200:
+ "200":
description: "OK"
schema:
$ref: "#/definitions/Info"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "500":
+ description: "Internal Server Error"
security:
- oauth2: []
/api/v1/spaces/{spaceGuid}/files:
get:
- summary: ""
- description: "Retrieves all Multi-Target Application files "
+ summary: "Retrieve all uploaded MTA archive files in a space"
+ description: "Retrieves all Multi-Target Application files"
operationId: "getMtaFiles"
produces:
- "application/json"
@@ -56,17 +76,23 @@ paths:
required: false
type: "string"
responses:
- 200:
+ "200":
description: "OK"
schema:
type: "array"
items:
$ref: "#/definitions/FileMetadata"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "500":
+ description: "Internal Server Error"
security:
- oauth2: []
post:
- summary: ""
- description: "Uploads a Multi Target Application archive or an Extension Descriptor "
+ summary: "Upload an MTA archive or Extension Descriptor"
+ description: "Uploads a Multi Target Application archive or an Extension Descriptor"
operationId: "uploadMtaFile"
consumes:
- "multipart/form-data"
@@ -84,19 +110,27 @@ paths:
required: false
type: "string"
responses:
- 200:
- description: "successful operation"
- schema:
- $ref: "#/definitions/FileMetadata"
- 201:
+ "201":
description: "Created"
schema:
$ref: "#/definitions/FileMetadata"
+ "400":
+ description: "Bad Request"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "413":
+ description: "Request Entity Too Large"
+ "415":
+ description: "Unsupported Media Type"
+ "500":
+ description: "Internal Server Error"
security:
- oauth2: []
/api/v1/spaces/{spaceGuid}/files/async:
post:
- summary: ""
+ summary: "Start an asynchronous MTA archive upload from a URL"
description: "Uploads a Multi Target Application archive or an Extension Descriptor\
\ from a remote endpoint"
operationId: "startUploadFromUrl"
@@ -120,13 +154,23 @@ paths:
schema:
$ref: "#/definitions/FileUrl"
responses:
- 202:
+ "202":
description: "Accepted"
+ "400":
+ description: "Bad Request"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "413":
+ description: "Request Entity Too Large"
+ "500":
+ description: "Internal Server Error"
security:
- oauth2: []
/api/v1/spaces/{spaceGuid}/files/jobs/{jobId}:
get:
- summary: ""
+ summary: "Get the status of an asynchronous URL upload job"
description: "Gets the status of an async upload job"
operationId: "getAsyncUploadJob"
produces:
@@ -148,20 +192,28 @@ paths:
required: true
type: "string"
responses:
- 200:
+ "200":
description: "OK"
schema:
$ref: "#/definitions/AsyncUploadResult"
- 201:
+ "201":
description: "Created"
schema:
$ref: "#/definitions/AsyncUploadResult"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "404":
+ description: "Not Found"
+ "500":
+ description: "Internal Server Error"
security:
- oauth2: []
/api/v1/spaces/{spaceGuid}/mtas:
get:
- summary: ""
- description: "Retrieves all Multi-Target Applications in a space "
+ summary: "Retrieve all deployed MTAs in a space"
+ description: "Retrieves all Multi-Target Applications in a space"
operationId: "getMtas"
produces:
- "application/json"
@@ -172,18 +224,24 @@ paths:
required: true
type: "string"
responses:
- 200:
+ "200":
description: "OK"
schema:
type: "array"
items:
$ref: "#/definitions/Mta"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "500":
+ description: "Internal Server Error"
security:
- oauth2: []
/api/v1/spaces/{spaceGuid}/mtas/{mtaId}:
get:
- summary: ""
- description: "Retrieves Multi-Target Application in a space "
+ summary: "Retrieve a specific deployed MTA by ID"
+ description: "Retrieves Multi-Target Application in a space"
operationId: "getMta"
produces:
- "application/json"
@@ -199,52 +257,70 @@ paths:
required: true
type: "string"
responses:
- 200:
+ "200":
description: "OK"
schema:
$ref: "#/definitions/Mta"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "404":
+ description: "Not Found"
+ "500":
+ description: "Internal Server Error"
security:
- oauth2: []
/api/v1/spaces/{spaceGuid}/operations:
get:
- summary: ""
- description: "Retrieves Multi-Target Application operations "
+ summary: "List MTA operations in a space"
+ description: "Retrieves Multi-Target Application operations"
operationId: "getMtaOperations"
produces:
- "application/json"
parameters:
- name: "spaceGuid"
in: "path"
+ description: "GUID of the CF space"
required: true
type: "string"
- name: "mtaId"
in: "query"
+ description: "Filter operations by MTA ID"
required: false
type: "string"
- name: "last"
in: "query"
+ description: "Return only the last N operations"
required: false
type: "integer"
format: "int32"
- name: "state"
in: "query"
+ description: "Filter operations by state, e.g. RUNNING, FINISHED, ERROR, ABORTED"
required: false
type: "array"
items:
type: "string"
collectionFormat: "multi"
responses:
- 200:
+ "200":
description: "OK"
schema:
type: "array"
items:
$ref: "#/definitions/Operation"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "500":
+ description: "Internal Server Error"
security:
- oauth2: []
post:
- summary: ""
- description: "Starts execution of a Multi-Target Application operation "
+ summary: "Start a new MTA operation (deploy, undeploy, blue-green deploy)"
+ description: "Starts execution of a Multi-Target Application operation"
operationId: "startMtaOperation"
consumes:
- "application/json"
@@ -253,147 +329,209 @@ paths:
parameters:
- name: "spaceGuid"
in: "path"
+ description: "GUID of the CF space to deploy into"
required: true
type: "string"
- in: "body"
name: "body"
+ description: "Operation descriptor specifying the process type and MTA parameters"
required: false
schema:
$ref: "#/definitions/Operation"
responses:
- 200:
- description: "successful operation"
- schema:
- $ref: "#/definitions/Operation"
- 202:
+ "202":
description: "Accepted"
+ "400":
+ description: "Bad Request"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "409":
+ description: "Conflict"
+ "500":
+ description: "Internal Server Error"
security:
- oauth2: []
/api/v1/spaces/{spaceGuid}/operations/{operationId}:
get:
- summary: ""
- description: "Retrieves Multi-Target Application operation "
+ summary: "Retrieve a specific MTA operation"
+ description: "Retrieves Multi-Target Application operation"
operationId: "getMtaOperation"
produces:
- "application/json"
parameters:
- name: "spaceGuid"
in: "path"
+ description: "GUID of the CF space containing the operation"
required: true
type: "string"
- name: "operationId"
in: "path"
+ description: "Process ID of the MTA operation"
required: true
type: "string"
- name: "embed"
in: "query"
- description: "Adds the specified property in the response body "
+ description: "Adds the specified property in the response body"
required: false
type: "string"
responses:
- 200:
+ "200":
description: "OK"
schema:
$ref: "#/definitions/Operation"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "404":
+ description: "Not Found"
+ "500":
+ description: "Internal Server Error"
security:
- oauth2: []
post:
- summary: ""
- description: "Executes a particular action over Multi-Target Application operation "
+ summary: "Execute an action on an MTA operation"
+ description: "Executes a particular action over Multi-Target Application operation"
operationId: "executeOperationAction"
parameters:
- name: "spaceGuid"
in: "path"
+ description: "GUID of the CF space containing the operation"
required: true
type: "string"
- name: "operationId"
in: "path"
+ description: "Process ID of the MTA operation"
required: true
type: "string"
- name: "actionId"
in: "query"
+ description: "Action to perform, e.g. abort or retry"
required: true
type: "string"
responses:
- 202:
+ "202":
description: "Accepted"
+ "400":
+ description: "Bad Request"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "404":
+ description: "Not Found"
+ "500":
+ description: "Internal Server Error"
security:
- oauth2: []
/api/v1/spaces/{spaceGuid}/operations/{operationId}/actions:
get:
- summary: ""
- description: "Retrieves available actions for Multi-Target Application operation "
+ summary: "List available actions for an MTA operation"
+ description: "Retrieves available actions for Multi-Target Application operation"
operationId: "getOperationActions"
produces:
- "application/json"
parameters:
- name: "spaceGuid"
in: "path"
+ description: "GUID of the CF space containing the operation"
required: true
type: "string"
- name: "operationId"
in: "path"
+ description: "Process ID of the MTA operation"
required: true
type: "string"
responses:
- 200:
+ "200":
description: "OK"
schema:
type: "array"
items:
type: "string"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "404":
+ description: "Not Found"
+ "500":
+ description: "Internal Server Error"
security:
- oauth2: []
/api/v1/spaces/{spaceGuid}/operations/{operationId}/logs:
get:
- summary: ""
- description: "Retrieves the logs Multi-Target Application operation "
+ summary: "List log files for an MTA operation"
+ description: "Retrieves the logs Multi-Target Application operation"
operationId: "getMtaOperationLogs"
produces:
- "application/json"
parameters:
- name: "spaceGuid"
in: "path"
+ description: "GUID of the CF space containing the operation"
required: true
type: "string"
- name: "operationId"
in: "path"
+ description: "Process ID of the MTA operation"
required: true
type: "string"
responses:
- 200:
+ "200":
description: "OK"
schema:
type: "array"
items:
$ref: "#/definitions/Log"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "404":
+ description: "Not Found"
+ "500":
+ description: "Internal Server Error"
security:
- oauth2: []
/api/v1/spaces/{spaceGuid}/operations/{operationId}/logs/{logId}/content:
get:
- summary: ""
- description: "Retrieves the log content for Multi-Target Application operation "
+ summary: "Retrieve the content of a specific operation log"
+ description: "Retrieves the log content for Multi-Target Application operation"
operationId: "getMtaOperationLogContent"
produces:
- "text/plain"
parameters:
- name: "spaceGuid"
in: "path"
+ description: "GUID of the CF space containing the operation"
required: true
type: "string"
- name: "operationId"
in: "path"
+ description: "Process ID of the MTA operation"
required: true
type: "string"
- name: "logId"
in: "path"
+ description: "ID of the log file to retrieve"
required: true
type: "string"
responses:
- 200:
+ "200":
description: "OK"
schema:
type: "string"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "404":
+ description: "Not Found"
+ "500":
+ description: "Internal Server Error"
security:
- oauth2: []
securityDefinitions:
diff --git a/multiapps-controller-api/src/main/resources/mtarest_v2.yaml b/multiapps-controller-api/src/main/resources/mtarest_v2.yaml
index 86cdc2210e..12f96029f0 100644
--- a/multiapps-controller-api/src/main/resources/mtarest_v2.yaml
+++ b/multiapps-controller-api/src/main/resources/mtarest_v2.yaml
@@ -13,7 +13,7 @@ schemes:
paths:
/api/v2/spaces/{spaceGuid}/mtas:
get:
- summary: ""
+ summary: "Retrieve all deployed MTAs in a space, filtered by namespace or name"
description: "Retrieves all Multi-Target Applications in a given space and namespace"
operationId: "getMtas"
produces:
@@ -35,12 +35,18 @@ paths:
required: false
type: "string"
responses:
- 200:
+ "200":
description: "OK"
schema:
type: "array"
items:
$ref: "#/definitions/Mta"
+ "401":
+ description: "Unauthorized"
+ "403":
+ description: "Forbidden"
+ "500":
+ description: "Internal Server Error"
security:
- oauth2: []
securityDefinitions: