415Unsupported Media Type in endpoint generated by openapi 3.0 - spring

I've generated endpoint using openapi 3.0 that consumes form data. No idea what could have I done wrong, since its all generated and in past I've been able to upload file like this. The difference is that now I have multiple fields other than file.
paths:
/movie:
post:
operationId: createMovie
description: creates movie
requestBody:
content:
multipart/form-data:
schema:
$ref: '#/components/schemas/MovieRequest'
responses:
'200':
description: ''
content:
application/json:
schema:
$ref: '#/components/schemas/Movie'
Movie request component:
MovieRequest:
type: object
properties:
title:
type: string
description:
type: string
director:
type: string
length:
type: integer
format: int64
category:
$ref: '#/components/schemas/Category'
ageCategory:
$ref: '#/components/schemas/AgeCategory'
poster:
type: string
format: binary
trailerLink:
type: string
shortDescription:
type: string
Generated controller:
#ApiOperation(value = "", nickname = "createMovie", notes = "creates movie", response = MovieModelApi.class, tags={ })
#ApiResponses(value = {
#ApiResponse(code = 200, message = "", response = MovieModelApi.class) })
#RequestMapping(
method = RequestMethod.POST,
value = "/movie",
produces = { "application/json" },
consumes = { "multipart/form-data" }
)
default ResponseEntity<MovieModelApi> createMovie(#ApiParam(value = "") #Valid #RequestPart(value = "title", required = false) String title,#ApiParam(value = "") #Valid #RequestPart(value = "description", required = false) String description,#ApiParam(value = "") #Valid #RequestPart(value = "director", required = false) String director,#ApiParam(value = "", allowableValues = "HORROR") #Valid #RequestPart(value = "category", required = false) CategoryModelApi category,#ApiParam(value = "", allowableValues = "PG13") #Valid #RequestPart(value = "ageCategory", required = false) AgeCategoryModelApi ageCategory,#ApiParam(value = "") #Valid #RequestPart(value = "poster", required = false) MultipartFile poster) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
String exampleString = "{ \"director\" : \"director\", \"isEnabled\" : true, \"description\" : \"description\", \"id\" : 5, \"title\" : \"title\", \"poster\" : \"poster\" }";
ApiUtil.setExampleResponse(request, "application/json", exampleString);
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
After importing the yaml to postman I send request:
That contains headers:
But I get 415 unsupported media error

HTTP 415 Unsupported Media Type indicates that the server is refusing to accept the request because the payload format is not supported. The format problem may be related to the Content-Type or Content-Encoding specified in the request, or as a result of direct validation of the data.

Related

Jackson xml (de)serialization with custom boolean format

I have xml file:
<?xml version="1.0" encoding="UTF-8"?>
<ADDRESSOBJECTS>
<OBJECT ID="1802267" NAME="SomeName" ISACTIVE="1" />
</ADDRESSOBJECTS>
and correponding classes in kotlin:
#JacksonXmlRootElement(localName = "ADDRESSOBJECTS")
class AddressingObjectCollection {
#JacksonXmlProperty(localName = "OBJECT")
#JacksonXmlElementWrapper(useWrapping = false)
open lateinit var objects: List<AddressingObject>
}
and
class AddressingObject : Serializable {
#JacksonXmlProperty(isAttribute = true, localName = "ID")
open var id: Long = 0
#JacksonXmlProperty(isAttribute = true, localName = "NAME")
open lateinit var name: String
#JacksonXmlProperty(isAttribute = true, localName = "ISACTIVE")
open var isActive: Boolean = false
}
when I try to deserialize I get error:
val deserialized = mapper.readValue(File(file).readText(), AddressingObjectCollection::class.java)
error:
Cannot deserialize value of type `boolean` from String "1": only "true"/"True"/"TRUE" or "false"/"False"/"FALSE" recognized
How to tell jackson to (de)serialize this format properly?
For this purpose I use Json attributes:
#JsonProperty("ISACTIVE")
#JacksonXmlProperty(isAttribute = true, localName = "ISACTIVE")
#JsonDeserialize(using = CustomBooleanDeserializer::class)
open var isActive: Boolean = false
And CustomBooleanDeserializer:
class CustomBooleanDeserializer : JsonDeserializer<Boolean>() {
override fun deserialize(p: JsonParser?, ctxt: DeserializationContext?): Boolean {
if (p?.currentTokenId() == JsonTokenId.ID_STRING){
var text = p.text
if (text == "1") return true
}
return false;
}
}
It works for me.

Open API 3.0.1 One Of Examples Definition in SpringBoot RestAPI

I'm writing an OpenAPI spec for an existing API. This API returns status 400 for two different failure, but with a different response structure.I tried the following structure.
How to use oneOf example in OpenAPI 3.0.1 with Springboot Rest API?
get:
tags:
- pet
summary: Find pet by ID
description: Returns a single pet
operationId: getPetById
parameters:
- name: petId
in: path
description: ID of pet to return
required: true
schema:
type: integer
format: int64
responses:
'400':
description: Invalid ID supplied
content:
application/json:
schema:
oneOf:
- $ref: '#/components/schemas/ApiResultOk'
- $ref: '#/components/schemas/ApiResultError'
examples:
success:
summary: Example of a Test response 1
value:
errorCode: "1001"
errorMsg: "Error Message 1"
error:
summary: Example of an error response 2
value:
errorCode: "1002"
errorMsg: "Error Message 2"
components:
schemas:
ApiResultOk:
type: object
properties:
errorCode:
type: string
errorMsg:
type: string
ApiResultError:
type: object
properties:
errorCode:
type: string
errorMsg:
type: string
Edit : After generated maven plugin and removed oneOf (single schema)
#ApiOperation(value = "Find pet by ID", nickname = "getPetById", notes = "Returns a single pet", authorizations = {
#Authorization(value = "api_key")
}, tags={ "pet", })
#ApiResponses(value = {
#ApiResponse(code = 400, message = "Invalid ID supplied", response = ApiResult.class),
#ApiResponse(code = 404, message = "Pet not found") })
#RequestMapping(value = "/ticketReOpen/{ticketUUID}", method = RequestMethod.POST, produces = MediaType.APPLICATION_JSON_VALUE)
public class ApiResult {
#JsonProperty("errorCode")
private String errorCode;
#JsonProperty("errorMsg")
private String errorMsg;
public ApiResult errorCode(String errorCode) {
this.errorCode = errorCode;
return this;
}
#ApiModelProperty(value = "")
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public ApiResult errorMsg(String errorMsg) {
this.errorMsg = errorMsg;
return this;
}
#ApiModelProperty(value = "")
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
}
Swagger UI doesnt display 400 code response and examples

Bad Request with 400 status code in Spring boot

Recently I am using Spring boot framework with Kotlin. Every thing is all okey with GET method. But when registering a new user with POST method I go faced some problem having Bad Request with status code 400.
Here is my code associate with my spring boot project
User.kt
#Entity
#Table(name = "user_info")
data class User(
#Id
#SequenceGenerator(
name = "user_seq",
sequenceName = "user_seq",
allocationSize = 1
)
#GeneratedValue(
strategy = SEQUENCE,
generator = "user_seq"
)
#Column(
name = "id",
updatable = false
)
val id: Long = -1,
#Column(
name = "first_name",
nullable = false,
length = 50,
updatable = true
)
val firstName: String,
#Column(
name = "last_name",
nullable = false,
length = 50,
updatable = true
)
val lastName: String,
#Column(
name = "email",
nullable = true,
length = 150,
updatable = true
)
val email: String,
#Column(
name = "gender",
nullable = false,
length = 2,
updatable = true
)
val gender: String,
#Column(
name = "date_of_birth",
nullable = false,
updatable = true
)
val dateOfBirth: LocalDate,
#Column(
name = "country",
nullable = false,
length = 50,
updatable = true
)
val country: String
)
UserController.kt
#RestController
#RequestMapping(
path = [
"/api/v1/"
]
)
class UserController(
#Autowired private val userService: UserService
) {
#PostMapping("register")
fun registerUser(#RequestBody user: User) {
userService.registerUser(user)
}
#GetMapping("users")
fun getUsers(): List<User> {
return userService.getUsers()
}
#GetMapping("user/{id}")
fun getUsers(#PathVariable("id") id: Long): User {
return userService.getUserInfo(id)
}
}
My Request Payload is
POST http://localhost:8080/api/v1/register
Content-Type: application/json
{
"first_name" : "Abid",
"last_name" : "Affan",
"email" : "aminul15-5281#diu.edu.bd",
"gender" : "M",
"date_of_birth" : "2019-05-03",
"country" : "Bangladesh"
}
and my response payload is
POST http://localhost:8080/api/v1/register
HTTP/1.1 400
Content-Type: application/json
Transfer-Encoding: chunked
Date: Mon, 01 Mar 2021 05:52:03 GMT
Connection: close
{
"timestamp": "2021-03-01T05:52:03.634+00:00",
"status": 400,
"error": "Bad Request",
"message": ""JSON parse error: Instantiation of [simple type, class com.example.demo.user.User] value failed for JSON property firstName due to missing (therefore NULL) value for creator parameter firstName which is a non-nullable type; nested exception is com.fasterxml.jackson.module.kotlin.MissingKotlinParameterException: Instantiation of [simple type, class com.example.demo.user.User] value failed for JSON property firstName due to missing (therefore NULL) value for creator parameter firstName which is a non-nullable type\n at [Source: (PushbackInputStream); line: 8, column: 1] (through reference chain: com.example.demo.user.User[\"firstName\"])",
"path": "/api/v1/register"
}
Response code: 400; Time: 214ms; Content length: 119 bytes
Add following property in your spring boot configuration
logging.level.org.springframework.web=DEBUG you will get the exact reason for getting 400 Bad requests on console logs.
You used camelCase in your entity - User and snake_case in your request payload. It is recommended not to mix syntax for object mapping. Can you try with this request poayload:
{
"firstName" : "Abid",
"lastName" : "Affan",
"email" : "aminul15-5281#diu.edu.bd",
"gender" : "M",
"dateOfBirth" : "2019-05-03",
"country" : "Bangladesh"
}

Conditionally put Item into DynamoDB

In my Lambda function, I want to conditionally put items into my DynamoDB only if the value DOESN'T EXIST already. I saw multiple different sources where they use this ConditionExpression and i cant figure out whats wrong with that.
body = await dynamo.put({
TableName: 'polit-stream',
Item: {
urlPath: data.urlPath,
},
ConditionExpression: "attribute_not_exists(urlPath)"
}).promise();
The put will always be successful, even if my secondary index value (urlPath) already exists.
Full Code:
const AWS = require('aws-sdk');
const crypto = require("crypto");
const dynamo = new AWS.DynamoDB.DocumentClient();
/**
* Demonstrates a simple HTTP endpoint using API Gateway. You have full
* access to the request and response payload, including headers and
* status code.
*
* To scan a DynamoDB table, make a GET request with the TableName as a
* query string parameter. To put, update, or delete an item, make a POST,
* PUT, or DELETE request respectively, passing in the payload to the
* DynamoDB API as a JSON body.
*/
exports.handler = async(event, context) => {
let body;
let statusCode = '200';
const headers = {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '* '
}
const data = JSON.parse(event.body);
const generateUUID = () => crypto.randomBytes(16).toString("hex");
try {
switch (event.httpMethod) {
case 'DELETE':
body = await dynamo.delete(JSON.parse(event.body)).promise();
break;
case 'GET':
body = await dynamo.scan({
TableName: 'db',
IndexName: 'urlPath',
FilterExpression: "urlPath = :urlPath",
ExpressionAttributeValues: {
":urlPath": event.queryStringParameters.urlPath
}
},
function(data) {
}).promise();
break;
case 'POST':
body = await dynamo.put({
TableName: 'db',
Item: {
id: generateUUID(),
name: data.name,
date: data.date,
place: data.place,
goals: data.goals,
type: data.type,
org: data.org,
email: data.email,
urlPath: data.urlPath,
createdAt: new Date().toISOString()
},
ConditionExpression: "attribute_not_exists(urlPath)"
}).promise();
break;
case 'PUT':
body = await dynamo.update(JSON.parse(event.body)).promise();
break;
default:
throw new Error(`Unsupported method "${event.httpMethod}"`);
}
}
catch (err) {
statusCode = '400';
body = err.message;
}
finally {
body = JSON.stringify(body);
}
return {
statusCode,
body,
headers,
};
};
Your error is that you misunderstood what ConditionExpression can do. Your full PutItem code is:
body = await dynamo.put({
TableName: 'db',
Item: {
id: generateUUID(),
name: data.name,
date: data.date,
place: data.place,
goals: data.goals,
type: data.type,
org: data.org,
email: data.email,
urlPath: data.urlPath,
createdAt: new Date().toISOString()
},
ConditionExpression: "attribute_not_exists(urlPath)"
}
What did you expect ConditionExpression: "attribute_not_exists(urlPath)" to do?
Apparently you thought that it will check whether any item exists with this value of urlPath. But this is not, unfortunately, what this expression does. What it does is to look at one specific item - the item with the same key (I don't know what is your key, id?) and check whether this specific item has a urlPath attribute (with any value).
If urlPath was the item's key, this work like you hoped it would. If the urlPath is unique (which it seems it is, according to what you wanted to do) then it can indeed serve as the item key.
In order to use the ConditionExpression you need to provide name and value for the attributes. Try this:
await dynamo.put({
TableName: 'db',
Item: {
id: generateUUID(),
name: data.name,
date: data.date,
place: data.place,
goals: data.goals,
type: data.type,
org: data.org,
email: data.email,
urlPath: data.urlPath,
createdAt: new Date().toISOString(),
},
ConditionExpression: "attribute_not_exists(#u) or (#u=:urlPath)",
ExpressionAttributeNames: { "#u": "urlPath" },
ExpressionAttributeValues: { ":urlPath": data.urlPath },
});

Creating a JSON String from a Swift object in Xcode 7+

I have the following class that I need to convert into a JSON String using Xcode 7 and above. In the previous version of Xcode there was a JSONSerelization.toJson(AnyObject) function available, however does not appear in Xcode7 .
I need to convert the following class :
struct ContactInfo
{
var contactDeviceType: String
var contactNumber: String
}
class Tradesmen
{
var type:String = ""
var name: String = ""
var companyName: String = ""
var contacts: [ContactInfo] = []
init(type:String, name:String, companyName:String, contacts [ContactInfo])
{
self.type = type
self.name = name
self.companyName = companyName
self.contacts = contacts
}
I Have set up my test data as follows
contactType =
[
ContactInfo(contactDeviceType: "Home", contactNumber: "(604) 555-1111"),
ContactInfo(contactDeviceType: "Cell", contactNumber: "(604) 555-2222"),
ContactInfo(contactDeviceType: "Work", contactNumber: "(604) 555-3333")
]
var tradesmen = Tradesmen(type: "Plumber", name: "Jim Jones", companyName: "Jim Jones Plumbing", contacts: contactType)
Any help or direction would be appreciated.
I do not think that there is any direct way, even in previous Xcode. You will need to write your own implementation. Something like below:
protocol JSONRepresenatble {
static func jsonArray(array : [Self]) -> String
var jsonRepresentation : String {get}
}
extension JSONRepresenatble {
static func jsonArray(array : [Self]) -> String {
return "[" + array.map {$0.jsonRepresentation}.joinWithSeparator(",") + "]"
}
}
And then implement JSONRepresentable in your modals like below:
struct ContactInfo: JSONRepresenatble {
var contactDeviceType: String
var contactNumber: String
var jsonRepresentation: String {
return "{\"contactDeviceType\": \"\(contactDeviceType)\", \"contactNumber\": \"\(contactNumber)\"}"
}
}
struct Tradesmen: JSONRepresenatble {
var type:String = ""
var name: String = ""
var companyName: String = ""
var contacts: [ContactInfo] = []
var jsonRepresentation: String {
return "{\"type\": \"\(type)\", \"name\": \"\(name)\", \"companyName\": \"\(companyName)\", \"contacts\": \(ContactInfo.jsonArray(contacts))}"
}
init(type:String, name:String, companyName:String, contacts: [ContactInfo]) {
self.type = type
self.name = name
self.companyName = companyName
self.contacts = contacts
}
}

Resources