Skip to content
Open
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
53 changes: 52 additions & 1 deletion multiapps-controller-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,27 @@
<swaggerFileName>mtarest</swaggerFileName>
<info>
<title>MTA REST API</title>
<description>This is the API of the Cloud Foundry MultiApps Controller
<description>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.
</description>
<version>1.4.0</version>
</info>
Expand Down Expand Up @@ -151,6 +171,37 @@
</execution>
</executions>
</plugin>
<!-- Quotes integer response-code keys (e.g. 200: -> "200":) in generated YAML.
Required because Kongchen swagger-maven-plugin 3.1.8 emits bare integers,
but the Swagger 2.0 spec requires string keys for response codes.
This transformation is idempotent: already-quoted keys (e.g. "200":) do not
match the bare-integer regex, so running mvn compile on a clean checkout with
pre-quoted source files leaves the working tree unmodified. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>quote-yaml-response-codes</id>
<phase>compile</phase>
<goals>
<goal>run</goal>
</goals>
<configuration>
<target>
<replaceregexp file="${project.basedir}/src/main/resources/mtarest.yaml"
Comment thread
Yavor16 marked this conversation as resolved.
match="^(\s+)([0-9]{3}):"
replace='\1"\2":'
byline="true"/>
<replaceregexp file="${project.basedir}/src/main/resources/mtarest_v2.yaml"
match="^(\s+)([0-9]{3}):"
replace='\1"\2":'
byline="true"/>
</target>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,24 @@ private Endpoints() {

}

public static class HttpResponses {

private HttpResponses() {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

is it possible to use something already existing from a library like Spring?

}

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";

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<Void> getCsrfToken() {
return delegate.getCsrfToken();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -39,23 +40,32 @@ 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<List<FileMetadata>>
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) {
return delegate.getFiles(spaceGuid, namespace);
}

@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<FileMetadata>
uploadFile(MultipartHttpServletRequest request,
@ApiParam(value = "GUID of space you wish to deploy in") @PathVariable(PathVariables.SPACE_GUID) String spaceGuid,
Expand All @@ -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<Void>
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,
Expand All @@ -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<AsyncUploadResult>
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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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<Info> getInfo() {
return delegate.getInfo();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<Mta> 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<List<Mta>>
getMtas(@ApiParam(value = "GUID of space with mtas") @PathVariable(PathVariables.SPACE_GUID) String spaceGuid) {
return delegate.getMtas(spaceGuid);
Expand Down
Loading
Loading