Server stub in spring from swagger yaml - spring

I am using spring server stub from swagger yaml to generate POJO classes. In my definition, I am having allOf keyword for image property of the Images model. But, the stub generated does not have the correct class reference. It is generating with Object class instead of the type Image class. What is the mistake I am doing here?
This is my swagger yaml.
Images:
title: images
type: object
description: A collection of still images related to the work
properties:
images:
type: object
#items:
allOf:
- $ref: '#/components/schemas/Image'
videos:
type: array
items:
$ref: '#/components/schemas/Video'
This is the generated POJO class of Images
public class Images {
#JsonProperty("images")
private Object images = null;
#JsonProperty("videos")
#Valid
private List<Video> videos = null;
public Images images(Object images) {
this.images = images;
return this;
}
}
Why is the type Object and not of Image?

Related

Swagger UI OpenAPI 3, query parameters displayed as an object instead of value-changeable fields

Previously in SpringBoot v2.5.7, I had this Spring REST controller method. It had a TestCriteria type DTO, as the path param.
#GetMapping(path = "/test")
public void test(TestCriteria testCriteria) {
}
And the TestCriteria class was like this.
(Language is an enum that can take either EN or FR).
public class TestCriteria {
#ApiModelProperty(allowEmptyValue = true)
List<Language> langauges;
}
I used Springfox Swagger (springfox-boot-starter v3) and the Swagger UI looked like this:
But later I had to upgrade SpringBoot to v3, and use Springdoc and OpenAPI v3 for Swagger. Now the TestCriteria class looks like this:
public class TestCriteria {
#Schema(type="array")
#Parameter(allowEmptyValue = true)
List<Langauge> languages;
}
Now Swagger UI does not display languages as a value-selectable field, but as an object.
I compared the generated OpenAPI definition as well and found,
previous API docs -
parameters:
- name: languages
in: query
required: false
type: array
items:
type: string
enum:
- EN
- FR
collectionFormat: multi
enum:
- EN
- FR
new API doc -
parameters:
- name: testCriteria
in: query
required: true
schema:
$ref: '#/components/schemas/TestCriteria'
Is there a way I can provide the previous Swagger UI view, where the user can select a value from the enum list, rather than getting user's input via an object representation?
Finally got the Swagger UI displayed as expected.
In the controller method argument, we need to add the #ParameterObject annotation:
#GetMapping(path = "/test")
public void test(#ParameterObject TestCriteria testCriteria) {
}
This is explained in the Springfox to Springdoc migration doc:
If you’re using an object to capture multiple request query params, annotate that method argument with #ParameterObject.

How can I receive multiple files with respective metadata with my spring controller in one request?

I am developing a java spring webApp with a typescript frontend and want to send several files with some additional informations which were added by the user in the frontend to the backend (with one request). So far I managed to implement the functionality such that I can send a single file and additional information in one request which then gets mapped into a respective object in the backend.
In the code below I would like to implement functionality such that all elements of the attachments array are sent in one go. Preferably the controller in the backend should directly map them to an array of the respective QuoteAttachment object.
My frontend DTO:
export type QuoteAttachment = {
file: File,
uploadedOn: Date,
};
My frontend post request:
async createAttachment(attachments: QuoteAttachment[], quoteReference: string) {
let formData: FormData = new FormData();
formData.append('file', attachments[0].file);
formData.append('uploadedOn', attachments[0].uploadedOn.toLocaleString());
formData.append('relatedQuote', quoteReference);
await this.http.performRequest('/attachment', {
method: HttpRequestMethod.Post,
data: formData,
headers: {
'Content-Type': 'multipart/form-data'
}
});
}
My backend DTO:
#Getter
#Setter
public class QuoteAttachment {
private MultipartFile file;
private String uploadedOn;
private String relatedQuote;
}
My backend controller:
#Controller
#RequiredArgsConstructor
#RequestMapping("/attachment")
public class AttachmentController {
private final AttachmentService attachmentService;
private final AttachmentValidationService attachmentValidationService;
#ResponseBody
#PostMapping
public void uploadFileWithInfos(#ModelAttribute final QuoteAttachment quoteAttachment) {
System.out.println("Test");
}
}
I really played around a lot and tried many different variations but didn't arrive at a good solution. I hope someone has a good idea for an implementation solving the problem.
I think there is a lot of solutions you can do for this:-
solution:
you can send the file as file in angular and as a Multipart in Spring boot, beside the QuoteAttachment DTO but as a String of JSON (NOT normal JSON). Example answer mentioned here in this URL then in java after receiving a String not a QuoteAttachment model you can map that string to a model with this simple code
ObjectMapper objectMapper = new ObjectMapper();
QuoteAttachment quoteAttachment = objectMapper.readValue(quoteAttachmentAsString, QuoteAttachment.class);
Note: ObjecMapper imported from com.fasterxml.jackson.databind library.
solution:
Also you can add all your QuoteAttachment DTO properties to formData in angular and receive them as #RequestParam in java. Example answer here
My preferred solution is number 1.

How to use ref of schema as oneOf/anyOf responses of an endpoint using springdoc-openapi?

I'm using springdoc-openapi and I defined a few schemas using one of my classes (so they don't have individual classes). I've defined them using addSchemas in OpenAPI bean as mentioned in this issue:
https://github.com/springdoc/springdoc-openapi/issues/685#issuecomment-636028451
It's something like this:
#Bean
public OpenAPI customOpenAPI() {
return new OpenAPI().components(new Components()
.addSchemas("ErrorOne", getSchemaWithDifferentDescription(ErrorObject.class, "ErrorOne")
.addSchemas("ErrorTwo", getSchemaWithDifferentDescription(ErrorObject.class, "ErrorTwo")));
}
private Schema getSchemaWithDifferentDescription(Class className, String description){
ResolvedSchema resolvedSchema = ModelConverters.getInstance()
.resolveAsResolvedSchema(
new AnnotatedType(className).resolveAsRef(false));
return resolvedSchema.schema.description(description);
}
Now I want to use these schemas in oneOf (or anyOf) using ref of them as possible responses of an endpoint. Something like this:
#PostMapping
#ApiResponses({
#ApiResponse(responseCode = "400",
content = { #Content(mediaType = "application/json",
schema = #Schema(oneOf = {"ErrorOne", "ErrorTwo"})) }
)
})
public HttpResponse myEndpoint(#RequestBody HttpRequest httpRequest) {
...
}
But the problem is that oneOf field in #Schema annotation only accepts a list of classes, and I can not use the ref of those schemas here.
Is there a way to link these schemas to oneOf/anyOf using their references in spring-doc?
And also I wanted to know is there a way to add some global responses as oneOf/anyOf responses for my endpoints (beside their own possible responses)?

Using a custom ID in a Spring Data REST repository backed by MongoDB

I'm working on a new project using Spring and MongoDB. I'm a novice with both of these so bear with me, I couldn't find a definitive answer to this question.
I'm using spring-boot-starter-data-rest and have a repository like this:
#RepositoryRestResource(collectionResourceRel = "widget", path = "widget")
interface WidgetRepository : MongoRepository<Widget, ObjectId> {
fun findByType(#Param("type") type: String): List<Widget>
}
For an entity like this:
data class Widget #JsonCreator constructor(#JsonProperty val type: String) {
#Id
lateinit var id: ObjectId
}
This automatically gives you a CRUD API using the Mongo document ID:
GET /widget/{mongo doc id}
GET /widget/search/findByType?type=
POST /widget
PUT /widget
PATCH /widget
But I don't want to use the Mongo document ID. I want to introduce a secondary identifier and use that everywhere in the API. This is because the "widgets" are items in the physical world that are barcoded, and we don't want to print the Mongo document ID in the barcode.
Obviously we can implement this using Spring REST API tools, eg
#GetMapping("/widget/{barcode}/")
fun getByBarcode(#PathVariable barcode: String): Widget {
return widgetRepository.findByBarcode(barcode)
}
etc.. but is there any clever way to get #RepositoryRestResource to build its automagic API for us with a custom ID? Maybe by implementing CrudRepository<Widget, Barcode> in such a way that we have to wrap a MongoRepository<Widget, ObjectId> ? I'm not familiar enough with how Spring works under the hood to know if anything like this is even possible.
Thanks in advance
I think you are looking for an EntityLookup:
SPI to customize which property of an entity is used as unique identifier and how the entity instance is looked up from the backend.
First - sorry if I make any mistake, I do not use to program in Kotlin - you need to include the barcode property in your entity:
data class Widget #JsonCreator constructor(#JsonProperty val type: String, #JsonProperty val barcode: String) {
#Id
lateinit var id: ObjectId
}
Then, modify your repository and define a new method that will provide a Widget given its barcode:
#RepositoryRestResource(collectionResourceRel = "widget", path = "widget")
interface WidgetRepository : MongoRepository<Widget, ObjectId> {
fun findByBarcode(#Param("barcode") barcode: String): Optional<Widget>
fun findByType(#Param("type") type: String): List<Widget>
}
Finally, configure a RestRepositoryConfigurer and register the EntityLookup through an EntityLookupRegistrar:
#Component
class RestRepositoryConfigurator : RepositoryRestConfigurer {
override fun configureRepositoryRestConfiguration(config: RepositoryRestConfiguration) {
config.withEntityLookup()
.forRepository(WidgetRepository::class.java)
.withIdMapping(Widget::barcode)
.withLookup(WidgetRepository::findByBarcode)
}
}
Please, review the section 15.1 of the Spring Data Rest documentation if you need more information.

Bind json value to two object in spring MVC restful

At present what I have is one view in HTML for entering Person's details and Company's details. I am using spring MVC framework restful.
I create json and send request using Ajax to Restcontroller.based on URL pattern create method is called .e.g. json is
{"name":"rohit","address":"Pune","company":"ABC"}
Here above name and address belong to person bean and company belongs to company bean. I want the json value bind to their respective bean. How to do it? I have tried the code below but I know it won't work.
#Requestmapping(value="/createperson",method=method.post)
public #Responsebody String createperson(#Requestbody person,#Requestbody company)
{
//Some code to save
}
I have a form, which will input the person's details and the person's company details.
What I want is that when this form is submitted, some of its fields are bound to Person object properties and some to Company object properties. How can this be done? And how to do validation for json value and send all errors as json responsive again back if there are any errors.
You can only have one #RequestBody. Spring then looks at the content-type header and finds an appropriate HttpMessageConverter which will read the entire http entity body (input stream) into a single object.
What you have basically done is try to merge Person and company into a single JSON object, and thereby flattened the structure. If you want spring to handle that, you need to create a new object with the same (flat) hierarchy. Or you need to create a wrapper class PersonAndCompany which contains both a Person and a Company, and then change the JSON to match the structure, so it looks like this.
{
"person" : {
"name":"rohit",
"address":"Pune"
},
"company" : {
"name":"ABC"
}
}
you should do like this if you are using relationship between Person and Company otherwise it is better to use single bean instead of two.
#ResponseBody
#RequestMapping(value = "/createperson", method=RequestMethod.POST ,consumes=MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<Person> createperson(#RequestBody Person person) {
if(error found ){
Person p new Person();
p.setError(" error message ");
return new ResponseEntity<Person>(p,HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<Person>(person,HttpStatus.OK);
}
public class Person {
private String name;
private String address;
Company company;
String error;
--- setters getters
}
public class Company {
String compName;
--- setters getters
}
input json
{"name":"person name ","address":"person address ","company":{"compName":"company name"}}

Resources