I am testing Swagger annotations using a code I copied from the Swagger-core Github.
The code excerpt I am testing comes from the static class SimpleOperations from here (line 446)
In my code, it looks like this:
(...)
#Controller("/")
class IntegratorWebController {
def convoyWebService
#Operation(
operationId = "subscribe",
description = "subscribes a client to updates relevant to the requestor's account, as identified by the input token. The supplied url will be used as the delivery address for response payloads",
parameters = {
#Parameter(in = ParameterIn.PATH, name = "subscriptionId", required = true,
schema = #Schema(implementation = Convoy.class),
style = ParameterStyle.SIMPLE, example = "example",
examples = {
#ExampleObject(name = "subscriptionId_1", value = "12345",
summary = "Subscription number 12345", externalValue = "Subscription external value 1"),
#ExampleObject(name = "subscriptionId_2", value = "54321",
summary = "Subscription number 54321", externalValue = "Subscription external value 2")
})
},
responses = {
#ApiResponse(
description = "test description",
content = #Content(
mediaType = "*/*",
schema = #Schema(
type = "string",
format = "uuid",
description = "the generated UUID",
accessMode = Schema.AccessMode.READ_ONLY,
example = "Schema example"
),
examples = {
#ExampleObject(name = "Default Response", value = "SubscriptionResponse",
summary = "Subscription Response Example", externalValue = "Subscription Response value 1")
}
))
})
def saveOrUpdateActivity(){
def result = [error:[]]
def status = OK
(...)
The only difference is that I replaced ExamplesTest.SubscriptionResponse.class to a class that exists on my code.
I am using
io.swagger.core.v3, swagger-annotations version 2.1.2
Java 11
Grails 4.0.2
I am getting:
IntegratorWebController.groovy: 28: unexpected token: # # line 28, column 2.
#Operation(
^
At the IDE it looks like this:
Javadoc says that #Parameter can be used independently in Operation or at method level to add a parameter to the operation, even if not bound to any method parameter. So the example is sound.
What is wrong?
Tks!
Because I am coding in Groovy, I have to use [] instead of {} for the array of #Parameters and #ExampleObject.
The correct code looks like this:
#Post(uri="/saveOrUpdateActivity", produces = MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
#Operation(
operationId = "subscribe",
description = "subscribes a client to updates relevant to the requestor's account, as identified by the input token. The supplied url will be used as the delivery address for response payloads",
parameters = [
#Parameter(in = ParameterIn.PATH, name = "subscriptionId", required = true,
schema = #Schema(implementation = Convoy.class),
style = ParameterStyle.SIMPLE, example = "example",
examples = [
#ExampleObject(name = "subscriptionId_1", value = "12345",
summary = "Subscription number 12345", externalValue = "Subscription external value 1"),
#ExampleObject(name = "subscriptionId_2", value = "54321",
summary = "Subscription number 54321", externalValue = "Subscription external value 2")
])
],
responses = [
#ApiResponse(
description = "test description",
content = #Content(
mediaType = "*/*",
schema = #Schema(
type = "string",
format = "uuid",
description = "the generated UUID",
accessMode = Schema.AccessMode.READ_ONLY,
example = "Schema example"
),
examples = [
#ExampleObject(name = "Default Response", value = "SubscriptionResponse",
summary = "Subscription Response Example", externalValue = "Subscription Response value 1")
]
))
])
Related
I am using the openapi-generator to create a spring-boot server. I am currently still in the experimentation phase.
The goal is that the server is able to provide multiple response media-types, depending on the media type that is accepted by the requester. According to different sources, this should be possible. See here for example of how the yaml file would then look. Other similar examples can be found here on stack overflow.
Concrete example. Let's say we have a post request where if a name is posted the name is returned (just a silly example). In case the requester sends the name John Doe and does not accept application/json, the response, in plain text, should look like this:
John Doe
In case the requester accepts application/json the response should look like this:
{"name": "John Doe"}
For explaining my question/problem I created an example spring boot server. At one point it has the path /user for which the response is:
responses:
'200':
description: The username.
content:
application/json:
schema:
properties:
name:
type: string
example: John Doe
text/plain:
schema:
type: string
example: John Doe
On the other hand I created the path /getuser (name is not really fortunate but it is for this example) which returns the following response:
'200':
description: The username.
content:
text/plain:
schema:
type: string
example: John Doe
application/json:
schema:
properties:
name:
type: string
example: John Doe
My problem is the following: for the first example, where I put the application/json first in the yaml file, the API looks like this:
default ResponseEntity<UserPost200Response> userPost(
#Parameter(name = "name", description = "The name of the user.") #Valid #RequestParam(value = "name", required = false) String name,
#Parameter(name = "UserPostRequest", description = "") #Valid #RequestBody(required = false) UserPostRequest userPostRequest
) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
String exampleString = "{ \"name\" : \"John Doe\" }";
ApiUtil.setExampleResponse(request, "application/json", exampleString);
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
If, however, I would like to return a ResponseEntity<String> this gives an error since UserPost200Response is not used.
For the path /getuser, where the String response is first defined in the yaml file, my API looks like this:
default ResponseEntity<String> getuserPost(
#Parameter(name = "name", description = "The name of the user.") #Valid #RequestParam(value = "name", required = false) String name,
#Parameter(name = "UserPostRequest", description = "") #Valid #RequestBody(required = false) UserPostRequest userPostRequest
) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
String exampleString = "\"John Doe\"";
ApiUtil.setExampleResponse(request, "application/json", exampleString);
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
This API makes it possible to return a ResponseEntity<String> but not a ResponseEntity<UserPost200Response> which defines the above mentioned json-model.
One workaround that I found, would be to use the path where the string-response is declared first in the yaml file (see /getuser) in the example above and that returns a ResponseEntity<String> and override and do something like this:
default ResponseEntity<String> getuserPost(
#Parameter(name = "name", description = "The name of the user.") #Valid #RequestParam(value = "name", required = false) String name,
#Parameter(name = "UserPostRequest", description = "") #Valid #RequestBody(required = false) UserPostRequest userPostRequest
) {
getRequest().ifPresent(request -> {
for (MediaType mediaType: MediaType.parseMediaTypes(request.getHeader("Accept"))) {
if (mediaType.isCompatibleWith(MediaType.valueOf("application/json"))) {
String exampleString = "{ \"name\" : \"John Doe\" }";
ApiUtil.setExampleResponse(request, "application/json", exampleString);
break;
}
if (mediaType.isCompatibleWith(MediaType.valueOf("text/plain"))) {
String exampleString = "John Doe";
ApiUtil.setExampleResponse(request, "text/plain", exampleString);
break;
}
}
});
return new ResponseEntity<>(HttpStatus.NOT_IMPLEMENTED);
}
In this case, I don't use the Model created by the openapi-generator and treat the JSON basically as a string.
Another option is to go for a wildcard like ResponseEntity<?> but from what I understand, except proven the contrary, this seems bad practice. I haven't figured out how to declare this in the .yml file that is used by the openapi-generator.
Neither options seem to respect the contract.
I wonder if (1) I am doing something wrong here and (2) how it could be better implemented. The goal is of course to not rewrite the API's and only implement the logic in the Controllers. Any ResponseEntity in the API's should thus not be changed.
I have a code that sends data to DataBase firebase. Its disadvantage is that it overwrites the data with each new send (update), and I would like the data to be added.
I am capturing data with okhttp3. File mainativity.kt shows how it happens
How can I do that?
Also at the end of the question, for clarity, I added the result that is transferred to the base
class PSInterceptor(
private val deviceId: String = "unknown device",
private val userId: String = "unknown user",
private val sessionId: String = "unknown session",
) :
Interceptor {
#Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val request: Request = chain.request()
val t1 = System.nanoTime()
val response: Response = chain.proceed(request)
val t2 = System.nanoTime()
val requestMap = mapOf(
"timestamp" to t1,
"duration" to t2 - t1,
"protocol" to chain.connection()?.protocol()?.name.toString(),
"request" to mapOf(
"method" to request.method,
"url" to request.url,
"headers" to request.headers.toString(),
"body" to request.body.toString(),
"size" to request.body.toString().length,
),
"response" to mapOf(
"code" to response.code.toString(),
"headers" to response.headers.toString(),
"body" to response.body?.string(),
"size" to response.body.toString().length,
),
)
firebasePush(requestMap)
return response
}
fun firebasePush(requestMap: Map<String, Any?>): Boolean {
val db = Firebase.database
val myRef = db.getReference(deviceId).child(userId).child(sessionId)
myRef.setValue(requestMap)
return true
}
}}
As Doug commented, calling setValue() will always replace the data at the reference/path with the data you pass to the call. If you want to update specific children under the path, you can use updateChildren() instead.
So this will replace the values of any keys in your requestMap, but will leave any other child nodes of myRef unmodified:
myRef.updateChildren(requestMap)
So I'm trying to serialize/deserialize RSS feeds using Jackson XML in Kotlin. However, currently it's not doing either properly. When I instantiate the RSS object manually and print out the xml, it seems like it's ignoring the localName and namespace parameters in #JacksonXmlProperty. Here is my code:
#JacksonXmlRootElement(localName="rss")
class RSSFeed(
#JacksonXmlProperty(localName="channel")
var channel: Channel
) {
constructor() : this(Channel())
}
class Channel(
#JacksonXmlProperty(localName="title")
var title: String? = null,
#JacksonXmlCData
#JacksonXmlProperty(localName="description")
var description: String? = null,
#JacksonXmlProperty(localName="image")
var image: RSSImage? = null,
#JacksonXmlProperty(localName = "explicit", namespace="itunes")
var isExplicit: String? = "no",
#JacksonXmlProperty(localName = "pubDate")
var publishDate: String? = null,
#JacksonXmlProperty(localName = "type", namespace="itunes")
var episodeType: String? = null,
#JacksonXmlProperty(localName = "link", namespace="atom")
var atomLink: AtomLink? = null,
#JacksonXmlProperty(localName = "new-feed-url", namespace="itunes")
var newFeedUrl: String? = null
)
Then I just do:
println(XmlMapper().writeValueAsString(RSSFeed(Channel(title = "blah", description = "blah", image = RSSImage(url = "https://someurl.com", link = "somelink"), isExplicit = "yes", atomLink = AtomLink(href="blah.com"), newFeedUrl = "thisisthenewfeed"))))
However, when I print that out it ignores the namespace and localName properties and just uses the variable names. What am I doing wrong?
Xml output: <rss><channel><title>blah</title><description><![CDATA[blah]]></description><image><url>https://someurl.com</url><title/><link><![CDATA[somelink]]></link></image><publishDate/><episodeType/><atomLink href="blah.com"/><newFeedUrl>thisisthenewfeed</newFeedUrl></channel></rss>
edit: it also doesn't seem to be printing the isExplicit property for some reason, not sure what that's about.
Well I figured it out! It turns out this was a Kotlin issue. I just had to do this: val xmlMapper = XmlMapper().registerModule(KotlinModule())
...and make sure that this is in your dependencies (I use gradle):
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
Anyways I hope this helps someone in the future.
I have to debug a REST API Java project that has been developed using Swagger.I'm new to it, so I'm a little bit confused on how to do certain things. For example, here's one method:
#GET
#Path("/location/name")
#Produces({MediaType.APPLICATION_JSON})
#Operation(
summary = "Get location information",
tags = {"Information"},
responses = {
#ApiResponse(responseCode = "200", content = #Content(schema = #Schema(implementation = LocationResponse.class)), description = "Get location information"),
#ApiResponse(responseCode = "500", description = "Error: Internal Server Error")
}
)
public Response searchLocationByName(
#Parameter(description = "Location name", required = true) #DefaultValue("Barcelona") #QueryParam("name") String locationName
) { /* METHOD CODE */ }
The #ApiResponse for the code 200 is not of type LocationResponse but of type ArrayList<LocationResponse>, as it can return more than one location. What would be the correct syntax for this change? I've been reading the documentation at https://github.com/swagger-api/swagger-core/wiki/Swagger-2.X---Annotations#operation-annotations but I couldn't find an appropriate example...
Thanks!
Use #ArraySchema instead of plain #Schema to define input or output data for array types.
For the PageDto<T> we can simply create ResponseDto which extends PageDto<T> and use it in swagger as : #ApiResponse(responseCode = "200", content = #Content(array = #ArraySchema(schema = #Schema(implementation = ResponseDto.class))), description = "Get location information"),. Thank You
I'm writing an Android app which retrieves xml data from the server. For manipulating such data I use Retrofit-SimpleXmlConverter(http://simple.sourceforge.net/home.php, https://github.com/square/retrofit/tree/master/retrofit-converters/simplexml) pipeline. I need to get this list of transactions:
<?xml version="1.0"?>
<Transactions>
<Transaction>
<Transaction_Time>12-01-2018</Transaction_Time>
<Transaction_Bonuses>123.11</Transaction_Bonuses>
<Summ>123.11</Summ>
<Transaction_Type>12</Transaction_Type>
<Dop_Info>BLOB Base64 data with cyrillic symbols</Dop_Info>
</Transaction>
<Transaction>
<Transaction_Time>12-01-2018</Transaction_Time>
<Transaction_Bonuses>123.11</Transaction_Bonuses>
<Summ>123.11</Summ>
<Transaction_Type>12</Transaction_Type>
<Dop_Info>no/Dop_Info>
</Transaction>
My problem is that xml has tag with BASE-64 encoded xml. From encoded xml I need follow data:
<CHECK>
<LINE name="Name of dish", quantity="1", summ="123,5"></LINE>
<LINE name="Name of dish", quantity="1", summ="123,5"></LINE>
<LINE name="Name of dish", quantity="1", summ="123,5"></LINE>
</CHECK>
Data classes:
#Root(name = "Transactions")
data class Transactions #JvmOverloads constructor(
#field:ElementList(inline = true, required = false)
#param:ElementList(inline = true, required = false)
val list: List<Transaction>? = null
) {
#Root(name = "Transaction")
data class Transaction #JvmOverloads constructor(
#field:Element(name = "Transaction_Time")
#param:Element(name = "Transaction_Time")
var transactionTime: String? = null,
#field:Element(name = "Transaction_Bonuses")
#param:Element(name = "Transaction_Bonuses")
val transactionBonuses: Double? = 0.0,
#field:Element(name = "Summ")
#param:Element(name = "Summ")
val summ: Double? = 0.0,
#field:Element(name = "Transaction_Type")
#param:Element(name = "Transaction_Type")
val transactionType: Int? = 0,
#field:Element(name = "Dop_Info")
#param:Element(name = "Dop_Info")
val dopInfo: ByteArray ? = null
){
#Root(name = "CHECK")
data class Check #JvmOverloads constructor(
#field:ElementList(inline = true, required = false)
#param:ElementList(inline = true, required = false)
#field:Path("CHECKDATA/CHECKLINES")
#param:Path("Holders_Cards/Holder_Card/Card")
val list: List<Line>? = null
){
#Root(name = "LINE")
data class Line #JvmOverloads constructor(
#field: Attribute(name = "name", required = false)
#param: Attribute(name = "name", required = false)
val name: String? = null,
#field: Attribute(name = "quantity", required=false)
#param: Attribute(name = "quantity", required=false)
val quantity: Int? = null,
#field: Attribute(name = "summ", required = false)
#param: Attribute(name = "summ", required = false)
val summ: Double? = null
)
}
}
}
At this point I'm confused - how can I correctly serialize the xml in the way I could get instead of blob-data Check model? What are next steps?
P.S For solution I tried to use guide from http://simple.sourceforge.net/download/stream/doc/tutorial/tutorial.php#callback but it didn't find anything helpful