Grails 2.4.5 Swaggydoc plugin: How to prevent the Grails Swaggydoc plugin from overriding my url names or force to use default method names? - rest

I have a grails 2.4.5 application with the grails plugin swaggydoc.
I'm able to get the UI with the documentation, however, the only way I'm able to have the POST, PUT and DELETE requests documentation automatically generated properly with the right url and Http method, is by overriding the RestFulController methods (save, update, and delete).
If I don't do that, the method is considered a get request, even despite having the proper annotation.
However, by doing that, the url names I put in UrlMappings.groovy gets overridden (except the GET requests) in the documentation and follows the default grails url naming behavior of controller/methodName, hence providing wrong urls for the documentation, so the documentation almost becomes useless, as not totally accurate.
pom.xml
...
<!-- API Documentation -->
<!-- Swagger-->
<dependency>
<groupId>com.github.rahulsom</groupId>
<artifactId>swaggydoc-commons</artifactId>
<version>0.28.0</version>
</dependency>
<dependency>
<groupId>org.grails.plugins</groupId>
<artifactId>swaggydoc</artifactId>
<version>0.9</version>
<type>zip</type>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2</artifactId>
<version>2.1.2</version>
</dependency>
<dependency>
<groupId>io.swagger.core.v3</groupId>
<artifactId>swagger-jaxrs2-servlet-initializer-v2</artifactId>
<version>2.1.2</version>
</dependency>
...
UrlMappings.groovy
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?" {
constraints {
// apply constraints here
}
}
//api documentation
"/api/v1/docs"(controller: 'api')
//Events resource
group "/api/v1/events", {
"/$eventId?"(controller: 'event', action: 'getEvents', method: 'GET', parseRequest: true)
"/$eventId?/$recovery?"(controller: 'event', action: 'update', method: 'PUT', parseRequest: true)
"/$eventId?/$recovery?"(controller: 'event', action: 'save', method: 'POST', parseRequest: true)
}
}
Sample controller
#Secured(['IS_AUTHENTICATED_FULLY'])
#Api(
value = 'Events',
description = "Event APIs",
position = 1,
produces = 'application/json',
basePath = 'api/v1',
protocols = 'http, https'
)
class EventController extends RestfulController {
static responseFormats = ['json']
static scope = "singleton"
#Override
#ApiOperation(
value = "Update event",
httpMethod = 'PUT',
authorizations = #Authorization(value = 'oauth2')
)
#ApiResponses([
#ApiResponse(code = 204, message = 'No Content')
])
#ApiImplicitParams([
#ApiImplicitParam(name = 'Id', value = 'event id', paramType = 'path', dataType ='long')
])
def update() {
...
}
}
UrlMappings.groovy
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?" {
constraints {
// apply constraints here
}
}
//api documentation
"/api/v1/docs"(controller: 'api')
//Events resource
group "/api/v1/events", {
"/$eventId?"(controller: 'event', action: 'getEvents', method: 'GET', parseRequest: true)
"/$eventId?/$recovery?"(controller: 'event', action: 'update', method: 'PUT', parseRequest: true)
"/$eventId?/$recovery?"(controller: 'event', action: 'save', method: 'POST', parseRequest: true)
}
}
I was expecting all the apis, as specified by the UrlMappings.groovy to not include any of the methods, however, Swaggydoc isn't even following my custom mapping.
I was expecting at least:
PUT /events/{eventId}
Is there a way to override this behavior, so I don't have to override the RestFulController Http methods (save, update and delete) and choose whatever method name I want and have the documentation urls being accurate?
At this point, I don't mind trying some other reliable plugin too, if that's not possible with the SwaggyDoc plugin.
The ideal scenario is to have the apis documentation automatically generated and have a ui that users can interact with and even pass a token to test the apis and get a response back.
Any feedback on how to do that with Grails 2.4.5 will be greatly appreciated.

Related

In Extjs5, erase() can't relate to destory api when the proxy type is rest

This is my viewController:
onRestDeleteClick: function(){
var ouType = Ext.create('MyApp.model.OuType',
{
id: 49,
ouTypeName: 'Lenlee',
entityName: 'Lenlee'
});
ouType.erase();
}
The model whose id is 49 exists in database.
This is the OuType model:
Ext.define('MyApp.model.OuType', {
extend: 'Ext.data.Model',
requires: [
'Ext.data.field.Field'
],
fields: [
{
name:'id'
},
{
name: 'ouTypeName'
},
{
name: 'entityName'
}
],
proxy:{
type: 'rest',
api: {
read: 'role/read',
update: 'role/update',
create: 'role/create',
destory: 'role/destory'
}
}
});
This is my server class:
#RequestMapping("/role")
#Controller("sysRoleContro")
public class SysRoleController {
…………
…………
#RequestMapping(value="/destory/{id}")
public void destoryOuType(HttpServletRequest request,
HttpServletResponse response, #PathVariable("id") Long id){
log.info("destory");
ouTypeRepository.delete(id);
log.info("end");
}
………
……
}
Now when i click the delete button, the request url is: http://localhost:7080/MyApp.model.OuType/49 404 Not Found.
The expected url is http://localhost:7080/CSQMS/role/destory/49
How can i delete the model?
Rest proxy is special in that that it does not CRUD operations to urls, as you set in api, but to HTTP verbs: GET, POST, PUT, DELETE, etc. So api config is most likely ignored. Configure the proxy this way:
proxy:{
type:'rest',
url:'/CSQMS/role'
}
If your server expects that CRUD operation verb is part of the URL you probably need a different proxy or you need to implement buildUrl method.
I want to say i'm so careless, i should write 'destroy' instead of 'destory'. MY GOD.

Ext.Direct File Upload - Form submit of type application/json

I am trying to upload a file through a form submit using Ext.Direct, however Ext.direct is sending my request as type 'application/json' instead of 'multipart/form-data'
Here is my form.
{
xtype: 'form',
api: {
submit: 'App.api.RemoteModel.Site_Supplicant_readCSV'
},
items: [
{
xtype: 'filefield',
buttonOnly: false,
allowBlank: true,
buttonText: 'Import CSV'
}
],
buttons:
[
{
text: 'Upload',
handler: function(){
var form = this.up('form').getForm();
if(form.isValid()){
form.submit({
waitMsg: 'Uploading...',
success: function(form, action){
console.log(action.result);
}
});
}
}
}
]
},
On the HTTP request, it checks to see if the request options is a form upload.
if (me.isFormUpload(options)) {
which arrives here
isFormUpload: function(options) {
var form = this.getForm(options);
if (form) {
return (options.isUpload || (/multipart\/form-data/i).test(form.getAttribute('enctype')));
}
return false;
},
getForm: function(options) {
var form = options.form || null;
if (form) {
form = Ext.getDom(form);
}
return form;
},
However, options looks like this
{
callback: function (options, success, response) {
jsonData: Object
action: "RemoteModel"
data: Array[1]
0: form
length: 1
__proto__: Array[0]
method: "Site_Supplicant_readCSV"
tid: 36
type: "rpc"
__proto__: Object
scope: constructor
timeout: undefined
transaction: constructor
}
And there is no direct form config, but it exists in jsonData.data[0]. So it doesn't set it as type multipart/form-data and it gets sent off as type application/json.
What am I doing wrong? Why isn't the form getting submitted properly?
Edit - I am seeing a lot of discussion about a 'formHandler' config for Ext.Direct? I am being led to assume this config could solve my issue. However I don't know where this should exist. I'll update my post if I can find the solution.
Solution - Simply adding /formHandler/ to the end of the params set the flag and solved my issue. Baffled.
Supplicant.prototype.readCSV = function(params,callback, request, response, sessionID/*formHandler*/)
{
var files = request.files;
console.log(files);
};
The method that handles file upload requests should be marked as formHandler in the
Ext.Direct API provided by the server side.
EDIT: You are using App.api.RemoteModel.Site_Supplicant_readCSV method to upload files; this method needs to be a formHandler.
I'm not very familiar with Node.js stack but looking at this example suggests that you may need to add /*formHandler*/ descriptor to the function's declaration on the server side.

Getting 404 Not Found - while accessing valid REST service with Jersey + ExtJS

I am working on ExtJS and need to access the REST services. I tried doing that with Spring's MVC REST supporting functionality and it worked well.
Now its kind of mandatory that I have to go with Jersey(JAX-RS). When tried with Jersey, I keep getting 404 error. I think the annotations, URL mapping, etc are fine (since the similar ones worked for Spring)
Below are the relevant code snippets.
Web.xml
<servlet>
<servlet-name>spring-jersey</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.packages</param-name>
<param-value>com.myProducts.controller</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-jersey</servlet-name>
<url-pattern>/jersey/*</url-pattern>
</servlet-mapping>
App.js
Ext.onReady(function() {
var store = Ext.create('Ext.data.Store', {
autoLoad: true,
autoSync: true,
model: 'Product',
proxy: {
type: 'rest',
url: 'products',
format: 'json',
headers: {
'Content-Type': 'application/json'
},
reader: {
type: 'json',
root: 'data'
},
writer: {
type: 'json'
},
api: {
create: 'products/createJ/',
read: 'products/readJ/',
update: 'products/editJ/',
destroy: 'products/deleteJ/'
}
}
});
Controller :
#Component
#Path("products/jersey/products")
public class JerseyProductsController {
#Autowired
private ProductService productService;
#POST
#Path("createJ/{id}")
#Consumes(MediaType.APPLICATION_JSON_VALUE)
#Produces(MediaType.APPLICATION_JSON_VALUE)
public Product createJ(#PathParam("id") int id, #RequestBody Product myProduct) {
myProduct.setId(id);
return productService.create(myProduct);
}
#PUT
#Path("editJ/{id}")
#Consumes(MediaType.APPLICATION_JSON_VALUE)
#Produces(MediaType.APPLICATION_JSON_VALUE)
public Product editJ(#PathParam("id") int id, #RequestBody Product myProduct) {
myProduct.setId(id);
return productService.update(myProduct);
}
#DELETE
#Path("deleteJ/{id}")
#Consumes(MediaType.APPLICATION_JSON_VALUE)
#Produces(MediaType.APPLICATION_JSON_VALUE)
public Product deleteJ(#PathParam("id") int id) {
return productService.delete(id);
}
#GET
#Path("readJ/")
#Consumes(MediaType.APPLICATION_JSON_VALUE)
#Produces(MediaType.APPLICATION_JSON_VALUE)
public List<Product> allProductsJ() {
return productService.getAll();
}
}
I get 404 for below URL:
Request URL:http://localhost:4080/MyProducts/products/jersey/products/readJ/.json?_dc=1407930853131&page=1&start=0&limit=25
Request Method:GET
Status Code:404 Not Found
Kindly let me know what is being missed.
You may need to use /jersey/products/jersey in your URL since you are having <url-pattern>/jersey/*</url-pattern>
Not sure what's the issue but you could try below one :
<url-pattern>*.do</url-pattern>
I believe its because it could capture requests ending with .do and it reduced the headache of having some pattern in between the URL.
I think your servlet mapping is incorrect. Your Url-Pattern is /jersey/* but in your url is /MyProducts/*
put this in your web.xml
<servlet-mapping>
<servlet-name>spring-jersey</servlet-name>
<url-pattern>/MyProducts/*</url-pattern>

Custom proxies on Stores and Models seems inconsistent (and does not work on Models)

Am using Extjs 4, and have created a custom Rest Proxy to handle communication with my Zend backend api.
(See post http://techfrere.blogspot.com/2011/08/linking-extjs4-to-zend-using-rest.html)
When using a Store to handle communication, I was using Ext.require to load the proxy, and then referenced the proxy on the type field and all was good and it loaded my data: as per:
Ext.require('App.utils.ZendRest');
...
proxy : {
type : 'zest', // My custom proxy alias
url : '/admin/user'
...
}
I then decided to try to use the proxy directly on a model... and no luck. The above logic does not work.
Problems
1. When referencing zest, it does not find the previously loaded ZendRest class (aliased to proxy.zest)
2. It tries to load the missing class from App.proxy.zest (which did not exist.)
So I tried moving my class to this location and renaming to what it seemed to want. No luck.
It loads the class, but still does not initialize the app... I get no errors anywhere so v difficult to figure out where the problem is after this...
For now it seems I will have to revert to using my Zend Rest proxy always via the Store.
Question is... has anyone else seen the behavior? Is it a bug, or am I missing something?
Thanks...
Using your proxy definition, I've managed to make it work.
I am not sure why it doesn't work for you. I have only moved ZendRest to Prj.proxy namespace and added requires: ['Prj.proxy.ZendRest'] to the model.
Code:
// controller/Primary.js
Ext.define('Prj.controller.Primary', {
extend: 'Ext.app.Controller',
stores: ['Articles'],
models: ['Article'],
views: ['article.Grid']
});
// model/Article.js
Ext.define('Prj.model.Article', {
extend: 'Ext.data.Model',
fields: [
'title', 'author', {
name: 'pubDate',
type: 'date'
}, 'link', 'description', 'content'
],
requires: ['Prj.proxy.ZendRest'],
proxy: {
type: 'zest',
url: 'feed-proxy.php'
}
});
// store/Articles.js
Ext.define('Prj.store.Articles', {
extend: 'Ext.data.Store',
autoLoad: true,
model: 'Prj.model.Article'
});
// proxy/ZendRest.js
Ext.define('Prj.proxy.ZendRest', {
extend: 'Ext.data.proxy.Ajax',
alias : 'proxy.zest',
appendId: true,
batchActions: false,
buildUrl: function(request) {
var me = this,
operation = request.operation,
records = operation.records || [],
record = records[0],
format = me.format,
reqParams = request.params,
url = me.getUrl(request),
id = record ? record.getId() : operation.id;
if (me.appendId && id) {
if (!url.match(/\/$/)) {
url += '/';
}
url += 'id/' + id;
}
if (format) {
reqParams['format'] = format;
}
/* <for example purpose> */
//request.url = url;
/* </for example purpose> */
return me.callParent(arguments);
}
}, function() {
Ext.apply(this.prototype, {
actionMethods: {
create : 'POST',
read : 'GET',
update : 'PUT',
destroy: 'DELETE'
},
/* <for example purpose> */
reader: {
type: 'xml',
record: 'item'
}
/* </for example purpose> */
});
});
Here is working sample, and here zipped code.

Struts 2 won't transfer params from ExtJS form to ActionSupport class

I am implementing a simple ExtJS form that submits to a Struts 2 ActionSupport class. The code for the various components is as follows:
MyAction.java:
//packaging and imports
public class MyAction extends ActionSupport {
private String aField;
private String anotherField;
public String execute() throws Exception {
System.out.println(afield + " " + anotherField); //just checking values, atm
return ActionSupport.SUCCESS;
}
public String getAField() {
return this.aField;
}
public void setAField(String aField) {
this.aField = aField;
}
public String getAnotherField() {
return this.anotherField;
}
public void setAnotherField(String anotherField) {
this.anotherField = anotherField;
}
}
myForm.js:
Ext.onReady(function() {
Ext.QuickTips.init();
// turn on validation errors beside the field globally
Ext.form.Field.prototype.msgTarget = 'side';
var myForm = new Ext.form.FormPanel({
id: 'myFormId',
url: 'submitMyForm.action',
defaults: {
xtype: 'textfield'
},
items: [
{
fieldLabel: 'A Field',
id: 'aField',
name: 'aField',
allowBlank: false
},
{
fieldLabel: 'Another Field',
id: 'anotherField',
name: 'anotherField',
allowBlank: false
}
],
renderTo: 'contentMain'
});
var submitButton = new Ext.Button({
text: 'SUBMIT',
handler: function(button, event) {
myForm.getForm().submit({
url: 'submitMyForm.action',
failure: function() {
Ext.Msg.alert('Error', 'Can not save data.');
}
});
}
});
});
struts.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="myPackage" namespace="/" extends="json-default">
<action name="submitMyForm" class="mycodepackage.MyAction">
<result name="*" type="json">
<param name="includeProperties">aField</param>
</result>
</action>
</package>
</struts>
When the submit button is pressed, my action executes properly, and in addition to standard debugging data prints out:
null null
The JSON result is sent back correctly, but of course is also null:
14:22:17,046DEBUG JSONResult:68 - Adding include property expression: aField
14:22:17,052DEBUG JSONWriter:68 - Ignoring property because of include rule: anotherField
14:22:17,053DEBUG JSONUtil:68 - [JSON]{"aField":null}
Now, it's my understanding that the values entered in the form should be inserted into the instance variables for my action class. Am I wrong in this? If not, what could be going wrong? If so, what can I do to ensure that the form data is sent to my action handler?
Thanks.
Any parameters sent will be placed into the similarly named setters. Why don't you first check that the form parameters are getting sent correctly with LiveHttpHeaders Firefox plugin.
Once we realized the form data was not being correctly passed into the http request, my coworker developed a form data interceptor, which we use to load the data in manually. For more information, look into the <interceptor>, <interceptor-stack>, and <interceptor-ref> tags.