oneOf and required body

This commit is contained in:
Anthony Stirling 2025-09-21 13:32:09 +01:00
parent 4596cd9aa1
commit 32e6ec2ea9
4 changed files with 67 additions and 4 deletions

View File

@ -6,6 +6,8 @@ import org.springframework.core.annotation.AliasFor;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import io.swagger.v3.oas.annotations.parameters.RequestBody;
/** /**
* Shortcut for a POST endpoint that is executed through the Stirling "autojob" framework. * Shortcut for a POST endpoint that is executed through the Stirling "autojob" framework.
* *
@ -29,6 +31,7 @@ import org.springframework.web.bind.annotation.RequestMethod;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Documented @Documented
@RequestMapping(method = RequestMethod.POST) @RequestMapping(method = RequestMethod.POST)
@RequestBody(required = true)
public @interface AutoJobPostMapping { public @interface AutoJobPostMapping {
/** Alias for {@link RequestMapping#value} the path mapping of the endpoint. */ /** Alias for {@link RequestMapping#value} the path mapping of the endpoint. */

View File

@ -4,6 +4,8 @@ import org.springframework.web.multipart.MultipartFile;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.AssertTrue;
import lombok.Data; import lombok.Data;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor; import lombok.NoArgsConstructor;
@ -11,12 +13,18 @@ import lombok.NoArgsConstructor;
@Data @Data
@NoArgsConstructor @NoArgsConstructor
@EqualsAndHashCode @EqualsAndHashCode
@Schema(description = "PDF file input - either upload a file or provide a server-side file ID")
public class PDFFile { public class PDFFile {
@Schema(description = "The input PDF file", format = "binary") @Schema(description = "The input PDF file", format = "binary")
private MultipartFile fileInput; private MultipartFile fileInput;
@Schema( @Schema(description = "File ID for server-side files (can be used instead of fileInput)")
description = "File ID for server-side files (can be used instead of fileInput)",
example = "a1b2c3d4-5678-90ab-cdef-ghijklmnopqr")
private String fileId; private String fileId;
@AssertTrue(message = "Either fileInput or fileId must be provided")
@Schema(hidden = true)
private boolean isValid() {
return (fileInput != null && (fileId == null || fileId.trim().isEmpty()))
|| (fileId != null && !fileId.trim().isEmpty() && fileInput == null);
}
} }

View File

@ -1,5 +1,8 @@
package stirling.software.SPDF.config; package stirling.software.SPDF.config;
import java.util.List;
import org.springdoc.core.customizers.OpenApiCustomizer;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -8,7 +11,10 @@ import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Contact; import io.swagger.v3.oas.models.info.Contact;
import io.swagger.v3.oas.models.info.Info; import io.swagger.v3.oas.models.info.Info;
import io.swagger.v3.oas.models.info.License; import io.swagger.v3.oas.models.info.License;
import io.swagger.v3.oas.models.media.ComposedSchema;
import io.swagger.v3.oas.models.media.ObjectSchema;
import io.swagger.v3.oas.models.media.Schema; import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.media.StringSchema;
import io.swagger.v3.oas.models.security.SecurityRequirement; import io.swagger.v3.oas.models.security.SecurityRequirement;
import io.swagger.v3.oas.models.security.SecurityScheme; import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.oas.models.servers.Server; import io.swagger.v3.oas.models.servers.Server;
@ -98,4 +104,46 @@ public class OpenApiConfig {
.addSecurityItem(new SecurityRequirement().addList("apiKey")); .addSecurityItem(new SecurityRequirement().addList("apiKey"));
} }
} }
@Bean
OpenApiCustomizer pdfFileOneOfCustomizer() {
return openApi -> {
var components = openApi.getComponents();
var schemas = components.getSchemas();
// Define the two shapes
var upload =
new ObjectSchema()
.name("PDFFileUpload")
.description("Upload a PDF file")
.addProperty("fileInput", new StringSchema().format("binary"))
.addRequiredItem("fileInput");
var ref =
new ObjectSchema()
.name("PDFFileRef")
.description("Reference a server-side file")
.addProperty(
"fileId",
new StringSchema()
.example("a1b2c3d4-5678-90ab-cdef-ghijklmnopqr"))
.addRequiredItem("fileId");
schemas.put("PDFFileUpload", upload);
schemas.put("PDFFileRef", ref);
// Create the oneOf schema
var pdfFileOneOf =
new ComposedSchema()
.oneOf(
List.of(
new Schema<>()
.$ref("#/components/schemas/PDFFileUpload"),
new Schema<>().$ref("#/components/schemas/PDFFileRef")))
.description("Either upload a file or provide a server-side file ID");
// Replace PDFFile schema
schemas.put("PDFFile", pdfFileOneOf);
};
}
} }

View File

@ -1,6 +1,8 @@
package stirling.software.SPDF.config; package stirling.software.SPDF.config;
import org.springdoc.core.customizers.OpenApiCustomizer;
import org.springdoc.core.models.GroupedOpenApi; import org.springdoc.core.models.GroupedOpenApi;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -8,7 +10,8 @@ import org.springframework.context.annotation.Configuration;
public class SpringDocConfig { public class SpringDocConfig {
@Bean @Bean
public GroupedOpenApi pdfProcessingApi() { public GroupedOpenApi pdfProcessingApi(
@Qualifier("pdfFileOneOfCustomizer") OpenApiCustomizer pdfFileOneOfCustomizer) {
return GroupedOpenApi.builder() return GroupedOpenApi.builder()
.group("file-processing") .group("file-processing")
.displayName("File Processing") .displayName("File Processing")
@ -23,6 +26,7 @@ public class SpringDocConfig {
"/api/v1/info/**", "/api/v1/info/**",
"/api/v1/general/job/**", "/api/v1/general/job/**",
"/api/v1/general/files/**") "/api/v1/general/files/**")
.addOpenApiCustomizer(pdfFileOneOfCustomizer)
.addOpenApiCustomizer( .addOpenApiCustomizer(
openApi -> { openApi -> {
openApi.info( openApi.info(