loic911 / Rest-api-doc

MIT License
21 stars 32 forks source link

The rest-api-doc command generates duplicate methods for annotated controllers #3

Closed bogdanalexe closed 10 years ago

bogdanalexe commented 10 years ago
  1. run rest-api-doc
  2. In the generated Json some methotds are duplicated
loic911 commented 10 years ago

Only some methods? Do they have something special (multiple url mappings for this method,...)? Could you please provide more details (sample code for duplicated method documentation and/or the json sample with duplicated method)?

Thanks

bogdanalexe commented 10 years ago

All of them except those who have the "listing = true " parameter. Here is an example of annotation for the show method:

@RestApiMethod(description = "Get a member", path = "/api/members/{id}", verb = RestApiVerb.GET) @RestApiParams(params = [ @RestApiParam(name = "id", type = "Long", paramType = RestApiParamType.PATH) ]) @RestApiResponseObject(objectIdentifier = "Member")

jsondoc

bogdanalexe commented 10 years ago

And here is the generated JSON:

{ "basePath": "http://localhost:8080", "apis": [ { "methods": [ { "headers": [], "bodyobject": { "mapKeyObject": "", "jsondocId": "5b67ced3-f055-42e8-9e32-0df19a779611", "mapValueObject": "", "map": "", "object": "member", "multiple": "Unknow" }, "jsondocId": "02cf451f-aa78-413a-a9bb-63805ed8c695", "consumes": ["application/json"], "response": { "mapKeyObject": "", "jsondocId": "6f1de1c3-8c1f-412b-b372-ca65df1c7370", "mapValueObject": "", "object": "Member", "multiple": "false" }, "pathparameters": [], "apierrors": [ { "jsondocId": "be17331b-2a48-4a48-a6c0-e8f9b2128769", "description": "Internal Server Error", "code": "500" }, { "jsondocId": "abe00557-ecf9-4e4e-82be-01b25dc0a4fb", "description": "Validation failed", "code": "406" } ], "verb": "POST", "description": "Create a member", "queryparameters": [], "path": "/api/members", "produces": ["application/json"], "methodName": "save" }, { "headers": [], "bodyobject": { "mapKeyObject": "", "jsondocId": "97ce9da4-ae2d-4c98-897c-730436479bd6", "mapValueObject": "", "map": "", "object": "member", "multiple": "Unknow" }, "jsondocId": "0f0386e2-0b3f-4f35-aec2-1985f5ecb4e8", "consumes": ["application/json"], "response": { "mapKeyObject": "", "jsondocId": "509d309b-b1d0-4d11-992e-3026e5f2b072", "mapValueObject": "", "object": "Member", "multiple": "false" }, "pathparameters": [], "apierrors": [ { "jsondocId": "4d74ff8c-07a4-4b56-b428-a3bd01acb958", "description": "Internal Server Error", "code": "500" }, { "jsondocId": "84884f3c-7be4-4f97-82c3-206ee6807b86", "description": "Validation failed", "code": "406" } ], "verb": "POST", "description": "Create a member", "queryparameters": [], "path": "/api/members", "produces": ["application/json"], "methodName": "save" }, { "headers": [], "bodyobject": null, "jsondocId": "ec8b7605-3811-45e2-b388-389e69b2fb91", "consumes": [], "response": { "mapKeyObject": "", "jsondocId": "b75a41b9-d9d2-43c9-8992-bbddb16f4d62", "mapValueObject": "", "object": "List", "multiple": "Member" }, "pathparameters": [], "apierrors": [ { "jsondocId": "d90b9360-0bc0-4eed-9be6-f42f6670ad21", "description": "Internal Server Error", "code": "500" }, { "jsondocId": "c7c99d29-d1bb-4c1f-ae68-a8d4dfb98a68", "description": "Object not found", "code": "404" } ], "verb": "GET", "description": "List members", "queryparameters": [ { "jsondocId": "e505e087-c3fb-4f53-9ec5-bb7b2d9c4bf3", "description": "Limit for listing", "name": "max", "allowedvalues": [], "format": "", "required": "false", "type": "Integer" }, { "jsondocId": "f78e43a0-5374-49fc-9227-3ac3fc7c0797", "description": "Offset for listing", "name": "offset", "allowedvalues": [], "format": "", "required": "false", "type": "Integer" }, { "jsondocId": "89e39ba8-c065-467c-a611-319ca65c5bda", "description": "Sort by field", "name": "sort", "allowedvalues": [], "format": "", "required": "false", "type": "String" }, { "jsondocId": "d1ba4746-dc43-4506-b89a-e5e38af03c34", "description": "Order for sorting", "name": "order", "allowedvalues": [ "asc", "desc" ], "format": "", "required": "false", "type": "String" }, { "jsondocId": "ec3aec7b-152a-4def-a137-7e28bd6a4510", "description": "Limit by tag", "name": "tag", "allowedvalues": [], "format": "", "required": "false", "type": "String" }, { "jsondocId": "1e6fdf77-017b-4f1c-84eb-0a2dce0a12fd", "description": "Limit by sex", "name": "sex", "allowedvalues": [ "male", "female" ], "format": "", "required": "false", "type": "String" }, { "jsondocId": "2143fdf4-49f8-4cea-94a7-5fb7265c6140", "description": "Limit by isSubscribed", "name": "isSubscribed", "allowedvalues": [], "format": "", "required": "false", "type": "Boolean" }, { "jsondocId": "b9eb39fc-519a-41f5-b519-6c35fdf435ee", "description": "Limit by search string", "name": "q", "allowedvalues": [], "format": "", "required": "false", "type": "String" } ], "path": "/api/members", "produces": ["application/json"], "methodName": "index" }, { "headers": [], "bodyobject": null, "jsondocId": "8bd8eb6b-18eb-4c2b-bafd-cb65af0b1bbd", "consumes": [], "response": { "mapKeyObject": "", "jsondocId": "dc44aee2-ca41-4bcb-9cdb-90f6f36560ab", "mapValueObject": "", "object": "void", "multiple": "false" }, "pathparameters": [{ "jsondocId": "a71cf9a0-3c33-4871-a2ae-2e93f785b3fb", "description": "", "name": "id", "allowedvalues": [], "format": "", "required": "true", "type": "Long" }], "apierrors": [ { "jsondocId": "4b968067-b101-4e51-bef1-825e28c261fb", "description": "Internal Server Error", "code": "500" }, { "jsondocId": "bc0943f0-77b5-4a87-8d61-10fda0afd754", "description": "Object not found", "code": "404" } ], "verb": "DELETE", "description": "Delete a member", "queryparameters": [], "path": "/api/members/{id}", "produces": ["application/json"], "methodName": "delete" }, { "headers": [], "bodyobject": null, "jsondocId": "66b9bab7-5531-4079-97f9-e6d190811e19", "consumes": [], "response": { "mapKeyObject": "", "jsondocId": "63dc3ba4-740e-4336-b725-570ada2ceddb", "mapValueObject": "", "object": "void", "multiple": "false" }, "pathparameters": [{ "jsondocId": "49da73ef-4aef-4fdd-9085-392a1462f21b", "description": "", "name": "id", "allowedvalues": [], "format": "", "required": "true", "type": "Long" }], "apierrors": [ { "jsondocId": "a25e61ff-6e67-493f-9037-bb98ba826aa6", "description": "Internal Server Error", "code": "500" }, { "jsondocId": "ebfa87fb-af0d-403c-b684-6561e84ab1d1", "description": "Object not found", "code": "404" } ], "verb": "DELETE", "description": "Delete a member", "queryparameters": [], "path": "/api/members/{id}", "produces": ["application/json"], "methodName": "delete" }, { "headers": [], "bodyobject": { "mapKeyObject": "", "jsondocId": "82102ec4-b939-4e27-9673-d191dcb50b98", "mapValueObject": "", "map": "", "object": "member", "multiple": "Unknow" }, "jsondocId": "ef29d6ef-e54f-44bb-a138-c2b7b794829d", "consumes": ["application/json"], "response": { "mapKeyObject": "", "jsondocId": "9579207a-08a8-4994-844a-779f7bfcfeaf", "mapValueObject": "", "object": "Member", "multiple": "false" }, "pathparameters": [{ "jsondocId": "f21bd0bc-b809-4c05-b7ec-c5228be8fd92", "description": "", "name": "id", "allowedvalues": [], "format": "", "required": "true", "type": "Long" }], "apierrors": [ { "jsondocId": "e68ec826-19bf-4fd1-a962-9379b5abf920", "description": "Internal Server Error", "code": "500" }, { "jsondocId": "7ea15806-5691-4395-a399-e78a649314c1", "description": "Object not found", "code": "404" }, { "jsondocId": "480ad08e-12b1-437b-b607-7d17f3117d23", "description": "Validation failed", "code": "406" } ], "verb": "PUT", "description": "Update a member", "queryparameters": [], "path": "/api/members/{id}", "produces": ["application/json"], "methodName": "update" }, { "headers": [], "bodyobject": { "mapKeyObject": "", "jsondocId": "69b94b71-49b9-4ccc-92ba-9886f0eef37c", "mapValueObject": "", "map": "", "object": "member", "multiple": "Unknow" }, "jsondocId": "23d791cb-7486-40a9-ab13-c1b508f5401c", "consumes": ["application/json"], "response": { "mapKeyObject": "", "jsondocId": "c79e2da0-c6cd-4d95-9c76-87136c236671", "mapValueObject": "", "object": "Member", "multiple": "false" }, "pathparameters": [{ "jsondocId": "02ab6571-6895-4754-9b85-30905319e40a", "description": "", "name": "id", "allowedvalues": [], "format": "", "required": "true", "type": "Long" }], "apierrors": [ { "jsondocId": "db19acaf-6abf-4f37-bf0c-b785ba260542", "description": "Internal Server Error", "code": "500" }, { "jsondocId": "2e5749c8-97b0-4be7-b21b-8341fc2c5bb7", "description": "Object not found", "code": "404" }, { "jsondocId": "9f22892d-a926-4c0a-a548-4e0857257dc6", "description": "Validation failed", "code": "406" } ], "verb": "PUT", "description": "Update a member", "queryparameters": [], "path": "/api/members/{id}", "produces": ["application/json"], "methodName": "update" }, { "headers": [], "bodyobject": null, "jsondocId": "b8fe736d-a047-40e4-bc54-85061474f54f", "consumes": [], "response": { "mapKeyObject": "", "jsondocId": "411e65ed-57cd-4539-927e-d1729e63de0f", "mapValueObject": "", "object": "Member", "multiple": "false" }, "pathparameters": [{ "jsondocId": "848c060c-6ab8-4efb-bb3f-013b625035db", "description": "", "name": "id", "allowedvalues": [], "format": "", "required": "true", "type": "Long" }], "apierrors": [ { "jsondocId": "0183984f-5160-412b-afe0-2b8aecc4442e", "description": "Internal Server Error", "code": "500" }, { "jsondocId": "5bb6a59d-76af-4bbf-8933-08eeb36fd1e4", "description": "Object not found", "code": "404" } ], "verb": "GET", "description": "Get a member", "queryparameters": [], "path": "/api/members/{id}", "produces": ["application/json"], "methodName": "show" }, { "headers": [], "bodyobject": null, "jsondocId": "062c9adf-7c27-4f7d-a6a2-2fdbcb1aaa1a", "consumes": [], "response": { "mapKeyObject": "", "jsondocId": "e184d7c0-16fd-4933-9031-76cf84dcdc9e", "mapValueObject": "", "object": "Member", "multiple": "false" }, "pathparameters": [{ "jsondocId": "f995bf5e-42e5-41b1-a382-80eea925aa15", "description": "", "name": "id", "allowedvalues": [], "format": "", "required": "true", "type": "Long" }], "apierrors": [ { "jsondocId": "28c5381e-c646-4f40-bb67-7508fb470c07", "description": "Internal Server Error", "code": "500" }, { "jsondocId": "192b0247-2f34-4aba-a0ae-5545958a27b1", "description": "Object not found", "code": "404" } ], "verb": "GET", "description": "Get a member", "queryparameters": [], "path": "/api/members/{id}", "produces": ["application/json"], "methodName": "show" }, { "headers": [], "bodyobject": null, "jsondocId": "482cf970-ab10-48c1-b614-11cb1456f762", "consumes": [], "response": { "mapKeyObject": "", "jsondocId": "cf57cc51-56c5-4058-8e5c-4c87f5b8786c", "mapValueObject": "", "object": "List", "multiple": "MemberTag" }, "pathparameters": [], "apierrors": [ { "jsondocId": "cfbde57c-4393-4229-9503-84a50b9e3b9c", "description": "Internal Server Error", "code": "500" }, { "jsondocId": "bcb850c2-6816-4c5f-8a2e-4a56aa7e269f", "description": "Object not found", "code": "404" } ], "verb": "GET", "description": "List available tags for members", "queryparameters": [], "path": "/api/members/tags", "produces": ["application/json"], "methodName": "listTags" } ], "jsondocId": "d94b204a-79ff-4db6-8cb8-52b202508991", "description": "Methods for managing members", "name": "Member services" } ], "objects": [], "version": "0.1.1" }

loic911 commented 10 years ago

Strange, still cannot reproduced... I'v try with your doc code in the sample project.

The "pseudocode" for method doc is:

FOR EACH controller in Controllers
      IF controller has @RestApi
            FOR EACH method in controller.methods
                IF method has @RestApiMethod
                           EXTRACT_DATA()
                           ADD_TO_METHOD_LIST()

I don't see where you may have a method twice in the list.

If you can grive me a grails project (a new simple project with this bug or your project if Opensource), it would help.

Thanks

roadtripryan commented 10 years ago

Just a me too. I have the same issue with one of my controllers showing methods more than once. The method that does not take any parameters, shows up one. The other methods that take parameters all show up twice. Another controller that has methods with parameters does NOT duplicate them.

loic911 commented 10 years ago

Could you try a clean (or clean-all) after a rest-api-doc?

roadtripryan commented 10 years ago

Tried grails clean, grails rest-api-doc, grails clean with the same results. Odd it is only one of 3 controllers that has duplicates. I'm using grails 2.3.4.

ghost commented 10 years ago

me too.

create a simple Person with a firstName and lastName. annotate as documented

generate a Person controller annotate the class level, and only the index method

run grails rest-api-doc

index method is duplicated in the json api file:

{ "basePath": "/restApiTest", "apis": [{ "methods": [ { "headers": [], "bodyobject": null, "jsondocId": "47f70062-4d0c-40c9-a575-579782c82a11", "consumes": [], "response": { "mapKeyObject": "", "jsondocId": "33cf22f7-dfc4-4df2-bc4b-d6241b5cfb5e", "mapValueObject": "", "object": "person", "multiple": "Unknow" }, "pathparameters": [], "apierrors": [], "verb": "GET", "description": "get the collection of all persons", "queryparameters": [], "path": "/person.json", "produces": [ "xml", "json" ], "methodName": "index" }, { "headers": [], "bodyobject": null, "jsondocId": "21e17ea9-e9da-4363-9356-0b8ae8dfaa15", "consumes": [], "response": { "mapKeyObject": "", "jsondocId": "6df43cea-ea7f-4247-9eb1-2c58d92900be", "mapValueObject": "", "object": "person", "multiple": "Unknow" }, "pathparameters": [], "apierrors": [], "verb": "GET", "description": "get the collection of all persons", "queryparameters": [], "path": "/person.json", "produces": [ "xml", "json" ], "methodName": "index" } ], "jsondocId": "12fdcc0f-8f5c-4614-af86-b1ddde28056a", "description": "person service", "name": "person service" }], "objects": [{ "jsondocId": "a31e056e-ce2a-4d76-b582-3cd5613d753a", "description": "A person identity", "name": "person", "fields": [ { "jsondocId": "d456a206-dca2-4023-a5a5-54fa86c1265d", "description": "Persons last name", "presentInResponse": true, "mandatory": true, "name": "firstName", "allowedvalues": null, "useForCreation": true, "format": null, "defaultValue": null, "type": "String", "multiple": "false" }, { "jsondocId": "f350d92a-927b-4a66-86d5-05554b3fef57", "description": "Persons first name", "presentInResponse": true, "mandatory": true, "name": "lastName", "allowedvalues": null, "useForCreation": true, "format": null, "defaultValue": null, "type": "String", "multiple": "false" } ] }], "version": "0.1.1" }

ghost commented 10 years ago

package com.tksoftware

import static org.springframework.http.HttpStatus.*

import grails.transaction.Transactional import org.restapidoc.annotation.RestApi import org.restapidoc.annotation.RestApiMethod import org.restapidoc.pojo.RestApiVerb

@Transactional(readOnly = true) @RestApi(name='person service', description='person service') class PersonController {

static allowedMethods = [save: "POST", update: "PUT", delete: "DELETE"]
static responseFormats = ['xml', 'json']

@RestApiMethod(
    description='get the collection of all persons', 
    listing=true, 
    verb=RestApiVerb.GET,
    produces=['xml', 'json']
    )
def index(Integer max) {
    params.max = Math.min(max ?: 10, 100)
    respond Person.list(params), model:[personInstanceCount: Person.count()]
}

... Other non-annotated methods }

loic911 commented 10 years ago

Thanks for the full sample. It seems that without the parameter (Integer max), its better. I will invastigate this, a new realease should be available shortly.

ghost commented 10 years ago

I just noticed also that getting the jsondoc report behind a firewall is not possible because the jquery.js is being retrieved directly from googleapis instead of from a local js directory

loic911 commented 10 years ago

The duplication bug should be fix now in the new release (1.1.2)