Closed mukulgupta2507 closed 10 years ago
Which version of swagger-core do you use? Which dependency do you use specifically?
@webron : I'm using swagger core 1.3.10 http://mvnrepository.com/artifact/com.wordnik/swagger-core_2.10/1.3.10. In addition to this, I'm also using swagger-jaxrs_2.10 and swagger-annotations. http://mvnrepository.com/artifact/com.wordnik/swagger-annotations
These are all libraries that I'm using for swagger related stuff. In addition to this for interacting with db, I 'm using jongo annotations which I have shown in the above mentioned class.
Can you try putting the @ApiModelProperty
on the getter of the field rather than on the field itself? I tested it not too long ago and it worked just fine.
I tried using @ApiModelProperty(dataType= "string") on the getter of the field but still it doesn't work. BTW I'm also using @XmlElement(name = "id") on the getter field since my rest api supports both json and xml. Can this be one problem ? Or the way I'm using @ApiModelProperty is not correct ?
It shouldn't be a problem but for testing purposes you can try removing the @XmlElement and see if it works.
No, I tried it after removing @XmlElement but still doesn't work. Any other work around ?
How do you use that model? As a response or parameter? Can you share the relevant method signature? On Oct 10, 2014 4:45 PM, "Mukul Gupta" notifications@github.com wrote:
No, I tried it after removing @XmlElement but still doesn't work. Any other work around ?
— Reply to this email directly or view it on GitHub https://github.com/wordnik/swagger-core/issues/720#issuecomment-58657250 .
I'm using that model as a reponse of my rest api. which method signature should I share with you ?
The one using it as a response, including the annotations. On Oct 10, 2014 4:52 PM, "Mukul Gupta" notifications@github.com wrote:
I'm using that model as a reponse of my rest api. which method signature should I share with you ?
— Reply to this email directly or view it on GitHub https://github.com/wordnik/swagger-core/issues/720#issuecomment-58658217 .
One more thing I'm not using @ApiModel on the main POJO class. Is it necessary to first use annotation @ApiModel on POJO ?
Method signature of my rest api is:
@ApiOperation(value="Find Id",response=MyPojo.class) public javax.ws.rs.core.Response getMethod(@ApiParam(value="ID of object",required=true) @PathParam("id") final String id, @ApiParam(value="type",required=false,allowMultiple=true) @QueryParam("type") final String type, @ApiParam(value="keys",required=false,allowMultiple=true) @QueryParam("keys") final String keys)
And I assume MyPojo.class is the one containing that field?
Yeah, that's right.
There's something that doesn't quite add up here. In the original post you have a code sample like this:
public class Person implements Serializable {
@org.jongo.marshall.jackson.oid.Id
@ObjectId
private String id; // internal mongo db id
...
That field is actually a String a not Mongo's ObjectId, which indeed translates to what you posted above when using its .toString(). So, are you trying to convert that field or another one? It could be useful to see the whole actual pojo if you can share it.
I'll try to clear your doubts. I have represented my mongo id as a string in my Java POJO, now since jongo mapper which I normally use to query my mongo db automatically converts into an ObjectId, I need not to worry about it. Using @ObjectId ensures that. But that thing is not happening when I started using swagger. I know the issue lies with the type of object mapper which internally swagger uses to process objectId. I'm trying to handle this field only but without breaking it into time,new,timestamp,machine,etc. It's happening something like a Date Object, when you know that swagger actually breaks into smaller things like minutes,seconds,hours,etc. Is there anyway I can tell swagger that don't process it this way. Simply handle it as an ObjectId ?
I have mostly mosted the main parts of POJO. Othere parts of POJO contains other fields, I don't think there is anything you can find from there.
I suggest making a test case in swagger-core so we can address this.
@webron @fehguy Is there anyway by which I can use Swagger Overriding models functionality to override ObjectId models. There is a blog post which shows how to override Date models. Can we do something similar to this ?
https://github.com/wordnik/swagger-core/wiki/overriding-models
You can, I just don't think it's needed. If you can provide us with a test case, we could investigate it further as to why it doesn't natively work.
Okay, for test cases should I write a sample pojo with all the necessary annotations and a sample rest api which will be using it and post it here with the corresponding output and expected outputs ?
Also, if you can give me some hints on the overriding models parts of swagger for ObjectId, it'll be great. I tried writing models something like:
String jsonString = "{" +
" \"_id\": \"ObjectId\"," +
" \"properties\": {" +
" \"date\": \"Date in ISO-8601 format\"," +
" \"time\": \"Date in ISO-8601 format\"," +
" \"timestamp\": \"Date in ISO-8601 format\"," +
" \"new\": false," +
" \"timeSecond\": \"Date in ISO-8601 format\"," +
" \"inc\": \"Date in ISO-8601 format\"," +
" \"machine\": \"Date in ISO-8601 format\"" +
" }" +
" }" +
"}";
OverrideConverter converter = new OverrideConverter();
converter.add(ObjectId.class.getName(), jsonString);
ModelConverters.addConverter(converter, true);
But it didn't work.
Yeah, the test case should be something I could run locally without too much work (I'll give you a hint, I don't have mongodb installed ;) - though that shouldn't matter for the test case)).
As for the converter, in which part of your code did you register it?
Okay, for the test cases I'll write a simple POJO with all the necessary annotations and a simple REST api so that you can test it on your local machine.
As for the converter, I have written a bootstrap servlet file which is initialized when Resteasy scans all my resources.
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import org.bson.types.ObjectId;
import com.wordnik.swagger.config.ConfigFactory;
import com.wordnik.swagger.config.ScannerFactory;
import com.wordnik.swagger.config.SwaggerConfig;
import com.wordnik.swagger.converter.ModelConverters;
import com.wordnik.swagger.converter.OverrideConverter;
import com.wordnik.swagger.converter.TypeConverter;
import com.wordnik.swagger.jaxrs.config.ReflectiveJaxrsScanner;
import com.wordnik.swagger.jaxrs.reader.DefaultJaxrsApiReader;
import com.wordnik.swagger.reader.ClassReaders;
public class Bootstrap extends HttpServlet {
@Override
public void init(ServletConfig servletConfig) {
try {
super.init(servletConfig);
SwaggerConfig swaggerConfig = new SwaggerConfig();
ConfigFactory.setConfig(swaggerConfig);
swaggerConfig.setBasePath("http://localhost:8080/myresource");
swaggerConfig.setApiVersion("1.0.0");
ReflectiveJaxrsScanner scanner = new ReflectiveJaxrsScanner();
scanner.setResourcePackage("my_package"); //your "resources" package
ScannerFactory.setScanner(scanner);
ClassReaders.setReader(new DefaultJaxrsApiReader());
String jsonString = "{" +
" \"_id\": \"ObjectId\"," +
" \"properties\": {" +
" \"date\": \"Date in ISO-8601 format\"," +
" \"time\": \"Date in ISO-8601 format\"," +
" \"timestamp\": \"Date in ISO-8601 format\"," +
" \"new\": false," +
" \"timeSecond\": \"Date in ISO-8601 format\"," +
" \"inc\": \"Date in ISO-8601 format\"," +
" \"machine\": \"Date in ISO-8601 format\"" +
" }" +
"}";
OverrideConverter converter = new OverrideConverter();
converter.add(ObjectId.class.getName(), jsonString);
ModelConverters.addConverter(converter, true);
} catch (ServletException e) {
System.out.println(e.getMessage());
}
}
}
I have copied my entire bootstrap file here. I guess this is how we override the way swagger looks the things. But it's not working, Any ideas ?
Have you registered the Bootstrap in your web.xml?
Yeah. I have done it like this:
<servlet>
<servlet-name>Bootstrap</servlet-name>
<servlet-class>my_package.Bootstrap</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
BootStrap is working fine, that's for sure because that way only I made swagger to scan my resources using DefaultJaxrsApiReader.
It looks right, and unfortunately I can't test it at the moment. The only thing I'd suggest trying is registering the converter before initializing the scanner and reader (no idea if that would help).
No, It's not working. Infact I'm getting an exception like this:
2014-10-20 15:32:05,246: ERROR com.wordnik.swagger.converter.OverrideConverter - failed to convert json to model org.json4s.package$MappingException: Did not find value which can be converted into java.lang.String at org.json4s.reflect.package$.fail(package.scala:96) at org.json4s.Extraction$.convert(Extraction.scala:554) at org.json4s.Extraction$.extract(Extraction.scala:331) at org.json4s.Extraction$.extract(Extraction.scala:42) at org.json4s.ExtractableJsonAstNode.extract(ExtractableJsonAstNode.scala:21) at com.wordnik.swagger.model.SwaggerSerializers$JsonSchemaModelSerializer$$anonfun$$init$$1$$anonfun$apply$1$$anonfun$applyOrElse$9.apply(SwaggerSerializers.scala:51) at com.wordnik.swagger.model.SwaggerSerializers$JsonSchemaModelSerializer$$anonfun$$init$$1$$anonfun$apply$1$$anonfun$applyOrElse$9.apply(SwaggerSerializers.scala:51) at org.json4s.ExtractableJsonAstNode.extractOrElse(ExtractableJsonAstNode.scala:59) at com.wordnik.swagger.model.SwaggerSerializers$JsonSchemaModelSerializer$$anonfun$$init$$1$$anonfun$apply$1.applyOrElse(SwaggerSerializers.scala:51) at com.wordnik.swagger.model.SwaggerSerializers$JsonSchemaModelSerializer$$anonfun$$init$$1$$anonfun$apply$1.applyOrElse(SwaggerSerializers.scala:33) at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:33) at org.json4s.CustomSerializer$$anonfun$deserialize$1.applyOrElse(Formats.scala:364) at org.json4s.CustomSerializer$$anonfun$deserialize$1.applyOrElse(Formats.scala:362) at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:33) at org.json4s.CustomSerializer$$anonfun$deserialize$1.applyOrElse(Formats.scala:362) at org.json4s.CustomSerializer$$anonfun$deserialize$1.applyOrElse(Formats.scala:362) at scala.PartialFunction$OrElse.apply(PartialFunction.scala:162) at org.json4s.CustomSerializer$$anonfun$deserialize$1.applyOrElse(Formats.scala:362) at org.json4s.CustomSerializer$$anonfun$deserialize$1.applyOrElse(Formats.scala:362) at scala.PartialFunction$OrElse.apply(PartialFunction.scala:162)
I'll post the test cases today when I'm free. Meanwhile, if you get a chance, can you please see what's wrong with the model part ?
Okay, that error actually implies that the converter is used, so that's a step in the right direction.
I believe the problem is with the jsonString
you use, but now that I think about it, the converter is probably not the right way to go. The converter can be used to replace a one model representation with another model representation, but in your case, you want to replace a model with a primitive.
Can you share your whole model definition as-is?
I want to convert following model to MongoDB ObjectId without breaking the original id into sub-fields: "_id": { "date": 1337063806000, "time": 1337063806000, "timestamp": 1337063806, "new": false, "timeSecond": 1337063806, "inc": -100663295, "machine": 808430657 } Rather I want it to be coming like this:
"_id" : "503b2ab3e4b032e338f11111"
If you want something else, then please let me know.
Yeah, I'm familiar with MongoDB's ObjectIds (just haven't used it for a while now).
I'd like to see a full model class containing such an ObjectID whose conversion gives you the unwanted results, assuming you can share it. If not, then we'll have to wait for the test case (which may still be needed even if you provide the full model class).
@webron @fehguy Okay, so I'm posting a simple test case and the process to reproduce it. I have created a test collection in my MongoDB in test DB on localhost and inserted one document:
> use test
> db.test.insert({name:"somerandomname"});
> db.test.findOne()
{ "_id" : ObjectId("544507e70ffab1fab3a6c492"), "name" : "somerandomname" }
My POJO looks like this:
import java.io.Serializable;
import org.jongo.marshall.jackson.oid.ObjectId;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Field;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonProperty;
@JsonInclude(JsonInclude.Include.NON_NULL)
public class SampleModel implements Serializable{
private static final long serialVersionUID = -7917366681345859073L;
@Id
@org.jongo.marshall.jackson.oid.Id
@ObjectId
private String id;
@Field("name")
@JsonProperty("name")
private String name;
public SampleModel() {
}
//@ApiModelProperty(dataType= "string") I tried using this but it's not working
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
}
And My Rest API looks like this:
@Path("/v1/samplerestapi")
@Component
@Api(value="/v1/samplerestapi",description="operations about sample rest api")
@Produces({MediaType.APPLICATION_JSON})
public class SampleRestAPI{
@GET
@Path("/name/{name}")
@Produces({MediaType.APPLICATION_JSON})
@ApiOperation(value="find document by name",response=SampleModel.class)
@ApiResponses(value={
@ApiResponse(code=200,message="Success"),
@ApiResponse(code=500,message="Exception occurred"),
@ApiResponse(code=404,message="Not Found")
})
public javax.ws.rs.core.Response getDocumentByName(@ApiParam(value="name of document to fetch",required=true) @PathParam("name") final String name) {
try{
if(name != null){
MongoCollection jongo = new Jongo(new MongoClient("localhost").getDB("test")).getCollection("test");
SampleModel obj = jongo.findOne(new BasicDBObject().append("name",name).toString()).as(SampleModel.class);
return javax.ws.rs.core.Response.status(Status.BAD_REQUEST).entity(obj).build();
}
else{
return javax.ws.rs.core.Response.status(Status.BAD_REQUEST).entity("Not found").build();
}
}catch(Exception e){
return javax.ws.rs.core.Response.status(Status.INTERNAL_SERVER_ERROR).entity("Not found").build();
}
}
}
So the hit which you will be using is: http://localhost:8080/sampleproject/api/v1/samplerestapi/name/somerandomname
And the output will be:
{
"name": "somerandomname",
"_id": {
"date": 1413810151000,
"time": 1413810151000,
"timestamp": 1413810151,
"new": false,
"timeSecond": 1413810151,
"inc": -1280916334,
"machine": 268087802
}
}
But I expect something like this:
{ "_id" : "544507e70ffab1fab3a6c492", "name" : "somerandomname" }
I guess that will work. Rest of the things you can configure on your local environment like adding swagger dependency and RestEasy configuration in your pom.xml and web.xml.
Please revert if anything is still not clear.
But if I understand correctly, what you're describing here is operating your own API, not looking at the generated Swagger specification. Swagger does not affect your API in any way.
But in my case it's affecting the API. If the remove swagger annotations then the output will come as expected. Adding the swagger annotations is the only change I have made in my Rest API's.
Hi, I think that's not possible. The swagger annotations don't affect any of the runtime of the API, just the description of it.
Do you have any custom Jackson configuration in your application?
No, I'm just using Jongo Annotations which internally uses Jackson itself. Also, since I'm working on legacy code so Spring Annotations are also there. I guess those are not creating any problem. There is something wrong in the mapper only but not able to figure out why it's happening only when using Swagger Annotations in my code. Is there something like when you install swagger then it overrides the default Jackson mapper because that's the only possible case if swagger doesn't change anything in the API.
https://github.com/bguerout/jongo/issues/154 shows an example of someone using both Jongo and Swagger, and they haven't reported an issue such as the one you describe (though granted, they use a different version of Swagger).
To be clear, which Swagger annotations you remove for it to work properly?
We can also investigate further. Any chance you can share the output of mvn dependency:tree
?
Hello guys,
I read all your discussion and actually the bug comes from an update of the Jongo library 1.0 to 1.1.
For a solution to your problem, please find an answer here: http://stackoverflow.com/questions/26474514/using-jongo-and-jackson-2-how-to-deserialize-a-mongodb-objectid-represented-un
I encountered the same problem today with jackson 2, jersey 2 and Jongo 1.1.
Note: do not forget to put +1 on my answer on stackoverflow.
Cheers guys.
From France.
@alino91 you are right, it's the issue of Jongo not Swagger. @webron The solution suggested by @alino91 is working for me right now. I guess I can use it till we get a fix in the Jongo itself.
For those who are facing the same problem and looking for a solution, you can follow the @alino91 answer, it will work fine.
Thanks for the help guys.
Cheers
@alino91 - thank you for sharing this information!
@mukulgupta2507 - glad you have it sorted out for now. Closing the issue now, please reopen if needed.
Another solution to solve this problem would consist in using Jongo.
See the following last post for a detailed explanation: https://github.com/bguerout/jongo/issues/221
Try to use OperationFilter:
public class SwaggerOperationFilter : IOperationFilter
{
private readonly IEnumerable<string> objectIdIgnoreParameters = new[]
{
nameof(ObjectId.Timestamp),
nameof(ObjectId.Machine),
nameof(ObjectId.Pid),
nameof(ObjectId.Increment),
nameof(ObjectId.CreationTime)
};
public void Apply(Operation operation, OperationFilterContext context)
{
operation.Parameters = operation.Parameters.Where(x =>
x.In != "query" || objectIdIgnoreParameters.Contains(x.Name) == false
).ToList();
}
}
and
options.OperationFilter<SwaggerOperationFilter>();
Hi, Swagger is not able to map Mongo ObjectId correctly. My model class looks something like this:
public class Person implements Serializable { @org.jongo.marshall.jackson.oid.Id @ObjectId private String id; // internal mongo db id ...
Swagger gives me response something like this:
Instead I expect something like this: "_id": "406b5b3de4b8dde5f3000000"
I have seen already existing issues and tried following things but nothing seems to work.
import com.wordnik.swagger.converter.*;
String jsonString = "{" + " \"_id\" : \"ObjectId\" "+ "}"; OverrideConverter converter = new OverrideConverter(); converter.add(ObjectId.class.getName(), jsonString); ModelConverters.addConverter(converter, true);
But it's not working, may be I'm doing it wrong way.
I also tried using Typeconverter but didn't solve the issue:
TypeConverter typeConverter = new TypeConverter(); typeConverter.add("_id", "string"); ModelConverters.addConverter(typeConverter, true);
Anyone who has faced the same issue and able to find any work around for this, please revert.
Thanks