From 632a442052818814483bab635d9e60b8b2030e13 Mon Sep 17 00:00:00 2001 From: Jonathan Cobb Date: Tue, 1 Dec 2020 17:49:43 -0500 Subject: [PATCH] WIP. adding more openapi docs, normalizing --- .../src/main/java/bubble/ApiConstants.java | 1 + .../bubble/{server => model}/AppLinks.java | 4 +- .../main/java/bubble/model/BasicAppLinks.java | 19 ++ .../resources/EntityConfigsResource.java | 5 +- .../bubble/resources/IdentityResource.java | 3 +- .../java/bubble/resources/SearchResource.java | 5 +- .../java/bubble/resources/TagsResource.java | 3 +- .../account/AccountOwnedResource.java | 13 +- .../account/AccountPromotionsResource.java | 7 +- .../resources/account/AccountsResource.java | 42 ++--- .../resources/account/AuthResource.java | 171 ++++++++++++++++-- .../java/bubble/server/BasicAppLinks.java | 18 -- .../bubble/server/BubbleConfiguration.java | 1 + .../src/main/resources/bubble-config.yml | 1 + utils/cobbzilla-wizard | 2 +- 15 files changed, 224 insertions(+), 71 deletions(-) rename bubble-server/src/main/java/bubble/{server => model}/AppLinks.java (88%) create mode 100644 bubble-server/src/main/java/bubble/model/BasicAppLinks.java delete mode 100644 bubble-server/src/main/java/bubble/server/BasicAppLinks.java diff --git a/bubble-server/src/main/java/bubble/ApiConstants.java b/bubble-server/src/main/java/bubble/ApiConstants.java index e216077c..bbcf3475 100644 --- a/bubble-server/src/main/java/bubble/ApiConstants.java +++ b/bubble-server/src/main/java/bubble/ApiConstants.java @@ -300,6 +300,7 @@ public class ApiConstants { public static final String API_TAG_SEARCH = "search"; public static final String API_TAG_BACKUP_RESTORE = "backup and restore"; public static final String API_TAG_NODE = "node"; + public static final String API_TAG_NODE_MANAGER = "node manager"; public static String getToken(String json) { if (json == null) return null; diff --git a/bubble-server/src/main/java/bubble/server/AppLinks.java b/bubble-server/src/main/java/bubble/model/AppLinks.java similarity index 88% rename from bubble-server/src/main/java/bubble/server/AppLinks.java rename to bubble-server/src/main/java/bubble/model/AppLinks.java index 2d7b5cae..514a2359 100644 --- a/bubble-server/src/main/java/bubble/server/AppLinks.java +++ b/bubble-server/src/main/java/bubble/model/AppLinks.java @@ -2,15 +2,17 @@ * Copyright (c) 2020 Bubble, Inc. All rights reserved. * For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ */ -package bubble.server; +package bubble.model; import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; import lombok.Getter; import lombok.Setter; import java.util.HashMap; import java.util.Map; +@Schema public class AppLinks extends BasicAppLinks { @JsonIgnore @Getter @Setter private Map locale = new HashMap<>(); diff --git a/bubble-server/src/main/java/bubble/model/BasicAppLinks.java b/bubble-server/src/main/java/bubble/model/BasicAppLinks.java new file mode 100644 index 00000000..73c69de2 --- /dev/null +++ b/bubble-server/src/main/java/bubble/model/BasicAppLinks.java @@ -0,0 +1,19 @@ +/** + * Copyright (c) 2020 Bubble, Inc. All rights reserved. + * For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ + */ +package bubble.model; + +import lombok.Getter; +import lombok.Setter; +import org.cobbzilla.wizard.model.entityconfig.annotations.ECField; + +public class BasicAppLinks { + + @ECField @Getter @Setter private String ios; + @ECField @Getter @Setter private String android; + @ECField @Getter @Setter private String windows; + @ECField @Getter @Setter private String macosx; + @ECField @Getter @Setter private String linux; + +} diff --git a/bubble-server/src/main/java/bubble/resources/EntityConfigsResource.java b/bubble-server/src/main/java/bubble/resources/EntityConfigsResource.java index dcd13fb7..7bd8518f 100644 --- a/bubble-server/src/main/java/bubble/resources/EntityConfigsResource.java +++ b/bubble-server/src/main/java/bubble/resources/EntityConfigsResource.java @@ -35,6 +35,7 @@ import static bubble.ApiConstants.ENTITY_CONFIGS_ENDPOINT; import static java.lang.Boolean.TRUE; import static org.cobbzilla.util.daemon.ZillaRuntime.die; import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; +import static org.cobbzilla.util.http.HttpStatusCodes.SC_OK; import static org.cobbzilla.util.io.FileUtil.abs; import static org.cobbzilla.util.string.StringUtil.packagePath; import static org.cobbzilla.wizard.resources.ResourceUtil.*; @@ -57,7 +58,7 @@ public class EntityConfigsResource extends AbstractEntityConfigsResource { description="Set a configuration parameter to `true`", parameters={@Parameter(name="param", description="the name of the parameter to set")}, responses={ - @ApiResponse(description="the value that was set", + @ApiResponse(responseCode=SC_OK, description="the value that was set", content={@Content(mediaType=APPLICATION_JSON, examples={ @ExampleObject(name="should always return true", value="true") } @@ -79,7 +80,7 @@ public class EntityConfigsResource extends AbstractEntityConfigsResource { @Parameter(name="value", description="the value to set the parameter to") }, responses={ - @ApiResponse(description="the value that was set", + @ApiResponse(responseCode=SC_OK, description="the value that was set", content={@Content(mediaType=APPLICATION_JSON, examples={ @ExampleObject(name="if the value was the String 'foo'", value="\"foo\"") } diff --git a/bubble-server/src/main/java/bubble/resources/IdentityResource.java b/bubble-server/src/main/java/bubble/resources/IdentityResource.java index 77809196..0b1793b7 100644 --- a/bubble-server/src/main/java/bubble/resources/IdentityResource.java +++ b/bubble-server/src/main/java/bubble/resources/IdentityResource.java @@ -30,6 +30,7 @@ import java.util.Map; import static bubble.ApiConstants.ID_ENDPOINT; import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; +import static org.cobbzilla.util.http.HttpStatusCodes.SC_OK; import static org.cobbzilla.wizard.resources.ResourceUtil.*; import static org.cobbzilla.wizard.server.config.OpenApiConfiguration.API_TAG_UTILITY; import static org.cobbzilla.wizard.server.config.OpenApiConfiguration.SEC_API_KEY; @@ -53,7 +54,7 @@ public class IdentityResource { description="Searches all model objects by ID. The id parameter is typically a UUID or name", parameters={@Parameter(name="id", description="an identifier (typically UUID or name) to search for")}, responses={ - @ApiResponse(description="a JSON object where the property names are entity types, and a property's corresponding value is the object of that type found with the given ID", + @ApiResponse(responseCode=SC_OK, description="a JSON object where the property names are entity types, and a property's corresponding value is the object of that type found with the given ID", content={@Content(mediaType=APPLICATION_JSON, examples={ @ExampleObject(name="usually a UUID only matches one object", value="{\"CloudService\": {\"uuid\": \"the-ID-you-searched-for\", \"other-cloud-service-fields\": \"would-be-shown\"}}"), @ExampleObject(name="a UUID for an Account also matches the AccountPolicy", value="{\"Account\": {\"uuid\": \"the-ID-you-searched-for\", \"other-account-fields\": \"would-be-shown\"}, \"AccountPolicy\": {\"uuid\": \"the-ID-you-searched-for\", \"other-policy-fields\": \"would-be-shown\"}}"), diff --git a/bubble-server/src/main/java/bubble/resources/SearchResource.java b/bubble-server/src/main/java/bubble/resources/SearchResource.java index 54d3f0f2..68df6303 100644 --- a/bubble-server/src/main/java/bubble/resources/SearchResource.java +++ b/bubble-server/src/main/java/bubble/resources/SearchResource.java @@ -23,6 +23,7 @@ import javax.ws.rs.core.Response; import static bubble.ApiConstants.*; import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; +import static org.cobbzilla.util.http.HttpStatusCodes.SC_OK; import static org.cobbzilla.wizard.resources.ResourceUtil.ok; import static org.cobbzilla.wizard.resources.ResourceUtil.userPrincipal; import static org.cobbzilla.wizard.server.config.OpenApiConfiguration.SEC_API_KEY; @@ -50,7 +51,7 @@ public class SearchResource { @Parameter(name=Q_SORT, description="sort field. prefix with + or - to indicate ascending/descending") }, responses={ - @ApiResponse(description="a SearchResults object, or if meta was true then a SqlViewField[] array") + @ApiResponse(responseCode=SC_OK, description="a SearchResults object, or if meta was true then a SqlViewField[] array") } ) public Response search(@Context Request req, @@ -80,7 +81,7 @@ public class SearchResource { @Parameter(name=Q_SORT, description="sort field. prefix with + or - to indicate ascending/descending") }, responses={ - @ApiResponse(description="a SearchResults object, or if meta was true then a SqlViewField[] array") + @ApiResponse(responseCode=SC_OK, description="a SearchResults object, or if meta was true then a SqlViewField[] array") } ) public Response search(@Context Request req, diff --git a/bubble-server/src/main/java/bubble/resources/TagsResource.java b/bubble-server/src/main/java/bubble/resources/TagsResource.java index f37b2e70..869a9ccc 100644 --- a/bubble-server/src/main/java/bubble/resources/TagsResource.java +++ b/bubble-server/src/main/java/bubble/resources/TagsResource.java @@ -22,6 +22,7 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.Response; import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; +import static org.cobbzilla.util.http.HttpStatusCodes.SC_OK; import static org.cobbzilla.wizard.resources.ResourceUtil.*; import static org.cobbzilla.wizard.server.config.OpenApiConfiguration.SEC_API_KEY; @@ -46,7 +47,7 @@ public class TagsResource { summary="Set a tag", description="Set a tag", parameters={@Parameter(name="name", description="name of the tag")}, - responses={@ApiResponse(description="a BubbleTags object representing the current list of tags")} + responses={@ApiResponse(responseCode=SC_OK, description="a BubbleTags object representing the current list of tags")} ) public Response set(@Context ContainerRequest ctx, @PathParam("name") String name, diff --git a/bubble-server/src/main/java/bubble/resources/account/AccountOwnedResource.java b/bubble-server/src/main/java/bubble/resources/account/AccountOwnedResource.java index e5c864c7..869e16e3 100644 --- a/bubble-server/src/main/java/bubble/resources/account/AccountOwnedResource.java +++ b/bubble-server/src/main/java/bubble/resources/account/AccountOwnedResource.java @@ -29,8 +29,7 @@ import java.util.List; import static bubble.ApiConstants.API_TAG_ACCOUNT_OBJECTS; import static org.cobbzilla.util.http.HttpContentTypes.APPLICATION_JSON; -import static org.cobbzilla.util.http.HttpStatusCodes.SC_NOT_FOUND; -import static org.cobbzilla.util.http.HttpStatusCodes.SC_PRECONDITION_FAILED; +import static org.cobbzilla.util.http.HttpStatusCodes.*; import static org.cobbzilla.util.reflect.ReflectionUtil.getFirstTypeParam; import static org.cobbzilla.util.reflect.ReflectionUtil.instantiate; import static org.cobbzilla.wizard.resources.ResourceUtil.*; @@ -69,7 +68,7 @@ public class AccountOwnedResource of public system configuration settings")} + responses={@ApiResponse(responseCode=SC_OK, description="a Map of public system configuration settings")} ) public Response getPublicSystemConfigs(@Context ContainerRequest ctx) { return ok(configuration.getPublicSystemConfigs()); @@ -171,7 +171,7 @@ public class AuthResource { summary="Determine if the API has been activated", description="Determine if the API has been activated", responses={ - @ApiResponse(description="returns true if API is activated, false otherwise", + @ApiResponse(responseCode=SC_OK, description="returns true if API is activated, false otherwise", content={@Content(mediaType=APPLICATION_JSON, examples={ @ExampleObject(name="Bubble is activated", value="true"), @ExampleObject(name="Bubble has not been activated", value="false") @@ -184,7 +184,7 @@ public class AuthResource { @Operation(tags={API_TAG_ACTIVATION}, summary="Get activation default configuration", description="Get activation default configuration", - responses={@ApiResponse(description="returns an array of CloudService[] representing the default CloudServices and their settings")} + responses={@ApiResponse(responseCode=SC_OK, description="returns an array of CloudService[] representing the default CloudServices and their settings")} ) public Response getActivationConfigs(@Context ContainerRequest ctx) { final Account caller = optionalUserPrincipal(ctx); @@ -198,7 +198,7 @@ public class AuthResource { summary="Perform one-time activation", description="Perform one-time activation", responses={ - @ApiResponse(description="the Account object for the initial admin account, with a new session token"), + @ApiResponse(responseCode=SC_OK, description="the Account object for the initial admin account, with a new session token"), @ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if caller is not admin and activation has already been completed"), @ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="validation errors occurred: activation has already been completed, or there were errors processing the ActivationRequest object") } @@ -326,7 +326,7 @@ public class AuthResource { summary="Register a new Account, starts a new API session.", description="Register a new Account, starts a new API session.", responses={ - @ApiResponse(responseCode=SC_OK, description="the Account object that was registered", ref="#/components/schemas/Account"), + @ApiResponse(responseCode=SC_OK, description="the Account object that was registered, `token` property holds session token"), @ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="validation errors occurred") } ) @@ -443,7 +443,7 @@ public class AuthResource { description="Login an Account, starts a new API session.", parameters={@Parameter(name="k", description="for a new Bubble that was launched with the lock enabled, the unlock key is required for the first login")}, responses={ - @ApiResponse(responseCode=SC_OK, description="the Account object that was logged in", ref="#/components/schemas/Account"), + @ApiResponse(responseCode=SC_OK, description="the Account object that was logged in"), @ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="validation errors occurred") } ) @@ -524,7 +524,7 @@ public class AuthResource { description="Login an Account using an existing session token, starts a new API session. If an existing session exists, it is invalidated", parameters={@Parameter(name="session", description="the session token to use for logging in")}, responses={ - @ApiResponse(responseCode=SC_OK, description="the Account object that was logged in", ref="#/components/schemas/Account"), + @ApiResponse(responseCode=SC_OK, description="the Account object that was logged in"), @ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="validation errors occurred") } ) @@ -592,7 +592,7 @@ public class AuthResource { summary="Called between Bubbles to verify RSA keys", description="Called between Bubbles to verify RSA keys", responses={ - @ApiResponse(responseCode=SC_OK, description="an RsaMessage object representing the encrypted challenge response", ref="#/components/schemas/RsaMessage"), + @ApiResponse(responseCode=SC_OK, description="an RsaMessage object representing the encrypted challenge response"), @ApiResponse(responseCode=SC_NOT_FOUND, description="some error occurred, check response") } ) @@ -654,7 +654,7 @@ public class AuthResource { summary="Re-key a node. Must be admin. Creates a new NodeKey that, being newest, will be the one the node starts using", description="Re-key a node. Must be admin. Creates a new NodeKey that, being newest, will be the one the node starts using", responses={ - @ApiResponse(description="the NodeKey that was created", ref="#/components/schemas/BubbleNodeKey"), + @ApiResponse(responseCode=SC_OK, description="the NodeKey that was created"), @ApiResponse(responseCode=SC_FORBIDDEN, description="forbidden if caller is not admin and is accessing any account Account other than themselves"), @ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="validation errors occurred") } @@ -727,6 +727,19 @@ public class AuthResource { } @POST @Path(EP_APPROVE+"/{token}") + @Operation(tags={API_TAG_AUTH}, + summary="Approve a request", + description="Approve a request. The token comes from an email or SMS message sent to the user.", + parameters={@Parameter(name="token", description="the confirmation token")}, + responses={ + @ApiResponse(responseCode=SC_OK, description="HTTP status 200 indicates success", + content={@Content(mediaType=APPLICATION_JSON, examples={ + @ExampleObject(name="if no login requested, returns an empty response", value=""), + @ExampleObject(name="if login requested, returns an Account object with either a valid session token ('token' property) or additional auth factors required (check 'multifactorAuth' property)") + })}), + @ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="a validation error occurred, for example the token might be invalid") + } + ) public Response approve(@Context Request req, @Context ContainerRequest ctx, @PathParam("token") String token, @@ -764,6 +777,18 @@ public class AuthResource { } @POST @Path(EP_AUTHENTICATOR) + @Operation(tags={API_TAG_AUTH}, + summary="Approve a TOTP request", + description="Approve a TOTP request. The token comes the end user's authenticator app.", + responses={ + @ApiResponse(responseCode=SC_OK, description="HTTP status 200 indicates success", + content={@Content(mediaType=APPLICATION_JSON, examples={ + @ExampleObject(name="if no login requested, returns an empty response", value=""), + @ExampleObject(name="if login requested, returns an Account object with either a valid session token ('token' property) or additional auth factors required (check 'multifactorAuth' property)") + })}), + @ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="a validation error occurred, for example the token might be invalid") + } + ) public Response authenticator(@Context Request req, @Context ContainerRequest ctx, AuthenticatorRequest request) { @@ -816,15 +841,38 @@ public class AuthResource { } @DELETE @Path(EP_AUTHENTICATOR) - @Operation(security=@SecurityRequirement(name=SEC_API_KEY)) + @Operation(security=@SecurityRequirement(name=SEC_API_KEY), + tags={API_TAG_AUTH}, + summary="Flush authenticator tokens", + description="Flush authenticator tokens. The next operation that requires TOTP auth will require the user to re-authenticate.", + responses={ + @ApiResponse(responseCode=SC_OK, description="HTTP status 200 indicates success", + content={@Content(mediaType=APPLICATION_JSON, examples={ + @ExampleObject(name="returns an empty JSON object", value="{}") + })}), + @ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="a validation error occurred, for example the token might be invalid") + } + ) public Response flushAuthenticatorTokens(@Context Request req, - @Context ContainerRequest ctx) { + @Context ContainerRequest ctx) { final Account caller = userPrincipal(ctx); authenticatorService.flush(caller.getToken()); return ok_empty(); } @POST @Path(EP_DENY+"/{token}") + @Operation(tags={API_TAG_AUTH}, + summary="Deny a request", + description="Deny a request. The token comes from an email or SMS message sent to the user.", + parameters={@Parameter(name="token", description="the confirmation token")}, + responses={ + @ApiResponse(responseCode=SC_OK, description="HTTP status 200 indicates success", + content={@Content(mediaType=APPLICATION_JSON, examples={ + @ExampleObject(name="returns the denial AccountMessage") + })}), + @ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="a validation error occurred, for example the token might be invalid") + } + ) public Response deny(@Context Request req, @Context ContainerRequest ctx, @PathParam("token") String token) { @@ -838,6 +886,15 @@ public class AuthResource { @GET @Path(EP_CA_CERT) @Produces(CONTENT_TYPE_ANY) + @Operation(tags={API_TAG_UTILITY}, + summary="Get the CA Certificate for this Bubble", + description="Get the CA Certificate for this Bubble. Response body is the certificate itself, in a format determined by deviceType or type", + parameters={ + @Parameter(name="deviceType", description="the device type"), + @Parameter(name="type", description="the certificate type") + }, + responses={@ApiResponse(responseCode=SC_OK, description="HTTP status 200 indicates success, response body is certificate")} + ) public Response getCaCert(@Context Request req, @Context ContainerRequest ctx, @QueryParam("deviceType") BubbleDeviceType deviceType, @@ -867,6 +924,13 @@ public class AuthResource { } @GET @Path(EP_KEY) + @Operation(tags={API_TAG_UTILITY}, + summary="Get the Node Key for this Bubble", + description="Get the Node Key for this Bubble", + responses={ + @ApiResponse(responseCode=SC_OK, description="HTTP status 200 indicates success") + } + ) public Response getNodeKey(@Context Request req, @Context ContainerRequest ctx) { final BubbleNode thisNode = configuration.getThisNode(); @@ -886,7 +950,16 @@ public class AuthResource { } @GET @Path(EP_LOGOUT) - @Operation(security=@SecurityRequirement(name=SEC_API_KEY)) + @Operation(security=@SecurityRequirement(name=SEC_API_KEY), + tags={API_TAG_AUTH}, + summary="Logout", + description="Logout of the current session, or logout of all sessions everywhere if the `all` parameter is true.", + parameters={@Parameter(name="all", description="logout of all sessions everywhere")}, + responses={ + @ApiResponse(responseCode=SC_OK, description="HTTP status 200 indicates success"), + @ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="If there is no current session to log out of") + } + ) public Response logout(@Context ContainerRequest ctx, @QueryParam("all") Boolean all) { final Account account = optionalUserPrincipal(ctx); @@ -900,7 +973,16 @@ public class AuthResource { } @POST @Path(EP_LOGOUT+"/{id}") - @Operation(security=@SecurityRequirement(name=SEC_API_KEY)) + @Operation(security=@SecurityRequirement(name=SEC_API_KEY), + tags={API_TAG_AUTH}, + summary="Logout a user everywhere", + description="Logout of the current session, or logout of all sessions everywhere if the `all` parameter is true.", + parameters={@Parameter(name="id", description="UUID or email of user to logout")}, + responses={ + @ApiResponse(responseCode=SC_OK, description="HTTP status 200 indicates success"), + @ApiResponse(responseCode=SC_PRECONDITION_FAILED, description="If there is no current session to log out of") + } + ) public Response logoutUserEverywhere(@Context ContainerRequest ctx, @PathParam("id") String id) { final Account caller = optionalUserPrincipal(ctx); @@ -916,11 +998,27 @@ public class AuthResource { return ok_empty(); } - @GET @Path(EP_TIME) public Response serverTime() { return ok(now()); } + @GET @Path(EP_TIME) + @Operation(tags={API_TAG_UTILITY}, + summary="Get current system time", + description="Get current system time. Returns current time as epoch time in milliseconds", + responses={ + @ApiResponse(responseCode=SC_OK, description="Returns current time as epoch time in milliseconds", + content={@Content(mediaType=APPLICATION_JSON, examples={@ExampleObject(name="time in milliseconds", value="1606858589683")})}) + } + ) + public Response serverTime() { return ok(now()); } @Autowired private GeoService geoService; @GET @Path(EP_SUPPORT) + @Operation(tags={API_TAG_UTILITY}, + summary="Get support information", + description="Get support information for the user's current locale, if available. Use the default locale otherwise.", + responses={ + @ApiResponse(responseCode=SC_OK, description="SupportInfo object") + } + ) public Response getSupportInfo (@Context Request req, @Context ContainerRequest ctx) { final List locales = geoService.getSupportedLocales(optionalUserPrincipal(ctx), getRemoteHost(req), normalizeLangHeader(req)); @@ -928,6 +1026,14 @@ public class AuthResource { } @GET @Path(EP_SUPPORT+"/{locale}") + @Operation(tags={API_TAG_UTILITY}, + summary="Get support information", + description="Get support information for the given locale, if available. Use the default locale otherwise.", + parameters={@Parameter(name="locale", description="locale to find support for")}, + responses={ + @ApiResponse(responseCode=SC_OK, description="SupportInfo object") + } + ) public Response getSupportInfo (@Context Request req, @Context ContainerRequest ctx, @PathParam("locale") String locale) { @@ -935,6 +1041,13 @@ public class AuthResource { } @GET @Path(EP_APP_LINKS) + @Operation(tags={API_TAG_UTILITY}, + summary="Get links to native applications", + description="Get links to native applications for the current user's locale, if available. Use the default locale otherwise.", + responses={ + @ApiResponse(responseCode=SC_OK, description="AppLinks object") + } + ) public Response getAppLinks (@Context Request req, @Context ContainerRequest ctx) { final List locales = geoService.getSupportedLocales(optionalUserPrincipal(ctx), getRemoteHost(req), normalizeLangHeader(req)); @@ -942,6 +1055,14 @@ public class AuthResource { } @GET @Path(EP_APP_LINKS+"/{locale}") + @Operation(tags={API_TAG_UTILITY}, + summary="Get links to native applications", + description="Get links to native applications for the given locale, if available. Use the default locale otherwise.", + parameters={@Parameter(name="locale", description="locale to find app links for")}, + responses={ + @ApiResponse(responseCode=SC_OK, description="AppLinks object") + } + ) public Response getAppLinks (@Context Request req, @Context ContainerRequest ctx, @PathParam("locale") String locale) { @@ -950,6 +1071,15 @@ public class AuthResource { @GET @Path(EP_PATCH+"/{token}") @Produces(APPLICATION_OCTET_STREAM) + @Operation(tags={API_TAG_NODE_MANAGER}, + summary="Find a node-manager patch file", + description="Find a node-manager patch file. The file must previously have been registered, yielding a token", + parameters={@Parameter(name="token", description="token of the patch file to retrieve")}, + responses={ + @ApiResponse(responseCode=SC_OK, description="raw file data", content={@Content(mediaType=APPLICATION_OCTET_STREAM)}), + @ApiResponse(responseCode=SC_NOT_FOUND, description="token not valid") + } + ) public Response getPatchFile(@Context ContainerRequest ctx, @PathParam("token") String token) { final File patch = nodeManagerService.findPatch(token); @@ -961,6 +1091,19 @@ public class AuthResource { @GET @Path(EP_UPGRADE+"/{node}/{key}") @Produces(APPLICATION_OCTET_STREAM) + @Operation(tags={API_TAG_NODE}, + summary="Return bubble jar", + description="Return bubble jar file for upgrading other nodes to our version.", + parameters={ + @Parameter(name="node", description="UUID of the calling node"), + @Parameter(name="key", description="UUID of the calling node's BubbleNodeKey") + }, + responses={ + @ApiResponse(responseCode=SC_OK, description="raw jar file data", content={@Content(mediaType=APPLICATION_OCTET_STREAM)}), + @ApiResponse(responseCode=SC_UNAUTHORIZED, description="calling node is not authorized"), + @ApiResponse(responseCode=SC_NOT_FOUND, description="token not valid") + } + ) public Response getUpgrade(@Context Request req, @Context ContainerRequest ctx, @PathParam("node") String nodeUuid, diff --git a/bubble-server/src/main/java/bubble/server/BasicAppLinks.java b/bubble-server/src/main/java/bubble/server/BasicAppLinks.java deleted file mode 100644 index dba95cf3..00000000 --- a/bubble-server/src/main/java/bubble/server/BasicAppLinks.java +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Copyright (c) 2020 Bubble, Inc. All rights reserved. - * For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/ - */ -package bubble.server; - -import lombok.Getter; -import lombok.Setter; - -public class BasicAppLinks { - - @Getter @Setter private String ios; - @Getter @Setter private String android; - @Getter @Setter private String windows; - @Getter @Setter private String macosx; - @Getter @Setter private String linux; - -} diff --git a/bubble-server/src/main/java/bubble/server/BubbleConfiguration.java b/bubble-server/src/main/java/bubble/server/BubbleConfiguration.java index bab33bda..1578c96c 100644 --- a/bubble-server/src/main/java/bubble/server/BubbleConfiguration.java +++ b/bubble-server/src/main/java/bubble/server/BubbleConfiguration.java @@ -13,6 +13,7 @@ import bubble.dao.account.AccountDAO; import bubble.dao.bill.AccountPlanDAO; import bubble.dao.bill.BubblePlanDAO; import bubble.dao.cloud.CloudServiceDAO; +import bubble.model.AppLinks; import bubble.model.bill.AccountPlan; import bubble.model.bill.BubblePlan; import bubble.model.cloud.BubbleNetwork; diff --git a/bubble-server/src/main/resources/bubble-config.yml b/bubble-server/src/main/resources/bubble-config.yml index 1b1a837a..bf7f7feb 100644 --- a/bubble-server/src/main/resources/bubble-config.yml +++ b/bubble-server/src/main/resources/bubble-config.yml @@ -17,6 +17,7 @@ openApi: licenseUrl: https://getbubblenow.com/bubble-license/ additionalPackages: - org.cobbzilla.wizard.model.search + - org.cobbzilla.wizard.model.support defaultLocale: {{#exists BUBBLE_DEFAULT_LOCALE}}{{BUBBLE_DEFAULT_LOCALE}}{{else}}en_US{{/exists}} testMode: {{#exists BUBBLE_TEST_MODE}}{{BUBBLE_TEST_MODE}}{{else}}false{{/exists}} diff --git a/utils/cobbzilla-wizard b/utils/cobbzilla-wizard index c40f0704..09d23d28 160000 --- a/utils/cobbzilla-wizard +++ b/utils/cobbzilla-wizard @@ -1 +1 @@ -Subproject commit c40f0704e1acb17ca3d1f1a4b590fe11997aa1b6 +Subproject commit 09d23d287d18bfa2655f56cb80a3dd7c4bcc0816