diff --git a/pom.xml b/pom.xml index 0b55d526be16..2e3cc3f8eff7 100644 --- a/pom.xml +++ b/pom.xml @@ -134,9 +134,9 @@ - org.scala-tools - maven-scala-plugin - 2.15.2 + net.alchim31.maven + scala-maven-plugin + 3.0.1 scala-compile-first @@ -166,8 +166,8 @@ - org.scala-tools - maven-scala-plugin + net.alchim31.maven + scala-maven-plugin ${scala-version} @@ -175,12 +175,12 @@ - + commons-io commons-io @@ -214,13 +214,32 @@ ${junit-version} test - - org.json4s - json4s-jackson_2.9.1 - 3.0.0-SNAPSHOT - + + com.fasterxml.jackson.module + jackson-module-scala + 2.0.0 + compile + + + com.fasterxml.jackson.core + jackson-annotations + ${jackson-version} + compile + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + 2.0.0 + compile + + + org.json4s + json4s-jackson_2.9.1 + 3.0.0-SNAPSHOT + + 2.0.4 1.1.1-SNAPSHOT 2.9.1-1 4.8.1 diff --git a/samples/client/petstore/objc/PetstoreClient/PetstoreClient.xcodeproj/project.xcworkspace/xcuserdata/tony.xcuserdatad/UserInterfaceState.xcuserstate b/samples/client/petstore/objc/PetstoreClient/PetstoreClient.xcodeproj/project.xcworkspace/xcuserdata/tony.xcuserdatad/UserInterfaceState.xcuserstate index 19d920078c61..05d3a0a62f29 100644 Binary files a/samples/client/petstore/objc/PetstoreClient/PetstoreClient.xcodeproj/project.xcworkspace/xcuserdata/tony.xcuserdatad/UserInterfaceState.xcuserstate and b/samples/client/petstore/objc/PetstoreClient/PetstoreClient.xcodeproj/project.xcworkspace/xcuserdata/tony.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/samples/client/petstore/python/PetApi.py b/samples/client/petstore/python/PetApi.py index bfdd663f0c7e..1d999f69440f 100644 --- a/samples/client/petstore/python/PetApi.py +++ b/samples/client/petstore/python/PetApi.py @@ -134,7 +134,7 @@ class PetApi(object): - def findPetsByStatus(self, status, **kwargs): + def findPetsByStatus(self, status= None, **kwargs): """Finds Pets by status Args: diff --git a/samples/client/petstore/python/PythonPetstoreCodegen.scala b/samples/client/petstore/python/PythonPetstoreCodegen.scala index 032fb6c38b51..5db4b3445612 100644 --- a/samples/client/petstore/python/PythonPetstoreCodegen.scala +++ b/samples/client/petstore/python/PythonPetstoreCodegen.scala @@ -27,6 +27,6 @@ object PythonPetstoreCodegen extends BasicPythonGenerator { override def supportingFiles = List( ("__init__.mustache", destinationDir, "__init__.py"), - ("swagger.mustache", destinationDir + File.separator + apiPackage.get, "swagger.py"), - ("__init__.mustache", destinationDir + File.separator + modelPackage.get, "__init__.py")) + ("swagger.mustache", destinationDir + File.separator + apiPackage.getOrElse(""), "swagger.py"), + ("__init__.mustache", destinationDir + File.separator + modelPackage.getOrElse(""), "__init__.py")) } diff --git a/samples/client/petstore/python3/PetApi.py b/samples/client/petstore/python3/PetApi.py index cd8b636186d4..0bf9a88777b7 100644 --- a/samples/client/petstore/python3/PetApi.py +++ b/samples/client/petstore/python3/PetApi.py @@ -134,7 +134,7 @@ class PetApi(object): - def findPetsByStatus(self, status, **kwargs): + def findPetsByStatus(self, status= None, **kwargs): """Finds Pets by status Args: diff --git a/samples/client/petstore/python3/Python3PetstoreCodegen.scala b/samples/client/petstore/python3/Python3PetstoreCodegen.scala index 9acbad548d98..b28c8446bd38 100644 --- a/samples/client/petstore/python3/Python3PetstoreCodegen.scala +++ b/samples/client/petstore/python3/Python3PetstoreCodegen.scala @@ -27,6 +27,6 @@ object Python3PetstoreCodegen extends BasicPython3Generator { override def supportingFiles = List( ("__init__.mustache", destinationDir, "__init__.py"), - ("swagger.mustache", destinationDir + File.separator + apiPackage.get, "swagger.py"), - ("__init__.mustache", destinationDir + File.separator + modelPackage.get, "__init__.py")) + ("swagger.mustache", destinationDir + File.separator + apiPackage.getOrElse(""), "swagger.py"), + ("__init__.mustache", destinationDir + File.separator + modelPackage.getOrElse(""), "__init__.py")) } diff --git a/samples/client/wordnik-api/objc/WordnikApiClient/WordnikApiClient.xcodeproj/project.xcworkspace/xcuserdata/tony.xcuserdatad/UserInterfaceState.xcuserstate b/samples/client/wordnik-api/objc/WordnikApiClient/WordnikApiClient.xcodeproj/project.xcworkspace/xcuserdata/tony.xcuserdatad/UserInterfaceState.xcuserstate index 22462c81d066..8a9dac494333 100644 Binary files a/samples/client/wordnik-api/objc/WordnikApiClient/WordnikApiClient.xcodeproj/project.xcworkspace/xcuserdata/tony.xcuserdatad/UserInterfaceState.xcuserstate and b/samples/client/wordnik-api/objc/WordnikApiClient/WordnikApiClient.xcodeproj/project.xcworkspace/xcuserdata/tony.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/samples/client/wordnik-api/objc/client/NIKApiTokenStatus.m b/samples/client/wordnik-api/objc/client/NIKApiTokenStatus.m index 8a6b1ebe09e5..990004314030 100644 --- a/samples/client/wordnik-api/objc/client/NIKApiTokenStatus.m +++ b/samples/client/wordnik-api/objc/client/NIKApiTokenStatus.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKApiTokenStatus.h" @implementation NIKApiTokenStatus diff --git a/samples/client/wordnik-api/objc/client/NIKAudioFile.m b/samples/client/wordnik-api/objc/client/NIKAudioFile.m index 21ab8f405d6b..e364f86deaa7 100644 --- a/samples/client/wordnik-api/objc/client/NIKAudioFile.m +++ b/samples/client/wordnik-api/objc/client/NIKAudioFile.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKAudioFile.h" @implementation NIKAudioFile diff --git a/samples/client/wordnik-api/objc/client/NIKAuthenticationToken.m b/samples/client/wordnik-api/objc/client/NIKAuthenticationToken.m index 51883c74ddbb..37315edc109a 100644 --- a/samples/client/wordnik-api/objc/client/NIKAuthenticationToken.m +++ b/samples/client/wordnik-api/objc/client/NIKAuthenticationToken.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKAuthenticationToken.h" @implementation NIKAuthenticationToken diff --git a/samples/client/wordnik-api/objc/client/NIKBigram.m b/samples/client/wordnik-api/objc/client/NIKBigram.m index 46b2ca38761e..ef67cf65086d 100644 --- a/samples/client/wordnik-api/objc/client/NIKBigram.m +++ b/samples/client/wordnik-api/objc/client/NIKBigram.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKBigram.h" @implementation NIKBigram diff --git a/samples/client/wordnik-api/objc/client/NIKCitation.m b/samples/client/wordnik-api/objc/client/NIKCitation.m index 2cd9c11153fe..8cce7ee5e786 100644 --- a/samples/client/wordnik-api/objc/client/NIKCitation.m +++ b/samples/client/wordnik-api/objc/client/NIKCitation.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKCitation.h" @implementation NIKCitation diff --git a/samples/client/wordnik-api/objc/client/NIKContentProvider.m b/samples/client/wordnik-api/objc/client/NIKContentProvider.m index 2f93695596c6..1685e7932e17 100644 --- a/samples/client/wordnik-api/objc/client/NIKContentProvider.m +++ b/samples/client/wordnik-api/objc/client/NIKContentProvider.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKContentProvider.h" @implementation NIKContentProvider diff --git a/samples/client/wordnik-api/objc/client/NIKDefinition.m b/samples/client/wordnik-api/objc/client/NIKDefinition.m index fff37bf26c0b..ce557065ebfe 100644 --- a/samples/client/wordnik-api/objc/client/NIKDefinition.m +++ b/samples/client/wordnik-api/objc/client/NIKDefinition.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKDefinition.h" @implementation NIKDefinition diff --git a/samples/client/wordnik-api/objc/client/NIKDefinitionSearchResults.m b/samples/client/wordnik-api/objc/client/NIKDefinitionSearchResults.m index 02a011854e9b..7a277189b97f 100644 --- a/samples/client/wordnik-api/objc/client/NIKDefinitionSearchResults.m +++ b/samples/client/wordnik-api/objc/client/NIKDefinitionSearchResults.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKDefinitionSearchResults.h" @implementation NIKDefinitionSearchResults diff --git a/samples/client/wordnik-api/objc/client/NIKExample.m b/samples/client/wordnik-api/objc/client/NIKExample.m index 4e17e0727e9d..328a182e1c79 100644 --- a/samples/client/wordnik-api/objc/client/NIKExample.m +++ b/samples/client/wordnik-api/objc/client/NIKExample.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKExample.h" @implementation NIKExample diff --git a/samples/client/wordnik-api/objc/client/NIKExampleSearchResults.m b/samples/client/wordnik-api/objc/client/NIKExampleSearchResults.m index 5d2a239a6870..454ed90fedcc 100644 --- a/samples/client/wordnik-api/objc/client/NIKExampleSearchResults.m +++ b/samples/client/wordnik-api/objc/client/NIKExampleSearchResults.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKExampleSearchResults.h" @implementation NIKExampleSearchResults diff --git a/samples/client/wordnik-api/objc/client/NIKExampleUsage.m b/samples/client/wordnik-api/objc/client/NIKExampleUsage.m index d24cea211454..46a065b3a689 100644 --- a/samples/client/wordnik-api/objc/client/NIKExampleUsage.m +++ b/samples/client/wordnik-api/objc/client/NIKExampleUsage.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKExampleUsage.h" @implementation NIKExampleUsage diff --git a/samples/client/wordnik-api/objc/client/NIKFacet.m b/samples/client/wordnik-api/objc/client/NIKFacet.m index 8d0b123f0b8c..94ef0dd6d7a1 100644 --- a/samples/client/wordnik-api/objc/client/NIKFacet.m +++ b/samples/client/wordnik-api/objc/client/NIKFacet.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKFacet.h" @implementation NIKFacet diff --git a/samples/client/wordnik-api/objc/client/NIKFacetValue.m b/samples/client/wordnik-api/objc/client/NIKFacetValue.m index 902707ac89ac..d48ab2a58271 100644 --- a/samples/client/wordnik-api/objc/client/NIKFacetValue.m +++ b/samples/client/wordnik-api/objc/client/NIKFacetValue.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKFacetValue.h" @implementation NIKFacetValue diff --git a/samples/client/wordnik-api/objc/client/NIKFrequency.m b/samples/client/wordnik-api/objc/client/NIKFrequency.m index 6db1f78cd5b3..fe55d23657c8 100644 --- a/samples/client/wordnik-api/objc/client/NIKFrequency.m +++ b/samples/client/wordnik-api/objc/client/NIKFrequency.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKFrequency.h" @implementation NIKFrequency diff --git a/samples/client/wordnik-api/objc/client/NIKFrequencySummary.m b/samples/client/wordnik-api/objc/client/NIKFrequencySummary.m index baedb200d977..bc6f5d5b73a0 100644 --- a/samples/client/wordnik-api/objc/client/NIKFrequencySummary.m +++ b/samples/client/wordnik-api/objc/client/NIKFrequencySummary.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKFrequencySummary.h" @implementation NIKFrequencySummary diff --git a/samples/client/wordnik-api/objc/client/NIKLabel.m b/samples/client/wordnik-api/objc/client/NIKLabel.m index 4369c5607cc5..9afe99290f14 100644 --- a/samples/client/wordnik-api/objc/client/NIKLabel.m +++ b/samples/client/wordnik-api/objc/client/NIKLabel.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKLabel.h" @implementation NIKLabel diff --git a/samples/client/wordnik-api/objc/client/NIKNote.m b/samples/client/wordnik-api/objc/client/NIKNote.m index b05925e18118..3c5bb68842b7 100644 --- a/samples/client/wordnik-api/objc/client/NIKNote.m +++ b/samples/client/wordnik-api/objc/client/NIKNote.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKNote.h" @implementation NIKNote diff --git a/samples/client/wordnik-api/objc/client/NIKRelated.m b/samples/client/wordnik-api/objc/client/NIKRelated.m index d22e8d346b80..ef3213056d4a 100644 --- a/samples/client/wordnik-api/objc/client/NIKRelated.m +++ b/samples/client/wordnik-api/objc/client/NIKRelated.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKRelated.h" @implementation NIKRelated diff --git a/samples/client/wordnik-api/objc/client/NIKScoredWord.m b/samples/client/wordnik-api/objc/client/NIKScoredWord.m index ac2b784562f7..48f1226fc135 100644 --- a/samples/client/wordnik-api/objc/client/NIKScoredWord.m +++ b/samples/client/wordnik-api/objc/client/NIKScoredWord.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKScoredWord.h" @implementation NIKScoredWord diff --git a/samples/client/wordnik-api/objc/client/NIKSentence.m b/samples/client/wordnik-api/objc/client/NIKSentence.m index 48a867c26620..1f83356949fa 100644 --- a/samples/client/wordnik-api/objc/client/NIKSentence.m +++ b/samples/client/wordnik-api/objc/client/NIKSentence.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKSentence.h" @implementation NIKSentence diff --git a/samples/client/wordnik-api/objc/client/NIKSimpleDefinition.m b/samples/client/wordnik-api/objc/client/NIKSimpleDefinition.m index 5ca758158917..79807221c5fe 100644 --- a/samples/client/wordnik-api/objc/client/NIKSimpleDefinition.m +++ b/samples/client/wordnik-api/objc/client/NIKSimpleDefinition.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKSimpleDefinition.h" @implementation NIKSimpleDefinition diff --git a/samples/client/wordnik-api/objc/client/NIKSimpleExample.m b/samples/client/wordnik-api/objc/client/NIKSimpleExample.m index e8b4c9e90bc6..c1aa71fed632 100644 --- a/samples/client/wordnik-api/objc/client/NIKSimpleExample.m +++ b/samples/client/wordnik-api/objc/client/NIKSimpleExample.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKSimpleExample.h" @implementation NIKSimpleExample diff --git a/samples/client/wordnik-api/objc/client/NIKStringValue.m b/samples/client/wordnik-api/objc/client/NIKStringValue.m index c94417c0b138..049115a380e6 100644 --- a/samples/client/wordnik-api/objc/client/NIKStringValue.m +++ b/samples/client/wordnik-api/objc/client/NIKStringValue.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKStringValue.h" @implementation NIKStringValue diff --git a/samples/client/wordnik-api/objc/client/NIKSyllable.m b/samples/client/wordnik-api/objc/client/NIKSyllable.m index 9cf8194ac5fe..7b10b2752140 100644 --- a/samples/client/wordnik-api/objc/client/NIKSyllable.m +++ b/samples/client/wordnik-api/objc/client/NIKSyllable.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKSyllable.h" @implementation NIKSyllable diff --git a/samples/client/wordnik-api/objc/client/NIKTextPron.m b/samples/client/wordnik-api/objc/client/NIKTextPron.m index 634cb43319fe..94fb3656147c 100644 --- a/samples/client/wordnik-api/objc/client/NIKTextPron.m +++ b/samples/client/wordnik-api/objc/client/NIKTextPron.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKTextPron.h" @implementation NIKTextPron diff --git a/samples/client/wordnik-api/objc/client/NIKUser.m b/samples/client/wordnik-api/objc/client/NIKUser.m index 3bbdbda47b7e..755b70192f49 100644 --- a/samples/client/wordnik-api/objc/client/NIKUser.m +++ b/samples/client/wordnik-api/objc/client/NIKUser.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKUser.h" @implementation NIKUser diff --git a/samples/client/wordnik-api/objc/client/NIKWordList.m b/samples/client/wordnik-api/objc/client/NIKWordList.m index afd423c2508d..47b8f5d3fae7 100644 --- a/samples/client/wordnik-api/objc/client/NIKWordList.m +++ b/samples/client/wordnik-api/objc/client/NIKWordList.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKWordList.h" @implementation NIKWordList diff --git a/samples/client/wordnik-api/objc/client/NIKWordListWord.m b/samples/client/wordnik-api/objc/client/NIKWordListWord.m index f2462c5a8998..20ab1fd67c8b 100644 --- a/samples/client/wordnik-api/objc/client/NIKWordListWord.m +++ b/samples/client/wordnik-api/objc/client/NIKWordListWord.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKWordListWord.h" @implementation NIKWordListWord diff --git a/samples/client/wordnik-api/objc/client/NIKWordObject.m b/samples/client/wordnik-api/objc/client/NIKWordObject.m index c01aad1d322d..adc2cb26c4aa 100644 --- a/samples/client/wordnik-api/objc/client/NIKWordObject.m +++ b/samples/client/wordnik-api/objc/client/NIKWordObject.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKWordObject.h" @implementation NIKWordObject diff --git a/samples/client/wordnik-api/objc/client/NIKWordOfTheDay.m b/samples/client/wordnik-api/objc/client/NIKWordOfTheDay.m index 165ee6a626ee..08921b2d07f4 100644 --- a/samples/client/wordnik-api/objc/client/NIKWordOfTheDay.m +++ b/samples/client/wordnik-api/objc/client/NIKWordOfTheDay.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKWordOfTheDay.h" @implementation NIKWordOfTheDay diff --git a/samples/client/wordnik-api/objc/client/NIKWordSearchResult.m b/samples/client/wordnik-api/objc/client/NIKWordSearchResult.m index 135eaf45e35f..28135ce3dc08 100644 --- a/samples/client/wordnik-api/objc/client/NIKWordSearchResult.m +++ b/samples/client/wordnik-api/objc/client/NIKWordSearchResult.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKWordSearchResult.h" @implementation NIKWordSearchResult diff --git a/samples/client/wordnik-api/objc/client/NIKWordSearchResults.m b/samples/client/wordnik-api/objc/client/NIKWordSearchResults.m index eba15e556f80..76a639d75858 100644 --- a/samples/client/wordnik-api/objc/client/NIKWordSearchResults.m +++ b/samples/client/wordnik-api/objc/client/NIKWordSearchResults.m @@ -1,4 +1,3 @@ -#import "NIKDate.h" #import "NIKWordSearchResults.h" @implementation NIKWordSearchResults diff --git a/samples/server-generator/node/output/App/main.js b/samples/server-generator/node/output/App/main.js index c87aa53c237b..10bc260d01dd 100644 --- a/samples/server-generator/node/output/App/main.js +++ b/samples/server-generator/node/output/App/main.js @@ -9,18 +9,18 @@ app.use(express.bodyParser()); swagger.setAppHandler(app); // resources for the demo -var petApi = require("./apis/PetApi.js"); var storeApi = require("./apis/StoreApi.js"); +var petApi = require("./apis/PetApi.js"); var userApi = require("./apis/UserApi.js"); swagger.addModels(models) + .addGET(storeApi.getOrderById) + .addDELETE(storeApi.deleteOrder) + .addPOST(storeApi.placeOrder) .addGET(petApi.getPetById) .addPOST(petApi.addPet) .addPUT(petApi.updatePet) .addGET(petApi.findPetsByStatus) .addGET(petApi.findPetsByTags) - .addGET(storeApi.getOrderById) - .addDELETE(storeApi.deleteOrder) - .addPOST(storeApi.placeOrder) .addPOST(userApi.createUsersWithArrayInput) .addPOST(userApi.createUser) .addPOST(userApi.createUsersWithListInput) diff --git a/samples/server-generator/node/output/App/models.js b/samples/server-generator/node/output/App/models.js index e0eb13dddaef..1585a850e2cc 100644 --- a/samples/server-generator/node/output/App/models.js +++ b/samples/server-generator/node/output/App/models.js @@ -1,2 +1,135 @@ exports.models = { - "Pet": {"id":"Pet","properties":{"id":{"type":"long"},"tags":{"items":{"$ref":"Tag"},"type":"Array"},"category":{"type":"Category"},"status":{"allowableValues":{"valueType":"LIST","values":["available","pending","sold"],"valueType":"LIST"},"description":"pet status in the store","type":"string"},"name":{"type":"string"},"photoUrls":{"items":{"type":"string"},"type":"Array"}}},"Category": {"id":"Category","properties":{"id":{"type":"long"},"name":{"type":"string"}}},"Tag": {"id":"Tag","properties":{"id":{"type":"long"},"name":{"type":"string"}}},"User": {"id":"User","properties":{"id":{"type":"long"},"lastName":{"type":"string"},"username":{"type":"string"},"phone":{"type":"string"},"email":{"type":"string"},"userStatus":{"allowableValues":{"valueType":"LIST","values":["1-registered","2-active","3-closed"],"valueType":"LIST"},"description":"User Status","type":"int"},"firstName":{"type":"string"},"password":{"type":"string"}}},"Order": {"id":"Order","properties":{"id":{"type":"long"},"petId":{"type":"long"},"status":{"allowableValues":{"valueType":"LIST","values":["placed"," approved"," delivered"],"valueType":"LIST"},"description":"Order Status","type":"string"},"quantity":{"type":"int"},"shipDate":{"type":"Date"}}}} + "Pet": { + "id" : "Pet", + "properties" : { + "id" : { + "type" : "long", + "required" : false + }, + "tags" : { + "type" : "Array", + "required" : false, + "items" : { + "$ref" : "Tag" + } + }, + "category" : { + "type" : "Category", + "required" : false + }, + "status" : { + "type" : "string", + "required" : false, + "description" : "pet status in the store", + "allowableValues" : { + "values" : [ "available", "pending", "sold" ], + "valueType" : "LIST" + } + }, + "name" : { + "type" : "string", + "required" : false + }, + "photoUrls" : { + "type" : "Array", + "required" : false, + "items" : { + "type" : "string" + } + } + } +},"Category": { + "id" : "Category", + "properties" : { + "id" : { + "type" : "long", + "required" : false + }, + "name" : { + "type" : "string", + "required" : false + } + } +},"Tag": { + "id" : "Tag", + "properties" : { + "id" : { + "type" : "long", + "required" : false + }, + "name" : { + "type" : "string", + "required" : false + } + } +},"User": { + "id" : "User", + "properties" : { + "id" : { + "type" : "long", + "required" : false + }, + "lastName" : { + "type" : "string", + "required" : false + }, + "username" : { + "type" : "string", + "required" : false + }, + "phone" : { + "type" : "string", + "required" : false + }, + "email" : { + "type" : "string", + "required" : false + }, + "userStatus" : { + "type" : "int", + "required" : false, + "description" : "User Status", + "allowableValues" : { + "values" : [ "1-registered", "2-active", "3-closed" ], + "valueType" : "LIST" + } + }, + "firstName" : { + "type" : "string", + "required" : false + }, + "password" : { + "type" : "string", + "required" : false + } + } +},"Order": { + "id" : "Order", + "properties" : { + "id" : { + "type" : "long", + "required" : false + }, + "petId" : { + "type" : "long", + "required" : false + }, + "status" : { + "type" : "string", + "required" : false, + "description" : "Order Status", + "allowableValues" : { + "values" : [ "placed", " approved", " delivered" ], + "valueType" : "LIST" + } + }, + "quantity" : { + "type" : "int", + "required" : false + }, + "shipDate" : { + "type" : "Date", + "required" : false + } + } +}} diff --git a/samples/server-generator/scalatra/output/src/main/scala/com/wordnik/client/model/Pet.scala b/samples/server-generator/scalatra/output/src/main/scala/com/wordnik/client/model/Pet.scala index 5fe768d81a41..31e4a0ae565d 100644 --- a/samples/server-generator/scalatra/output/src/main/scala/com/wordnik/client/model/Pet.scala +++ b/samples/server-generator/scalatra/output/src/main/scala/com/wordnik/client/model/Pet.scala @@ -6,10 +6,10 @@ import scala.reflect.BeanProperty case class Pet ( id: Long, - tags: java.util.List[Tag], + tags: List[Tag], category: Category, /* pet status in the store */ status: String, name: String, - photoUrls: java.util.List[String]) + photoUrls: List[String]) diff --git a/samples/server-generator/scalatra/templates/api.mustache b/samples/server-generator/scalatra/templates/api.mustache index ad18a0f8b2c0..39e82ccf0822 100644 --- a/samples/server-generator/scalatra/templates/api.mustache +++ b/samples/server-generator/scalatra/templates/api.mustache @@ -16,7 +16,7 @@ class {{className}} (implicit val swagger: Swagger) extends ScalatraServlet with protected implicit val jsonFormats: Formats = DefaultFormats protected val applicationDescription: String = "{{className}}" - override protected val applicationName: Option[String] = Some("{{name}}") + override protected val applicationName: Option[String] = Some("{{baseName}}") def swaggerToModel(cls: Class[_]) = { val docObj = ApiPropertiesReader.read(cls) diff --git a/samples/server-generator/scalatra/templates/model.mustache b/samples/server-generator/scalatra/templates/model.mustache index 145616684e79..5f30743935e1 100644 --- a/samples/server-generator/scalatra/templates/model.mustache +++ b/samples/server-generator/scalatra/templates/model.mustache @@ -10,8 +10,6 @@ import scala.reflect.BeanProperty case class {{classname}} ( {{#vars}} - {{#notes}}/* {{notes}} */ - {{/notes}} {{#description}}/* {{description}} */ {{/description}} {{name}}: {{datatype}}{{#hasMore}},{{newline}} {{/hasMore}} diff --git a/samples/server-generator/sinatra/SinatraServerGenerator.scala b/samples/server-generator/sinatra/SinatraServerGenerator.scala index c7f23c66b10f..b3dc2573e988 100644 --- a/samples/server-generator/sinatra/SinatraServerGenerator.scala +++ b/samples/server-generator/sinatra/SinatraServerGenerator.scala @@ -24,9 +24,11 @@ import scala.collection.mutable.{ HashMap, ListBuffer } object SinatraServerGenerator extends BasicRubyGenerator { def main(args: Array[String]) = generateClient(args) - override def toApiName(name: String): String = { - name + "_api" - } + override def toApiName(name: String): String = name + "_api" + + // undo the ruby-ish conversions in the BasicRubyGenerator + override def toVarName(name: String): String = name + override def toMethodName(name: String): String = name override def templateDir = "samples/server-generator/sinatra/templates" @@ -57,7 +59,7 @@ object SinatraServerGenerator extends BasicRubyGenerator { mutable.map(k => { k._1 match { - // the scalatra templates like lower-case httpMethods + // the sinatra templates like lower-case httpMethods case e: String if (e == "httpMethod") => mutable += "httpMethod" -> k._2.toString.toLowerCase // convert path into ruby-ish syntax without basePart (i.e. /pet.{format}/{petId} => /:petId diff --git a/samples/server-generator/sinatra/output/README.md b/samples/server-generator/sinatra/output/README.md index e09f82c3a798..23c501353da5 100644 --- a/samples/server-generator/sinatra/output/README.md +++ b/samples/server-generator/sinatra/output/README.md @@ -17,7 +17,7 @@ sinatra-cross_origin This sample was generated with the [swagger-codegen](https://github.com/wordnik/swagger-codegen) project. ``` -rackup -p 4567 +rackup -p 4567 config.ru ``` In your [swagger ui](https://github.com/wordnik/swagger-ui), put in the following URL: diff --git a/samples/server-generator/sinatra/output/my_app.rb b/samples/server-generator/sinatra/output/my_app.rb index d0dd2d68ad3c..415efe1a84d7 100644 --- a/samples/server-generator/sinatra/output/my_app.rb +++ b/samples/server-generator/sinatra/output/my_app.rb @@ -7,7 +7,7 @@ class MyApp < Swaggering end end -require './lib/pet_api.rb' require './lib/store_api.rb' +require './lib/pet_api.rb' require './lib/user_api.rb' diff --git a/samples/server-generator/sinatra/templates/README.md b/samples/server-generator/sinatra/templates/README.md index beec4407438e..23c501353da5 100644 --- a/samples/server-generator/sinatra/templates/README.md +++ b/samples/server-generator/sinatra/templates/README.md @@ -17,7 +17,7 @@ sinatra-cross_origin This sample was generated with the [swagger-codegen](https://github.com/wordnik/swagger-codegen) project. ``` -rackup -p 4567 examples/config.ru +rackup -p 4567 config.ru ``` In your [swagger ui](https://github.com/wordnik/swagger-ui), put in the following URL: diff --git a/samples/server-generator/sinatra/templates/api.mustache b/samples/server-generator/sinatra/templates/api.mustache index 4dede10ab797..cfeae19de7ac 100644 --- a/samples/server-generator/sinatra/templates/api.mustache +++ b/samples/server-generator/sinatra/templates/api.mustache @@ -4,7 +4,7 @@ require 'json' {{#operation}} MyApp.add_route('{{httpMethod}}', '{{path}}', { - "resourcePath" => "/{{name}}", + "resourcePath" => "/{{baseName}}", "summary" => "{{{summary}}}", "nickname" => "{{nickname}}", "responseClass" => "{{#returnType}}{{returnType}}{{/returnType}}{{^returnType}}void{{/returnType}}", diff --git a/src/main/resources/Java/api.mustache b/src/main/resources/Java/api.mustache index 5517e99206e2..9632e3ce763f 100644 --- a/src/main/resources/Java/api.mustache +++ b/src/main/resources/Java/api.mustache @@ -1,7 +1,7 @@ package {{package}}; -import com.wordnik.client.ApiException; -import com.wordnik.client.ApiInvoker; +import {{invokerPackage}}.ApiException; +import {{invokerPackage}}.ApiInvoker; {{#imports}}import {{import}}; {{/imports}} diff --git a/src/main/resources/Java/apiException.mustache b/src/main/resources/Java/apiException.mustache index 9a1f62b3061e..a6bcba75b7c1 100644 --- a/src/main/resources/Java/apiException.mustache +++ b/src/main/resources/Java/apiException.mustache @@ -1,4 +1,4 @@ -package com.wordnik.client; +package {{invokerPackage}}; public class ApiException extends Exception { int code = 0; diff --git a/src/main/resources/Java/model.mustache b/src/main/resources/Java/model.mustache index aab0e4670815..f4472d468974 100644 --- a/src/main/resources/Java/model.mustache +++ b/src/main/resources/Java/model.mustache @@ -7,9 +7,7 @@ package {{package}}; public class {{classname}} { {{#vars}} - {{#notes}}/* {{notes}} */ - {{/notes}} - {{#description}}/* {{description}} */ + {{#description}}/* {{{description}}} */ {{/description}} private {{{datatype}}} {{name}} = {{{defaultValue}}}; {{/vars}} diff --git a/src/main/resources/flash/model.mustache b/src/main/resources/flash/model.mustache index 5720c96508b7..c3369514e80e 100644 --- a/src/main/resources/flash/model.mustache +++ b/src/main/resources/flash/model.mustache @@ -9,8 +9,6 @@ package {{package}} { public class {{classname}} { {{#vars}} - {{#notes}}/* {{notes}} */ - {{/notes}} {{#description}}/* {{description}} */ {{/description}} diff --git a/src/main/resources/scala/model.mustache b/src/main/resources/scala/model.mustache index a6fe797c5fae..59b6084dd207 100644 --- a/src/main/resources/scala/model.mustache +++ b/src/main/resources/scala/model.mustache @@ -10,9 +10,7 @@ package {{package}} case class {{classname}} ( {{#vars}} - {{#notes}}/* {{notes}} */ - {{/notes}} - {{#description}}/* {{description}} */ + {{#description}}/* {{{description}}} */ {{/description}} {{name}}: {{datatype}}{{#hasMore}},{{newline}} {{/hasMore}} {{/vars}}) diff --git a/src/main/scala/com/wordnik/swagger/codegen/BasicFlashCodegen.scala b/src/main/scala/com/wordnik/swagger/codegen/BasicFlashCodegen.scala index d8e3fd077f1f..b48caa443e71 100644 --- a/src/main/scala/com/wordnik/swagger/codegen/BasicFlashCodegen.scala +++ b/src/main/scala/com/wordnik/swagger/codegen/BasicFlashCodegen.scala @@ -16,17 +16,14 @@ package com.wordnik.swagger.codegen -import com.wordnik.swagger.core._ +import com.wordnik.swagger.model._ abstract class BasicFlashCodegen extends BasicGenerator { override def defaultIncludes = Set( "Date", - "double", - "int", - "long", - "float", "String", - "boolean") + "Boolean", + "Number") override def typeMapping = Map( "boolean" -> "Boolean", @@ -110,8 +107,8 @@ abstract class BasicFlashCodegen extends BasicGenerator { typeMapping.getOrElse(declaredType, declaredType) } - override def toDeclaration(obj: DocumentationSchema) = { - var declaredType = toDeclaredType(obj.getType) + override def toDeclaration(obj: ModelProperty) = { + var declaredType = toDeclaredType(obj.`type`) declaredType match { case "Array" => { @@ -131,9 +128,8 @@ abstract class BasicFlashCodegen extends BasicGenerator { (declaredType, defaultValue) } - // default values - override def toDefaultValue(properCase: String, obj: DocumentationSchema) = { - properCase match { + override def toDefaultValue(dataType: String, obj: ModelProperty) = { + dataType match { case "Boolean" => "false" case "Number" => "0.0" case "List" => "new Array()" diff --git a/src/main/scala/com/wordnik/swagger/codegen/BasicGenerator.scala b/src/main/scala/com/wordnik/swagger/codegen/BasicGenerator.scala index dbeeddf16d2e..1972847af7ab 100644 --- a/src/main/scala/com/wordnik/swagger/codegen/BasicGenerator.scala +++ b/src/main/scala/com/wordnik/swagger/codegen/BasicGenerator.scala @@ -16,18 +16,18 @@ package com.wordnik.swagger.codegen -import com.wordnik.swagger.codegen.language.CodegenConfig import com.wordnik.swagger.codegen._ import com.wordnik.swagger.codegen.util._ -import com.wordnik.swagger.core._ -import com.wordnik.swagger.core.util.JsonUtil +import com.wordnik.swagger.codegen.language.CodegenConfig +import com.wordnik.swagger.codegen.spec.SwaggerSpecValidator +import com.wordnik.swagger.model._ + import java.io.{ File, FileWriter } import scala.io._ import scala.collection.JavaConversions._ import scala.collection.mutable.{ ListBuffer, HashMap, HashSet } import scala.io.Source -import spec.SwaggerSpecValidator abstract class BasicGenerator extends CodegenConfig with PathUtil { def packageName = "com.wordnik.client" @@ -42,24 +42,6 @@ abstract class BasicGenerator extends CodegenConfig with PathUtil { var codegen = new Codegen(this) def json = ScalaJsonUtil.getJsonMapper - def extractOperations(subDocs:List[Documentation], allModels: HashMap[String, DocumentationSchema] )(implicit basePath:String) = { - val output = new ListBuffer[(String, String, DocumentationOperation)] - subDocs.foreach(subDoc => { - val basePath = subDoc.basePath - val resourcePath = subDoc.resourcePath - if (subDoc.getApis != null) { - subDoc.getApis.foreach(api => { - for ((apiPath, operation) <- ApiExtractor.extractOperations(basePath, api)) { - output += Tuple3(basePath, apiPath, operation) - } - }) - output.map(op => processOperation(op._2, op._3)) - allModels ++= CoreUtils.extractModels(subDoc) - } - }) - output.toList - } - def generateClient(args: Array[String]) = { if (args.length == 0) { throw new RuntimeException("Need url to resources.json as argument. You can also specify VM Argument -DfileMap=/path/to/folder/containing.resources.json/") @@ -71,8 +53,7 @@ abstract class BasicGenerator extends CodegenConfig with PathUtil { } val doc = { try { - val jsonString = ResourceExtractor.extractListing(getResourcePath(host), apiKey) - json.readValue(jsonString, classOf[Documentation]) + ResourceExtractor.fetchListing(getResourcePath(host), apiKey) } catch { case e: Exception => throw new Exception("unable to read from " + host, e) } @@ -80,19 +61,17 @@ abstract class BasicGenerator extends CodegenConfig with PathUtil { implicit val basePath = getBasePath(doc.basePath) - val apis = doc.getApis - if (apis == null) + val apiReferences = doc.apis + if (apiReferences == null) throw new Exception("No APIs specified by resource") - val subDocs = ApiExtractor.extractApiDocs(basePath, apis.toList, apiKey) - // val models = CoreUtils.extractAllModels(subDocs) + val apis = ApiExtractor.fetchApiListings(basePath, apiReferences, apiKey) - new SwaggerSpecValidator(doc, subDocs).validate() + new SwaggerSpecValidator(doc, apis).validate() - val allModels = new HashMap[String, DocumentationSchema] - val operations = extractOperations(subDocs, allModels) - val apiMap = groupApisToFiles(operations) - - val modelBundle = prepareModelBundle(allModels.toMap) + val allModels = new HashMap[String, Model] + val operations = extractApiOperations(apis, allModels) + val operationMap = groupOperationsToFiles(operations) + val modelBundle = prepareModelMap(allModels.toMap) val modelFiles = bundleToSource(modelBundle, modelTemplateFiles.toMap) modelFiles.map(m => { @@ -107,7 +86,7 @@ abstract class BasicGenerator extends CodegenConfig with PathUtil { println("wrote model " + filename) }) - val apiBundle = prepareApiBundle(apiMap.toMap) + val apiBundle = prepareApiBundle(operationMap.toMap) val apiFiles = bundleToSource(apiBundle, apiTemplateFiles.toMap) apiFiles.map(m => { @@ -122,18 +101,35 @@ abstract class BasicGenerator extends CodegenConfig with PathUtil { println("wrote api " + filename) }) - codegen.writeSupportingClasses(apiMap.toMap, allModels.toMap) + codegen.writeSupportingClasses(operationMap, allModels.toMap) + } + + def extractApiOperations(apiListings: List[ApiListing], allModels: HashMap[String, Model] )(implicit basePath:String) = { + val output = new ListBuffer[(String, String, Operation)] + apiListings.foreach(apiDescription => { + val basePath = apiDescription.basePath + val resourcePath = apiDescription.resourcePath + apiDescription.apis.foreach(api => { + for ((apiPath, operation) <- ApiExtractor.extractApiOperations(basePath, api)) { + output += Tuple3(basePath, apiPath, operation) + } + }) + output.map(op => processApiOperation(op._2, op._3)) + allModels ++= CoreUtils.extractApiModels(apiDescription) + }) + output.toList } /** * creates a map of models and properties needed to write source */ - def prepareModelBundle(models: Map[String, DocumentationSchema]): List[Map[String, AnyRef]] = { + def prepareModelMap(models: Map[String, Model]): List[Map[String, AnyRef]] = { (for ((name, schema) <- models) yield { if (!defaultIncludes.contains(name)) { val m = new HashMap[String, AnyRef] m += "name" -> toModelName(name) - m += "className" -> (name) + m += "className" -> name + m += "filename" -> toModelFilename(name) m += "apis" -> None m += "models" -> List((name, schema)) m += "package" -> modelPackage @@ -147,13 +143,16 @@ abstract class BasicGenerator extends CodegenConfig with PathUtil { }).flatten.toList } - def prepareApiBundle(apiMap: Map[(String, String), List[(String, DocumentationOperation)]] ): List[Map[String, AnyRef]] = { + def prepareApiBundle(apiMap: Map[(String, String), List[(String, Operation)]] ): List[Map[String, AnyRef]] = { (for ((identifier, operationList) <- apiMap) yield { val basePath = identifier._1 val name = identifier._2 val className = toApiName(name) val m = new HashMap[String, AnyRef] + + m += "baseName" -> name + m += "filename" -> toApiFilename(name) m += "name" -> toApiName(name) m += "className" -> className m += "basePath" -> basePath @@ -168,14 +167,11 @@ abstract class BasicGenerator extends CodegenConfig with PathUtil { }).flatten.toList } - /** - * turns a bundle into source files with output file name - */ def bundleToSource(bundle:List[Map[String, AnyRef]], templates: Map[String, String]): List[(String, String)] = { val output = new ListBuffer[(String, String)] bundle.foreach(m => { for ((file, suffix) <- templates) { - output += Tuple2(m("outputDirectory").toString + File.separator + m("name").toString + suffix, codegen.generateSource(m, file)) + output += Tuple2(m("outputDirectory").toString + File.separator + m("filename").toString + suffix, codegen.generateSource(m, file)) } }) output.toList @@ -193,12 +189,12 @@ abstract class BasicGenerator extends CodegenConfig with PathUtil { println("wrote " + filename) } - def groupApisToFiles(operations: List[(String /*basePath*/ , String /*apiPath*/ , DocumentationOperation /* operation*/ )]): Map[(String, String), List[(String, DocumentationOperation)]] = { - val opMap = new HashMap[(String, String), ListBuffer[(String, DocumentationOperation)]] + def groupOperationsToFiles(operations: List[(String, String, Operation)]): Map[(String, String), List[(String, Operation)]] = { + val opMap = new HashMap[(String, String), ListBuffer[(String, Operation)]] for ((basePath, apiPath, operation) <- operations) { val className = resourceNameFromFullPath(apiPath) val listToAddTo = opMap.getOrElse((basePath, className), { - val l = new ListBuffer[(String, DocumentationOperation)] + val l = new ListBuffer[(String, Operation)] opMap += (basePath, className) -> l l }) diff --git a/src/main/scala/com/wordnik/swagger/codegen/BasicJavaGenerator.scala b/src/main/scala/com/wordnik/swagger/codegen/BasicJavaGenerator.scala index 1fbf742f9b57..833d53debaf6 100644 --- a/src/main/scala/com/wordnik/swagger/codegen/BasicJavaGenerator.scala +++ b/src/main/scala/com/wordnik/swagger/codegen/BasicJavaGenerator.scala @@ -16,7 +16,7 @@ package com.wordnik.swagger.codegen -import com.wordnik.swagger.core._ +import com.wordnik.swagger.model._ object BasicJavaGenerator extends BasicJavaGenerator { def main(args: Array[String]) = generateClient(args) @@ -30,6 +30,7 @@ class BasicJavaGenerator extends BasicGenerator { "float", "String", "boolean", + "Boolean", "Double", "Integer", "Long", @@ -43,6 +44,7 @@ class BasicJavaGenerator extends BasicGenerator { * variable declarations. */ override def typeMapping = Map( + "boolean" -> "Boolean", "string" -> "String", "int" -> "Integer", "float" -> "Float", @@ -114,13 +116,11 @@ class BasicJavaGenerator extends BasicGenerator { typeMapping.getOrElse(declaredType, declaredType) } - override def toDeclaration(obj: DocumentationSchema) = { - var declaredType = toDeclaredType(obj.getType) + override def toDeclaration(obj: ModelProperty) = { + var declaredType = toDeclaredType(obj.`type`) declaredType match { - case "Array" => { - declaredType = "List" - } + case "Array" => declaredType = "List" case e: String => e } @@ -128,8 +128,15 @@ class BasicJavaGenerator extends BasicGenerator { declaredType match { case "List" => { val inner = { - if (obj.items.ref != null) obj.items.ref - else obj.items.getType + obj.items match { + case Some(items) => { + if(items.ref != null) + items.ref + else + items.`type` + } + case _ => throw new Exception("no inner type defined") + } } declaredType += "<" + toDeclaredType(inner) + ">" } @@ -142,7 +149,7 @@ class BasicJavaGenerator extends BasicGenerator { * we are defaulting to null values since the codegen uses java objects instead of primitives * If you change to primitives, you can put in the appropriate values (0.0f, etc). */ - override def toDefaultValue(dataType: String, obj: DocumentationSchema) = { + override def toDefaultValue(dataType: String, obj: ModelProperty) = { dataType match { case "Boolean" => "null" case "Integer" => "null" @@ -151,8 +158,15 @@ class BasicJavaGenerator extends BasicGenerator { case "Double" => "null" case "List" => { val inner = { - if (obj.items.ref != null) obj.items.ref - else obj.items.getType + obj.items match { + case Some(items) => { + if(items.ref != null) + items.ref + else + items.`type` + } + case _ => throw new Exception("no inner type defined") + } } "new ArrayList<" + toDeclaredType(inner) + ">" + "()" } diff --git a/src/main/scala/com/wordnik/swagger/codegen/BasicObjcGenerator.scala b/src/main/scala/com/wordnik/swagger/codegen/BasicObjcGenerator.scala index d3a965135ce9..79e60f9f81f3 100644 --- a/src/main/scala/com/wordnik/swagger/codegen/BasicObjcGenerator.scala +++ b/src/main/scala/com/wordnik/swagger/codegen/BasicObjcGenerator.scala @@ -16,7 +16,7 @@ package com.wordnik.swagger.codegen -import com.wordnik.swagger.core._ +import com.wordnik.swagger.model._ object BasicObjcGenerator extends BasicObjcGenerator { def main(args: Array[String]) = generateClient(args) @@ -59,6 +59,8 @@ class BasicObjcGenerator extends BasicGenerator { override def importMapping = Map( "Date" -> "NIKDate") + override def toModelFilename(name: String) = "NIK" + name + // naming for the models override def toModelName(name: String) = { (typeMapping.values ++ @@ -135,8 +137,8 @@ class BasicObjcGenerator extends BasicGenerator { } } - override def toDeclaration(obj: DocumentationSchema) = { - var declaredType = toDeclaredType(obj.getType) + override def toDeclaration(obj: ModelProperty) = { + var declaredType = toDeclaredType(obj.`type`) declaredType match { case "Array" => { declaredType = "List" @@ -148,8 +150,15 @@ class BasicObjcGenerator extends BasicGenerator { declaredType match { case "List" => { val inner = { - if (obj.items.ref != null) obj.items.ref - else obj.items.getType + obj.items match { + case Some(items) => { + if(items.ref != null) + items.ref + else + items.`type` + } + case _ => throw new Exception("no inner type defined") + } } declaredType += "<" + inner + ">" "NSArray" @@ -161,8 +170,7 @@ class BasicObjcGenerator extends BasicGenerator { override def escapeReservedWord(word: String) = "_" + word - // default values - override def toDefaultValue(properCase: String, obj: DocumentationSchema) = { + override def toDefaultValue(properCase: String, obj: ModelProperty) = { properCase match { case "boolean" => "false" case "int" => "0" @@ -171,8 +179,15 @@ class BasicObjcGenerator extends BasicGenerator { case "double" => "0.0" case "List" => { val inner = { - if (obj.items.ref != null) obj.items.ref - else obj.items.getType + obj.items match { + case Some(items) => { + if(items.ref != null) + items.ref + else + items.`type` + } + case _ => throw new Exception("no inner type defined") + } } "new ArrayList<" + inner + ">" + "()" } diff --git a/src/main/scala/com/wordnik/swagger/codegen/BasicPHPGenerator.scala b/src/main/scala/com/wordnik/swagger/codegen/BasicPHPGenerator.scala index ab2e45589e09..ccf95f4f7707 100644 --- a/src/main/scala/com/wordnik/swagger/codegen/BasicPHPGenerator.scala +++ b/src/main/scala/com/wordnik/swagger/codegen/BasicPHPGenerator.scala @@ -16,7 +16,7 @@ package com.wordnik.swagger.codegen -import com.wordnik.swagger.core._ +import com.wordnik.swagger.model._ import java.io.File @@ -123,13 +123,11 @@ class BasicPHPGenerator extends BasicGenerator { } } - override def toDeclaration(obj: DocumentationSchema) = { - var declaredType = toDeclaredType(obj.getType) + override def toDeclaration(obj: ModelProperty) = { + var declaredType = toDeclaredType(obj.`type`) declaredType match { - case "Array" => { - declaredType = "array" - } + case "Array" => declaredType = "array" case e: String => e } @@ -137,8 +135,15 @@ class BasicPHPGenerator extends BasicGenerator { declaredType match { case "array" => { val inner = { - if (obj.items.ref != null) obj.items.ref - else toDeclaredType(obj.items.getType) + obj.items match { + case Some(items) => { + if(items.ref != null) + items.ref + else + items.`type` + } + case _ => throw new Exception("no inner type defined") + } } declaredType += "[" + inner + "]" "array" diff --git a/src/main/scala/com/wordnik/swagger/codegen/BasicPython3Generator.scala b/src/main/scala/com/wordnik/swagger/codegen/BasicPython3Generator.scala index 45da9a4c8a80..057d4d11da87 100644 --- a/src/main/scala/com/wordnik/swagger/codegen/BasicPython3Generator.scala +++ b/src/main/scala/com/wordnik/swagger/codegen/BasicPython3Generator.scala @@ -16,8 +16,6 @@ package com.wordnik.swagger.codegen -import com.wordnik.swagger.core._ - import java.io.File object BasicPython3Generator extends BasicPython3Generator { diff --git a/src/main/scala/com/wordnik/swagger/codegen/BasicPythonGenerator.scala b/src/main/scala/com/wordnik/swagger/codegen/BasicPythonGenerator.scala index 90de831c25f2..042d0ff13469 100644 --- a/src/main/scala/com/wordnik/swagger/codegen/BasicPythonGenerator.scala +++ b/src/main/scala/com/wordnik/swagger/codegen/BasicPythonGenerator.scala @@ -16,7 +16,7 @@ package com.wordnik.swagger.codegen -import com.wordnik.swagger.core._ +import com.wordnik.swagger.model._ import java.io.File @@ -41,7 +41,7 @@ class BasicPythonGenerator extends BasicGenerator { override def modelPackage = Some("models") // package for apis - override def apiPackage = Some("") + override def apiPackage = None // file suffix override def fileSuffix = ".py" @@ -106,7 +106,7 @@ class BasicPythonGenerator extends BasicGenerator { "Boolean" -> "bool", "Date" -> "datetime", "string" -> "str" - ) + ) override def toDeclaredType(dt: String): String = { val declaredType = typeMapping.getOrElse(dt, dt) @@ -118,28 +118,37 @@ class BasicPythonGenerator extends BasicGenerator { case false => "list[" + innerType + "]" } } - case _ => declaredType + case _ => { + declaredType + } } } - override def toDeclaration(obj: DocumentationSchema) = { - var declaredType = toDeclaredType(obj.getType) + override def toDeclaration(obj: ModelProperty) = { + var declaredType = toDeclaredType(obj.`type`) declaredType match { - case "Array" => { - declaredType = "list" + case "Array" => declaredType = "list" + case e: String => { + e } - case e: String => e } val defaultValue = toDefaultValue(declaredType, obj) declaredType match { case "list" => { val inner = { - if (obj.items.ref != null) obj.items.ref - else toDeclaredType(obj.items.getType) + obj.items match { + case Some(items) => { + if(items.ref != null) + items.ref + else + items.`type` + } + case _ => throw new Exception("no inner type defined") + } } - declaredType += "[" + inner + "]" + declaredType += "[" + toDeclaredType(inner) + "]" "list" } case _ => @@ -153,8 +162,8 @@ class BasicPythonGenerator extends BasicGenerator { // supporting classes override def supportingFiles = List( ("__init__.mustache", destinationDir, "__init__.py"), - ("swagger.mustache", destinationDir + File.separator + apiPackage.get, + ("swagger.mustache", destinationDir + File.separator + apiPackage.getOrElse(""), "swagger.py"), ("__init__.mustache", destinationDir + File.separator + - modelPackage.get, "__init__.py")) + modelPackage.getOrElse(""), "__init__.py")) } \ No newline at end of file diff --git a/src/main/scala/com/wordnik/swagger/codegen/BasicRubyGenerator.scala b/src/main/scala/com/wordnik/swagger/codegen/BasicRubyGenerator.scala index e58470ce1df4..ea8af283dadc 100644 --- a/src/main/scala/com/wordnik/swagger/codegen/BasicRubyGenerator.scala +++ b/src/main/scala/com/wordnik/swagger/codegen/BasicRubyGenerator.scala @@ -16,7 +16,7 @@ package com.wordnik.swagger.codegen -import com.wordnik.swagger.core._ +import com.wordnik.swagger.model._ import java.io.File @@ -58,6 +58,9 @@ class BasicRubyGenerator extends BasicGenerator { } } + override def toModelFilename(name: String) = name.toLowerCase + override def toApiFilename(name: String) = name.toLowerCase + "_api" + override def toVarName(name: String): String = toUnderscore(name) override def toMethodName(name: String): String = toUnderscore(name) @@ -71,8 +74,8 @@ class BasicRubyGenerator extends BasicGenerator { sb.toString } - override def toDeclaration(obj: DocumentationSchema) = { - var datatype = obj.getType.charAt(0).toUpperCase + obj.getType.substring(1) + override def toDeclaration(obj: ModelProperty) = { + var datatype = obj.`type`.charAt(0).toUpperCase + obj.`type`.substring(1) datatype match { case "Array" => datatype = "List" @@ -83,9 +86,15 @@ class BasicRubyGenerator extends BasicGenerator { datatype match { case "List" => { val inner = { - if (obj.items.ref != null) obj.items.ref - else obj.items.getType - } + obj.items match { + case Some(items) => { + if(items.ref != null) + items.ref + else + items.`type` + } + case _ => throw new Exception("no inner type defined") + } } datatype = "java.util.List[" + inner + "]" } case _ => diff --git a/src/main/scala/com/wordnik/swagger/codegen/BasicScalaGenerator.scala b/src/main/scala/com/wordnik/swagger/codegen/BasicScalaGenerator.scala index eca723e54337..bf8c2784ee0c 100644 --- a/src/main/scala/com/wordnik/swagger/codegen/BasicScalaGenerator.scala +++ b/src/main/scala/com/wordnik/swagger/codegen/BasicScalaGenerator.scala @@ -16,7 +16,7 @@ package com.wordnik.swagger.codegen -import com.wordnik.swagger.core._ +import com.wordnik.swagger.model._ object BasicScalaGenerator extends BasicScalaGenerator { def main(args: Array[String]) = generateClient(args) @@ -73,12 +73,19 @@ class BasicScalaGenerator extends BasicGenerator { } } - override def toDeclaration(obj: DocumentationSchema): (String, String) = { - obj.getType match { + override def toDeclaration(obj: ModelProperty): (String, String) = { + obj.`type` match { case "Array" => { val inner = { - if (obj.items.ref != null) obj.items.ref - else obj.items.getType + obj.items match { + case Some(items) => { + if(items.ref != null) + items.ref + else + items.`type` + } + case _ => throw new Exception("no inner type defined") + } } val e = "List[%s]" format toDeclaredType(inner) (e, toDefaultValue(inner, obj)) diff --git a/src/main/scala/com/wordnik/swagger/codegen/Codegen.scala b/src/main/scala/com/wordnik/swagger/codegen/Codegen.scala index 166b09c29d0b..262597f1cd5e 100644 --- a/src/main/scala/com/wordnik/swagger/codegen/Codegen.scala +++ b/src/main/scala/com/wordnik/swagger/codegen/Codegen.scala @@ -16,7 +16,7 @@ package com.wordnik.swagger.codegen -import com.wordnik.swagger.core._ +import com.wordnik.swagger.model._ import com.wordnik.swagger.codegen.util.{ CoreUtils, ScalaJsonUtil } import com.wordnik.swagger.codegen.language.CodegenConfig import com.wordnik.swagger.codegen.spec.SwaggerSpec._ @@ -26,11 +26,10 @@ import org.fusesource.scalate.layout.DefaultLayoutStrategy import org.fusesource.scalate.mustache._ import org.fusesource.scalate.support.ScalaCompiler -import java.io.File -import java.io.FileWriter -import java.io.InputStream +import java.io.{ File, FileWriter, InputStream } import org.apache.commons.io.FileUtils + import scala.io.Source import scala.collection.mutable.{ HashMap, ListBuffer, HashSet } import scala.collection.JavaConversions._ @@ -40,15 +39,16 @@ object Codegen { } class Codegen(config: CodegenConfig) { - val m = ScalaJsonUtil.getJsonMapper + val json = ScalaJsonUtil.getJsonMapper def generateSource(bundle: Map[String, AnyRef], templateFile: String): String = { val allImports = new HashSet[String] val includedModels = new HashSet[String] val modelList = new ListBuffer[Map[String, AnyRef]] val models = bundle("models") + models match { - case e: List[Tuple2[String, DocumentationSchema]] => { + case e: List[Tuple2[String, Model]] => { e.foreach(m => { includedModels += m._1 val modelMap = modelToMap(m._1, m._2) @@ -65,10 +65,9 @@ class Codegen(config: CodegenConfig) { val modelData = Map[String, AnyRef]("model" -> modelList.toList) val operationList = new ListBuffer[Map[String, AnyRef]] val classNameToOperationList = new HashMap[String, ListBuffer[AnyRef]] - val apis = bundle("apis") apis match { - case a: Map[String, List[(String, DocumentationOperation)]] => { + case a: Map[String, List[(String, Operation)]] => { a.map(op => { val classname = op._1 val ops = op._2 @@ -100,7 +99,10 @@ class Codegen(config: CodegenConfig) { includedModels.contains(model) match { case false => { config.importMapping.containsKey(model) match { - case true => imports += Map("import" -> config.importMapping(model)) + case true => { + if(!imports.flatten.map(m => m._2).toSet.contains(config.importMapping(model))) + imports += Map("import" -> config.importMapping(model)) + } case false => } } @@ -116,7 +118,11 @@ class Codegen(config: CodegenConfig) { case false => { config.importMapping.containsKey(model) match { case true => - case false => imports += Map("import" -> (importScope + model)) + case false => { + if(!imports.flatten.map(m => m._2).toSet.contains(importScope + model)){ + imports += Map("import" -> (importScope + model)) + } + } } } case true => // no need to add the model @@ -151,15 +157,13 @@ class Codegen(config: CodegenConfig) { var data = Map[String, AnyRef]( "name" -> bundle("name"), "package" -> bundle("package"), + "baseName" -> bundle.getOrElse("baseName", None), "className" -> bundle("className"), "invokerPackage" -> bundle("invokerPackage"), "imports" -> imports, "operations" -> f, "models" -> modelData, "basePath" -> bundle.getOrElse("basePath", "")) - - // println(m.writeValueAsString(modelData)) - var output = engine.layout(config.templateDir + File.separator + templateFile, template, data.toMap) // a shutdown method will be added to scalate in an upcoming release @@ -167,39 +171,19 @@ class Codegen(config: CodegenConfig) { output } - def extractImportsFromApi(operation: Tuple2[String, DocumentationOperation]) = { - val imports = new ListBuffer[AnyRef] - val modelNames = CoreUtils.extractModelNames(operation._2) - - modelNames.foreach(modelName => { - // apply mapings, packages for generated code - val qualifiedModel = (config.importMapping.contains(modelName) match { - case true => config.importMapping(modelName) - case false => { - config.modelPackage match { - case Some(p) => p + "." + config.toModelName(modelName) - case None => config.toModelName(modelName) - } - } - }) - imports += Map("import" -> qualifiedModel) - }) - imports.toSet - } - - def allowableValuesToString(v: DocumentationAllowableValues) = { + def allowableValuesToString(v: AllowableValues) = { v match { - case av: DocumentationAllowableListValues => { - av.getValueType + av.getValues.mkString("[", ",", "]") + case av: AllowableListValues => { + av.values.mkString("LIST[", ",", "]") } - case av: DocumentationAllowableRangeValues => { - av.getValueType + "[" + av.getMin() + "," + av.getMax() + "]" + case av: AllowableRangeValues => { + "RANGE[" + av.min + "," + av.max + "]" } case _ => "unsupported" } } - def apiToMap(path: String, op: DocumentationOperation): Map[String, AnyRef] = { + def apiToMap(path: String, operation: Operation): Map[String, AnyRef] = { var bodyParam: Option[String] = None var queryParams = new ListBuffer[AnyRef] val pathParams = new ListBuffer[AnyRef] @@ -207,8 +191,8 @@ class Codegen(config: CodegenConfig) { val bodyParams = new ListBuffer[AnyRef] var paramList = new ListBuffer[HashMap[String, AnyRef]] - if (op.getParameters != null) { - op.getParameters.foreach(param => { + if (operation.parameters != null) { + operation.parameters.foreach(param => { val params = new HashMap[String, AnyRef] params += (param.paramType + "Parameter") -> "true" params += "type" -> param.paramType @@ -220,7 +204,7 @@ class Codegen(config: CodegenConfig) { params += "allowMultiple" -> param.allowMultiple.toString param.allowableValues match { - case a: DocumentationAllowableValues => params += "allowableValues" -> allowableValuesToString(a) + case a: AllowableValues => params += "allowableValues" -> allowableValuesToString(a) case _ => } @@ -314,10 +298,10 @@ class Codegen(config: CodegenConfig) { val properties = HashMap[String, AnyRef]( "path" -> path, - "nickname" -> config.toMethodName(op.nickname), - "summary" -> op.summary, - "notes" -> op.notes, - "deprecated" -> op.deprecated, + "nickname" -> config.toMethodName(operation.nickname), + "summary" -> operation.summary, + "notes" -> operation.notes, + "deprecated" -> operation.`deprecated`, "bodyParam" -> bodyParam, "allParams" -> sp, "bodyParams" -> bodyParams.toList, @@ -325,12 +309,12 @@ class Codegen(config: CodegenConfig) { "queryParams" -> queryParams.toList, "headerParams" -> headerParams.toList, "requiredParams" -> requiredParams.toList, - "httpMethod" -> op.httpMethod.toUpperCase, - op.httpMethod.toLowerCase -> "true") + "httpMethod" -> operation.httpMethod.toUpperCase, + operation.httpMethod.toLowerCase -> "true") if (requiredParams.size > 0) properties += "requiredParamCount" -> requiredParams.size.toString - op.responseClass.indexOf("[") match { + operation.responseClass.indexOf("[") match { case -1 => { - val baseType = op.responseClass + val baseType = operation.responseClass properties += "returnType" -> config.processResponseDeclaration(baseType) properties += "returnBaseType" -> config.processResponseClass(baseType) properties += "returnSimpleType" -> "true" @@ -343,9 +327,10 @@ class Codegen(config: CodegenConfig) { } case n: Int => { val ComplexTypeMatcher = ".*\\[(.*)\\].*".r - val ComplexTypeMatcher(basePart) = op.responseClass - properties += "returnType" -> config.processResponseDeclaration(op.responseClass.replaceAll(basePart, config.processResponseClass(basePart).get)) - properties += "returnContainer" -> (op.responseClass.substring(0, n)) + val ComplexTypeMatcher(basePart) = operation.responseClass + + properties += "returnType" -> config.processResponseDeclaration(operation.responseClass.replaceAll(basePart, config.processResponseClass(basePart).get)) + properties += "returnContainer" -> (operation.responseClass.substring(0, n)) properties += "returnBaseType" -> config.processResponseClass(basePart) properties += "returnTypeIsPrimitive" -> { (config.languageSpecificPrimitives.contains(basePart) || primitives.contains(basePart)) match { @@ -358,7 +343,7 @@ class Codegen(config: CodegenConfig) { config.processApiMap(properties.toMap) } - def modelToMap(className: String, model: DocumentationSchema): Map[String, AnyRef] = { + def modelToMap(className: String, model: Model): Map[String, AnyRef] = { val data: HashMap[String, AnyRef] = HashMap( "classname" -> config.toModelName(className), @@ -371,18 +356,21 @@ class Codegen(config: CodegenConfig) { val imports = new HashSet[AnyRef] model.properties.map(prop => { val propertyDocSchema = prop._2 - val dt = propertyDocSchema.getType + val dt = propertyDocSchema.`type` var baseType = dt // import the object inside the container if (propertyDocSchema.items != null) { // import the container imports += Map("import" -> dt) - if (propertyDocSchema.items.ref != null) { - baseType = propertyDocSchema.items.ref - } - else if (propertyDocSchema.items.getType != null) { - baseType = propertyDocSchema.items.getType + propertyDocSchema.items match { + case Some(items) => { + if(items.ref != null) + baseType = items.ref + else if (items.`type` != null) + baseType = items.`type` + } + case _ => } } baseType = config.typeMapping.contains(baseType) match { @@ -397,10 +385,10 @@ class Codegen(config: CodegenConfig) { case _ => imports += Map("import" -> baseType) } - val isList = (if (isListType(propertyDocSchema.getType)) true else None) - val isMap = (if (isMapType(propertyDocSchema.getType)) true else None) - val isNotContainer = if (!isListType(propertyDocSchema.getType) && !isMapType(propertyDocSchema.getType)) true else None - val isContainer = if (isListType(propertyDocSchema.getType) || isMapType(propertyDocSchema.getType)) true else None + val isList = (if (isListType(propertyDocSchema.`type`)) true else None) + val isMap = (if (isMapType(propertyDocSchema.`type`)) true else None) + val isNotContainer = if (!isListType(propertyDocSchema.`type`) && !isMapType(propertyDocSchema.`type`)) true else None + val isContainer = if (isListType(propertyDocSchema.`type`) || isMapType(propertyDocSchema.`type`)) true else None val properties = HashMap( @@ -423,7 +411,7 @@ class Codegen(config: CodegenConfig) { "datatype" -> config.toDeclaration(propertyDocSchema)._1, "defaultValue" -> config.toDeclaration(propertyDocSchema)._2, "description" -> propertyDocSchema.description, - "notes" -> propertyDocSchema.notes, + "notes" -> propertyDocSchema.description, "required" -> propertyDocSchema.required.toString, "getter" -> config.toGetter(prop._1, config.toDeclaration(propertyDocSchema)._1), "setter" -> config.toSetter(prop._1, config.toDeclaration(propertyDocSchema)._1), @@ -458,7 +446,7 @@ class Codegen(config: CodegenConfig) { } } - def writeSupportingClasses(apis: Map[(String, String), List[(String, com.wordnik.swagger.core.DocumentationOperation)]], models: Map[String, DocumentationSchema]) = { + def writeSupportingClasses(apis: Map[(String, String), List[(String, Operation)]], models: Map[String, Model]) = { val rootDir = new java.io.File(".") val engine = new TemplateEngine(Some(rootDir)) @@ -476,11 +464,16 @@ class Codegen(config: CodegenConfig) { val modelList = new ListBuffer[HashMap[String, AnyRef]] + val nonScalaMapper = JsonUtil.getJsonMapper models.foreach(m => { + val str = json.writeValueAsString(m._2) + val jsonObj = nonScalaMapper.readValue(str, classOf[AnyRef]) + val modelJson = nonScalaMapper.writeValueAsString(jsonObj) + modelList += HashMap( "modelName" -> m._1, "model" -> m._2, - "modelJson" -> ScalaJsonUtil.getJsonMapper.writeValueAsString(m._2), + "modelJson" -> modelJson, "hasMore" -> "true") }) modelList.size match { @@ -523,7 +516,6 @@ class Codegen(config: CodegenConfig) { println("wrote " + outputFilename) } else { val is = getInputStream(config.templateDir + File.separator + srcTemplate) - println("copying file " + config.templateDir + File.separator + srcTemplate + " to " + outputFilename) val outputFile = new File(outputFilename) val parentDir = new File(outputFile.getParent) if (parentDir != null && !parentDir.exists) { @@ -557,3 +549,23 @@ class Codegen(config: CodegenConfig) { } } } + +import com.fasterxml.jackson.core.JsonGenerator.Feature +import com.fasterxml.jackson.databind._ +import com.fasterxml.jackson.annotation._ +import com.fasterxml.jackson.databind.annotation.JsonSerialize + + +object JsonUtil { + def getJsonMapper = { + val mapper = new ObjectMapper() + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + mapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT) + mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) + mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) + mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY) + mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false) + mapper.configure(SerializationFeature.INDENT_OUTPUT, true) + mapper + } +} \ No newline at end of file diff --git a/src/main/scala/com/wordnik/swagger/codegen/PathUtil.scala b/src/main/scala/com/wordnik/swagger/codegen/PathUtil.scala index bf3e217c79d5..a7224b7a5635 100644 --- a/src/main/scala/com/wordnik/swagger/codegen/PathUtil.scala +++ b/src/main/scala/com/wordnik/swagger/codegen/PathUtil.scala @@ -16,10 +16,6 @@ package com.wordnik.swagger.codegen -/** - * @since 7/6/12 9:06 AM - * - */ trait PathUtil { def getResourcePath(host: String) = { System.getProperty("fileMap") match { diff --git a/src/main/scala/com/wordnik/swagger/codegen/language/CodegenConfig.scala b/src/main/scala/com/wordnik/swagger/codegen/language/CodegenConfig.scala index 019b3cb806f1..89a9aced50a1 100644 --- a/src/main/scala/com/wordnik/swagger/codegen/language/CodegenConfig.scala +++ b/src/main/scala/com/wordnik/swagger/codegen/language/CodegenConfig.scala @@ -16,7 +16,7 @@ package com.wordnik.swagger.codegen.language -import com.wordnik.swagger.core._ +import com.wordnik.swagger.model._ import scala.collection.mutable.{ HashMap, HashSet } @@ -29,6 +29,8 @@ abstract class CodegenConfig { def destinationDir: String def toModelName(name: String): String def toApiName(name: String): String + def toModelFilename(name: String) = name + def toApiFilename(name: String) = toApiName(name) def apiNameFromPath(apiPath: String): String def processApiMap(m: Map[String, AnyRef]): Map[String, AnyRef] = m def processModelMap(m: Map[String, AnyRef]): Map[String, AnyRef] = m @@ -61,9 +63,12 @@ abstract class CodegenConfig { def toMethodName(name: String): String = name // override if you want to do something special on processing - def processOperation(apiPath: String, op: DocumentationOperation) = {} + // def processOperation(apiPath: String, op: DocumentationOperation) = {} + def processOperation(apiPath: String, op: Operation) = {} + def processResponseClass(responseClass: String): Option[String] = Some(responseClass) + def processApiOperation(apiPath: String, op: Operation) = {} def processResponseDeclaration(responseClass: String): Option[String] = { responseClass match { case "void" => None @@ -74,9 +79,9 @@ abstract class CodegenConfig { def supportingFiles = List(): List[(String, String, String)] // mapping for datatypes - def toDeclaration(obj: DocumentationSchema) = { - var declaredType = toDeclaredType(obj.getType) - val defaultValue = toDefaultValue(declaredType, obj) + def toDeclaration(property: ModelProperty) = { + var declaredType = toDeclaredType(property.`type`) + val defaultValue = toDefaultValue(declaredType, property) (declaredType, defaultValue) } @@ -123,7 +128,7 @@ abstract class CodegenConfig { } else None } - def toDefaultValue(dataType: String, obj: DocumentationSchema) = { + def toDefaultValue(dataType: String, obj: ModelProperty) = { dataType match { case "int" => "0" case "long" => "0L" @@ -131,8 +136,15 @@ abstract class CodegenConfig { case "double" => "0.0" case e: String if (Set("List").contains(e)) => { val inner = { - if (obj.items.ref != null) obj.items.ref - else obj.items.getType + obj.items match { + case Some(items) => { + if(items.ref != null) + items.ref + else + items.`type` + } + case _ => throw new Exception("no inner type defined") + } } "new java.util.ArrayList[" + toDeclaredType(inner) + "]" + "()" } diff --git a/src/main/scala/com/wordnik/swagger/codegen/spec/SwaggerSpecValidator.scala b/src/main/scala/com/wordnik/swagger/codegen/spec/SwaggerSpecValidator.scala index ddc754e5c1d0..0bdb3cb18696 100644 --- a/src/main/scala/com/wordnik/swagger/codegen/spec/SwaggerSpecValidator.scala +++ b/src/main/scala/com/wordnik/swagger/codegen/spec/SwaggerSpecValidator.scala @@ -16,7 +16,8 @@ package com.wordnik.swagger.codegen.spec -import com.wordnik.swagger.core._ +import com.wordnik.swagger.model._ +import com.wordnik.swagger.codegen.util.ScalaJsonUtil import com.wordnik.swagger.codegen.PathUtil import com.wordnik.swagger.codegen.spec.SwaggerSpec._ import com.wordnik.swagger.codegen.util.CoreUtils @@ -33,10 +34,14 @@ import scala.io.Source import scala.collection.mutable.HashSet import scala.collection.immutable.HashMap -class SwaggerSpecValidator(private val doc: Documentation, - private val subDocs: List[Documentation], +class SwaggerSpecValidator(private val doc: ResourceListing, + private val apis: List[ApiListing], private val fix: Boolean = true) extends PathUtil { + + import ValidationMessage._ + val json = ScalaJsonUtil.getJsonMapper + private val validationMessages = ListBuffer.empty[ValidationMessage] private val LOGGER = Logger.getLogger(classOf[SwaggerSpecValidator].getName) @@ -44,42 +49,41 @@ class SwaggerSpecValidator(private val doc: Documentation, def validate() { checkRootProperties() - subDocs.foreach(subDoc => { - fixSubDoc(subDoc) + apis.foreach(api => { + fixSubDoc(api) - if (subDoc.getModels != null) { - fixReturnModels(subDoc.getModels.toMap, subDoc) - fixInputDataTypes(subDoc.getModels.toMap, subDoc.getApis.toList) - fixModels(subDoc.getModels.toMap) + if (api.models != null) { + fixReturnModels(api.models.toMap, apis) + fixInputDataTypes(api.models.toMap, apis) + fixModels(api.models.toMap) } }) - validateResponseModels(subDocs) + validateResponseModels(apis) println("----------") println(this) } - def validateResponseModels(subDocs: List[Documentation]) = { - val validModelNames = CoreUtils.extractAllModels(subDocs).map(m => m._1).toSet + def validateResponseModels(subDocs: List[ApiListing]) = { + val validModelNames = CoreUtils.extractAllModels2(subDocs).map(m => m._1).toSet val requiredModels = new HashSet[String] subDocs.foreach(subDoc => { - if (subDoc.getApis != null) { - subDoc.getApis.foreach(api => { - api.getOperations.foreach(op => { + if (subDoc.apis != null) { + subDoc.apis.foreach(api => { + api.operations.foreach(op => { requiredModels += { val responseClass = op.responseClass responseClass.indexOf("[") match { - case i: Int if (i > 0) => { + case i: Int if (i > 0) => { CoreUtils.extractBasePartFromType(responseClass) - } - case _ => responseClass + } + case _ => responseClass } } }) - }) + }) } }) - val missingModels = requiredModels.toSet -- (validModelNames ++ primitives) if (missingModels.size > 0) println("missing models: " + missingModels) @@ -130,20 +134,19 @@ class SwaggerSpecValidator(private val doc: Documentation, case e: String => println("api version: " + e) case _ => !!(doc, RESOURCE_LISTING, "Properties", "Missing api version", WARNING) } - } /** * this is here because sub documents don't have the same resourcePath as declared in * the main resource listing */ - private def fixSubDoc(subDoc: Documentation) = { - if (subDoc.resourcePath.indexOf(".{format}") == -1) { - doc.getApis.foreach(api => { - if (api.path.indexOf(".{format}") > 0 && api.path.replaceAll(".\\{format\\}", "") == subDoc.resourcePath) { - // println("--> added subdoc format string to " + subDoc.resourcePath) - // !!(subDoc, RESOURCE, "Path " + subDoc.resourcePath, format("Must be %s.{format}", subDoc.resourcePath), WARNING) - if (fix) subDoc.resourcePath = subDoc.resourcePath + ".{format}" + private def fixSubDoc(api: ApiListing) = { + if (api.resourcePath.indexOf(".{format}") == -1) { + doc.apis.foreach(op => { + if (op.path.indexOf(".{format}") > 0 && op.path.replaceAll(".\\{format\\}", "") == api.resourcePath) { + if (fix) { + api.resourcePath = api.resourcePath + ".{format}" + } } }) } @@ -152,7 +155,7 @@ class SwaggerSpecValidator(private val doc: Documentation, /** * this is here because models don't have the proper references to types */ - private def fixModels(models: Map[String, DocumentationSchema]) = { + private def fixModels(models: Map[String, Model]) = { val validModelNames = models.map(_._1).toSet LOGGER.finest("all valid models: " + validModelNames) for ((name, model) <- models) { @@ -175,59 +178,75 @@ class SwaggerSpecValidator(private val doc: Documentation, val subObjectName = prop._1 val subObject = prop._2 - if (containers.contains(subObject.getType)) { + if (containers.contains(subObject.`type`)) { // process the sub object - if (subObject.items != null && subObject.items.ref != null) { - getUpdatedType(validModelNames, subObject.items.ref) match { - case Some(updatedType) => { - if (!subObject.items.ref.equals(updatedType)) { - !!(model, MODEL_PROPERTY, format("%s->%s: %s", model.id, subObjectName, subObject.getType), format("Invalid ref (%s). Best guess: %s", subObject.items.ref, updatedType)) - LOGGER.finest("updated subObject.items.ref " + subObject.items.ref + " to " + updatedType) - if (fix) subObject.items.ref = updatedType + subObject.items match { + case Some(item) => { + getUpdatedType(validModelNames, item.ref) match { + case Some(updatedType) => { + if (!item.ref.equals(updatedType)) { + !!(model, MODEL_PROPERTY, format("%s->%s: %s", model.id, subObjectName, subObject.`type`), format("Invalid ref (%s). Best guess: %s", item.ref, updatedType)) + LOGGER.finest("updated subObject.items.ref " + item.ref + " to " + updatedType) + if (fix) { + subObject.items = Some(ModelRef(ref = updatedType)) + } + } + } + case None => + } + } + case _ => + } + } else if (containers.contains(subObject.`type`)) { + // process the sub object + if (subObject.items != null && subObject.items != None && subObject.items.get.ref != null){ + subObject.items match { + case Some(item) => { + getUpdatedType(validModelNames, item.ref) match { + case Some(updatedType) => { + if (!item.ref.equals(updatedType)) { + !!(model, MODEL_PROPERTY, format("%s->%s: %s", model.id, subObjectName, subObject.`type`), format("Invalid ref (%s). Best guess: %s", item.ref, updatedType)) + LOGGER.finest("updated subObject.items.ref " + item.ref + " to " + updatedType) + if (fix) subObject.items = Some(ModelRef(ref = updatedType)) + } + } + case None => { + !!(model, MODEL_PROPERTY, format("%s->%s: %s", model.id, subObjectName, subObject.`type`), format("Invalid ref (%s).", item.ref)) + LOGGER.finest("didn't know what to do with " + item.ref) + } } } - case None => + case _ => } } - } else if (containers.contains(subObject.getType)) { - // process the sub object - if (subObject.items != null && subObject.items.ref != null) { - getUpdatedType(validModelNames, subObject.items.ref) match { - case Some(updatedType) => { - if (!subObject.items.ref.equals(updatedType)) { - !!(model, MODEL_PROPERTY, format("%s->%s: %s", model.id, subObjectName, subObject.getType), format("Invalid ref (%s). Best guess: %s", subObject.items.ref, updatedType)) - LOGGER.finest("updated subObject.items.ref " + subObject.items.ref + " to " + updatedType) - if (fix) subObject.items.ref = updatedType + else if (subObject.items != null && subObject.items != None && subObject.items.get.`type` != null) { + subObject.items match { + case Some(item) => { + getUpdatedType(validModelNames, item.`type`) match { + case Some(updatedType) => { + if (!item.`type`.equals(updatedType)) { + !!(model, MODEL_PROPERTY, format("%s->%s: %s", model.id, subObjectName, subObject.`type`), format("Invalid type (%s). Best guess: %s", item.`type`, updatedType)) + LOGGER.finest("updated subObject.items.type" + item.`type` + " to " + updatedType) + if (fix) subObject.items = Some(ModelRef(`type` = updatedType)) + } + } + case None => { + println("nothing found for " + json.writeValueAsString(subObject)) + !!(model, MODEL_PROPERTY, format("%s->%s: %s", model.id, subObjectName, subObject.`type`), format("Invalid ref (%s).", item.ref)) + LOGGER.finest("didn't know what to do with " + item.ref) + } } } - case None => { - !!(model, MODEL_PROPERTY, format("%s->%s: %s", model.id, subObjectName, subObject.getType), format("Invalid ref (%s).", subObject.items.ref)) - LOGGER.finest("didn't know what to do with " + subObject.items.ref) - } - } - } else if (subObject.items != null && subObject.items.getType != null) { - getUpdatedType(validModelNames, subObject.items.getType) match { - case Some(updatedType) => { - if (!subObject.items.getType.equals(updatedType)) { - !!(model, MODEL_PROPERTY, format("%s->%s: %s", model.id, subObjectName, subObject.getType), format("Invalid type (%s). Best guess: %s", subObject.items.getType, updatedType)) - LOGGER.finest("updated subObject.items.type" + subObject.items.getType + " to " + updatedType) - if (fix) subObject.items.setType(updatedType) - } - } - case None => { - println("nothing found for " + com.wordnik.swagger.core.util.JsonUtil.getJsonMapper.writeValueAsString(subObject)) - !!(model, MODEL_PROPERTY, format("%s->%s: %s", model.id, subObjectName, subObject.getType), format("Invalid ref (%s).", subObject.items.ref)) - LOGGER.finest("didn't know what to do with " + subObject.items.ref) - } + case _ => } } } else { - getUpdatedType(validModelNames, subObject.getType) match { + getUpdatedType(validModelNames, subObject.`type`) match { case Some(updatedType) => { - if (!subObject.getType.equals(updatedType)) { - !!(model, MODEL_PROPERTY, format("%s->%s: %s", model.id, subObjectName, subObject.getType), format("Invalid type (%s). Best guess: %s", subObject.getType, updatedType)) - LOGGER.finest("updated subObject.getType " + subObject.getType + " to " + updatedType) - if (fix) subObject.setType(updatedType) + if (!subObject.`type`.equals(updatedType)) { + !!(model, MODEL_PROPERTY, format("%s->%s: %s", model.id, subObjectName, subObject.`type`), format("Invalid type (%s). Best guess: %s", subObject.`type`, updatedType)) + LOGGER.finest("updated subObject.getType " + subObject.`type` + " to " + updatedType) + if (fix) subObject.`type` = updatedType } } case None => @@ -249,53 +268,60 @@ class SwaggerSpecValidator(private val doc: Documentation, /** * this is here because input params in operations don't match primitives or model names */ - private def fixInputDataTypes(models: Map[String, DocumentationSchema], apis: List[DocumentationEndPoint]) = { + private def fixInputDataTypes(models: Map[String, Model], a: List[ApiListing]) = { val validModelNames = models.map(m => m._1).toSet - apis.foreach(api => { - if (api.getOperations != null) { - api.getOperations.foreach(op => { - if (op.getParameters != null) { - val modelNames = new ListBuffer[String] - op.getParameters.foreach(p => { - val dataType = p.dataType - p.paramType match { - case "body" => { - getUpdatedType(validModelNames, dataType) match { - case Some(updatedName) => { - if (!p.dataType.equals(updatedName)) { + // List[ApiListing] + a.foreach(listing => { + if (listing.apis != null) { + listing.apis.foreach(api => { + // List[ApiDescription] + api.operations.foreach(op => { + // List[Operation] + val modelNames = new ListBuffer[String] + if(op.parameters != null) { + op.parameters.foreach(p => { + val dataType = p.dataType + + p.paramType match { + case "body" => { + getUpdatedType(validModelNames, dataType) match { + case Some(updatedName) => { + if (!p.dataType.equals(updatedName)) { + // LOGGER.finest("--> updated " + dataType + " to " + updatedName) + !!(p, OPERATION_PARAM, format("%s.%s(body: %s)", apiNameFromPath(api.path), op.nickname, p.dataType), format("Invalid data type %s. Best guess: %s", p.dataType, updatedName)) + if (fix) p.dataType = updatedName + } + } + case _ => LOGGER.finest("rats!") // leave it alone + } + } + case "path" => { + getUpdatedType(validModelNames, dataType) match { + case Some(updatedName) => { // LOGGER.finest("--> updated " + dataType + " to " + updatedName) - !!(p, OPERATION_PARAM, format("%s.%s(body: %s)", apiNameFromPath(api.getPath()), op.nickname, p.dataType), format("Invalid data type %s. Best guess: %s", p.dataType, updatedName)) + !!(p, OPERATION_PARAM, format("%s.%s(path_%s: %s)", apiNameFromPath(api.path), op.nickname, p.name, p.dataType), format("Invalid data type %s. Best guess: %s", p.dataType, updatedName)) if (fix) p.dataType = updatedName } + case _ => // leave it alone } - case _ => LOGGER.finest("rats!") // leave it alone } - } - case "path" => { - getUpdatedType(validModelNames, dataType) match { - case Some(updatedName) => { - // LOGGER.finest("--> updated " + dataType + " to " + updatedName) - !!(p, OPERATION_PARAM, format("%s.%s(path_%s: %s)", apiNameFromPath(api.getPath()), op.nickname, p.name, p.dataType), format("Invalid data type %s. Best guess: %s", p.dataType, updatedName)) - if (fix) p.dataType = updatedName + case "query" => { + getUpdatedType(validModelNames, dataType) match { + case Some(updatedName) => { + // LOGGER.finest("--> updated " + dataType + " to " + updatedName) + !!(p, OPERATION_PARAM, format("%s.%s(query_%s: %s)", apiNameFromPath(api.path), op.nickname, p.name, p.dataType), format("Invalid %s. Best guess: %s", p.dataType, updatedName)) + if (fix) p.dataType = updatedName + } + case _ => // leave it alone } - case _ => // leave it alone } + case _ => } - case "query" => { - getUpdatedType(validModelNames, dataType) match { - case Some(updatedName) => { - // LOGGER.finest("--> updated " + dataType + " to " + updatedName) - !!(p, OPERATION_PARAM, format("%s.%s(query_%s: %s)", apiNameFromPath(api.getPath()), op.nickname, p.name, p.dataType), format("Invalid %s. Best guess: %s", p.dataType, updatedName)) - if (fix) p.dataType = updatedName - } - case _ => // leave it alone - } - } - case _ => - } - }) - } + + }) + } + }) }) } }) @@ -304,21 +330,23 @@ class SwaggerSpecValidator(private val doc: Documentation, /** * this is here because the return types are inconsistent from the swagger-core-1.02-SNAPSHOT */ - private def fixReturnModels(models: Map[String, DocumentationSchema], doc: Documentation) = { + private def fixReturnModels(models: Map[String, Model], a: List[ApiListing]) = { val validModelNames = models.map(m => m._1).toSet - if (doc.getApis != null) { - doc.getApis.foreach(api => { - if (api.getOperations != null) { - api.getOperations.foreach(op => { - // check return type + // List[ApiListing] + a.foreach(listing => { + if (listing.apis != null) { + listing.apis.foreach(api => { + // List[ApiDescription] + api.operations.foreach(op => { + // List[Operation] val responseClass = op.responseClass if (responseClass != null) { getUpdatedType(validModelNames, responseClass) match { case Some(updatedName) => { if (!responseClass.equals(updatedName)) { LOGGER.finest("--> updated " + responseClass + " to " + updatedName) - !!(op, OPERATION, format("%s.%s(): %s", apiNameFromPath(api.getPath()), op.nickname, op.responseClass), format("Invalid response class. Best guess: %s", updatedName)) + !!(op, OPERATION, format("%s.%s(): %s", apiNameFromPath(api.path), op.nickname, op.responseClass), format("Invalid response class. Best guess: %s", updatedName)) if (fix) op.responseClass = updatedName } } @@ -327,12 +355,14 @@ class SwaggerSpecValidator(private val doc: Documentation, } } }) - } - }) - } + }) + } + }) } private def getUpdatedType(validModelNames: Set[String], name: String): Option[String] = { + if(name == null) return None + if (validModelNames.contains(name)) { Some(name) } else if (name.indexOf("[") > 0) { diff --git a/src/main/scala/com/wordnik/swagger/codegen/spec/Validator.scala b/src/main/scala/com/wordnik/swagger/codegen/spec/Validator.scala index c1ee774e403f..907db4311788 100644 --- a/src/main/scala/com/wordnik/swagger/codegen/spec/Validator.scala +++ b/src/main/scala/com/wordnik/swagger/codegen/spec/Validator.scala @@ -17,9 +17,8 @@ package com.wordnik.swagger.codegen.spec import com.wordnik.swagger.codegen.util.{CoreUtils, ApiExtractor, ResourceExtractor} -import com.wordnik.swagger.core.Documentation import com.wordnik.swagger.codegen.PathUtil -import com.wordnik.swagger.core.util.JsonUtil + import scala.collection.JavaConversions._ object Validator extends PathUtil { @@ -39,18 +38,16 @@ object Validator extends PathUtil { } val doc = { try { - val json = ResourceExtractor.extractListing(getResourcePath(host), apiKey) - - JsonUtil.getJsonMapper.readValue(json, classOf[Documentation]) + ResourceExtractor.fetchListing(getResourcePath(host), apiKey) } catch { case e: Exception => throw new Exception("unable to read from " + host, e) } } val basePath = getBasePath(doc.basePath) - val subDocs = ApiExtractor.extractApiDocs(basePath, doc.getApis().toList, apiKey) + val apis = ApiExtractor.fetchApiListings(basePath, doc.apis, apiKey) - val swaggerSpecValidator = new SwaggerSpecValidator(doc, subDocs, false) + val swaggerSpecValidator = new SwaggerSpecValidator(doc, apis, false) swaggerSpecValidator.validate() swaggerSpecValidator.generateReport(host, outputFilename) diff --git a/src/main/scala/com/wordnik/swagger/codegen/util/ApiExtractor.scala b/src/main/scala/com/wordnik/swagger/codegen/util/ApiExtractor.scala index 617ccb8a8297..72384a63e6cc 100644 --- a/src/main/scala/com/wordnik/swagger/codegen/util/ApiExtractor.scala +++ b/src/main/scala/com/wordnik/swagger/codegen/util/ApiExtractor.scala @@ -16,39 +16,41 @@ package com.wordnik.swagger.codegen.util -import com.wordnik.swagger.core.util.JsonUtil - -import com.wordnik.swagger.core._ +import com.wordnik.swagger.model._ import scala.io._ -import scala.collection.JavaConversions._ import scala.collection.mutable.{ ListBuffer, HashMap, HashSet } -import Source._ object ApiExtractor { - def m = JsonUtil.getJsonMapper + def json = ScalaJsonUtil.getJsonMapper - def extractApiDocs(basePath: String, apis: List[DocumentationEndPoint], apiKey: Option[String] = None): List[Documentation] = { + def fetchApiListings(basePath: String, apis: List[ApiListingReference], apiKey: Option[String] = None): List[ApiListing] = { for (api <- apis) yield { - val json = basePath.startsWith("http") match { + val str = basePath.startsWith("http") match { case true => { println("calling: " + ((basePath + api.path + apiKey.getOrElse("")).replaceAll(".\\{format\\}", ".json"))) Source.fromURL((basePath + api.path + apiKey.getOrElse("")).replaceAll(".\\{format\\}", ".json")).mkString } case false => Source.fromFile((basePath + api.path).replaceAll(".\\{format\\}", ".json")).mkString } - val out = m.readValue(json, classOf[Documentation]) - out + json.readValue(str, classOf[ApiListing]) } } - def extractOperations(basePath: String, api: DocumentationEndPoint): List[(String, DocumentationOperation)] = { - (for(op <- api.getOperations.toList) yield (api.path, op)).toList + def extractApiOperations(basePath: String, references: List[ApiListingReference], apiKey: Option[String] = None) = { + for (api <- references) yield { + val str = basePath.startsWith("http") match { + case true => { + println("calling: " + ((basePath + api.path + apiKey.getOrElse("")).replaceAll(".\\{format\\}", ".json"))) + Source.fromURL((basePath + api.path + apiKey.getOrElse("")).replaceAll(".\\{format\\}", ".json")).mkString + } + case false => Source.fromFile((basePath + api.path).replaceAll(".\\{format\\}", ".json")).mkString + } + json.readValue(str, classOf[ApiListing]) + } } - def getOperations(path: String, op: List[DocumentationOperation]): Map[String, DocumentationOperation] = { - val opMap = new HashMap[String, DocumentationOperation] - op.foreach(operation => opMap += path -> operation) - opMap.toMap + def extractApiOperations(basePath: String, apiDescription: ApiDescription): List[(String, Operation)] = { + (for(op <- apiDescription.operations) yield (apiDescription.path, op)).toList } } \ No newline at end of file diff --git a/src/main/scala/com/wordnik/swagger/codegen/util/CoreUtils.scala b/src/main/scala/com/wordnik/swagger/codegen/util/CoreUtils.scala index 5986418274c1..668d45eeb06f 100644 --- a/src/main/scala/com/wordnik/swagger/codegen/util/CoreUtils.scala +++ b/src/main/scala/com/wordnik/swagger/codegen/util/CoreUtils.scala @@ -16,8 +16,7 @@ package com.wordnik.swagger.codegen.util -import com.wordnik.swagger.core._ -import com.wordnik.swagger.core.util.JsonUtil +import com.wordnik.swagger.model._ import scala.collection.mutable.{ HashSet, ListBuffer, HashMap } import scala.collection.JavaConversions._ @@ -26,23 +25,21 @@ import com.wordnik.swagger.codegen.spec.SwaggerSpec._ import scala.io.Source object CoreUtils { - def m = JsonUtil.getJsonMapper - - def extractAllModels(docs: List[Documentation]): Map[String, DocumentationSchema] = { - val modelObjects = new HashMap[String, DocumentationSchema] - docs.foreach(sd => { - for ((nm, model) <- extractModels(sd)) modelObjects += nm -> model - if (sd.getModels != null) sd.getModels.foreach(sm => modelObjects += sm._1 -> sm._2) + def extractAllModels2(apis: List[ApiListing]): Map[String, Model] = { + val modelObjects = new HashMap[String, Model] + apis.foreach(api => { + for ((nm, model) <- extractApiModels(api)) modelObjects += nm -> model + if (api.models != null) api.models.foreach(model => modelObjects += model._1 -> model._2) }) modelObjects.toMap } - def extractModelNames(op: DocumentationOperation): Set[String] = { + def extractModelNames(op: Operation): Set[String] = { val modelNames = new HashSet[String] modelNames += op.responseClass // POST, PUT, DELETE body - if (op.getParameters != null) { - op.getParameters.filter(p => p.paramType == "body") + if (op.parameters != null) { + op.parameters.filter(p => p.paramType == "body") .foreach(p => modelNames += p.dataType) } val baseNames = (for (modelName <- (modelNames.toList)) @@ -50,13 +47,13 @@ object CoreUtils { baseNames.toSet } - def extractModelNames(modelObjects: Map[String, DocumentationSchema], ep: DocumentationOperation): Set[String] = { + def extractModelNames2(modelObjects: Map[String, Model], ep: Operation): Set[String] = { val modelNames = new HashSet[String] modelNames += ep.responseClass // POST, PUT, DELETE body - if (ep.getParameters != null) - ep.getParameters.filter(p => p.paramType == "body") + if (ep.parameters != null) + ep.parameters.filter(p => p.paramType == "body") .foreach(p => modelNames += p.dataType) val baseNames = (for (modelName <- (modelNames.toList)) @@ -72,12 +69,18 @@ object CoreUtils { subNames += model._1 model._2.properties.foreach(prop => { val subObject = prop._2 - if (containers.contains(subObject.getType)) { - if (subObject.items.ref != null) - subNames += subObject.items.ref - else - subNames += subObject.items.getType - } else subNames += subObject.getType + if (containers.contains(subObject.`type`)) { + subObject.items match { + case Some(item) => { + if(item.ref != null) + subNames += item.ref + else + subNames += item.`type` + } + case None => + } + } + else subNames += subObject.`type` }) }) subNames.toSet @@ -91,24 +94,22 @@ object CoreUtils { } } - def extractModels(sd: Documentation): Map[String, DocumentationSchema] = { + def extractApiModels(sd: ApiListing): Map[String, Model] = { val modelNames = new HashSet[String] - val modelObjects = new HashMap[String, DocumentationSchema] + val modelObjects = new HashMap[String, Model] // return types - if (sd.getApis != null) { - sd.getApis.foreach(api => { - if (api.getOperations != null) - api.getOperations.foreach(op => { - modelNames += op.responseClass - // POST, PUT, DELETE body - if (op.getParameters != null) - op.getParameters.filter(p => p.paramType == "body") - .foreach(p => modelNames += p.dataType) - }) + sd.apis.foreach(api => { + if (api.operations != null) + api.operations.foreach(op => { + modelNames += op.responseClass + // POST, PUT, DELETE body + if(op.parameters != null) + op.parameters.filter(p => p.paramType == "body") + .foreach(p => modelNames += p.dataType) }) - } - if (sd.getModels != null) - for ((name, m) <- sd.getModels) modelObjects += name -> m + }) + for ((name, m) <- sd.models) + modelObjects += name -> m // extract all base model names, strip away Containers like List[] and primitives val baseNames = (for (modelName <- (modelNames.toList -- primitives)) @@ -122,15 +123,18 @@ object CoreUtils { requiredModels.map(model => { model._2.properties.foreach(prop => { val subObject = prop._2 - if (containers.contains(subObject.getType)) { - if (subObject.items != null) { - if (subObject.items.ref != null) { - subNames += subObject.items.ref - } else { - subNames += subObject.items.getType + if (containers.contains(subObject.`type`)) { + subObject.items match { + case Some(subItem) => { + if (subItem.ref != null) { + subNames += subItem.ref + } else { + subNames += subItem.`type` + } } + case _ => } - } else subNames += subObject.getType + } else subNames += subObject.`type` }) }) @@ -138,15 +142,18 @@ object CoreUtils { modelObjects.filter(obj => subNames.contains(obj._1)).foreach(model => { model._2.properties.foreach(prop => { val subObject = prop._2 - if (containers.contains(subObject.getType)) { - if (subObject.items != null) { - if (subObject.items.ref != null) { - subNames += subObject.items.ref - } else { - subNames += subObject.items.getType + if (containers.contains(subObject.`type`)) { + subObject.items match { + case Some(subItem) => { + if (subItem.ref != null) { + subNames += subItem.ref + } else { + subNames += subItem.`type` + } } + case _ => } - } else subNames += subObject.getType + } else subNames += subObject.`type` }) }) val subModels = modelObjects.filter(obj => subNames.contains(obj._1)) diff --git a/src/main/scala/com/wordnik/swagger/codegen/util/ResourceExtractor.scala b/src/main/scala/com/wordnik/swagger/codegen/util/ResourceExtractor.scala index 501b7af5910a..c2d5d5f42088 100644 --- a/src/main/scala/com/wordnik/swagger/codegen/util/ResourceExtractor.scala +++ b/src/main/scala/com/wordnik/swagger/codegen/util/ResourceExtractor.scala @@ -16,13 +16,18 @@ package com.wordnik.swagger.codegen.util +import com.wordnik.swagger.model._ + import scala.io.Source object ResourceExtractor { - def extractListing(path: String, apiKey: Option[String] = None) = { - path.startsWith("http") match { - case true => Source.fromURL(path + apiKey.getOrElse("")).mkString - case false => Source.fromFile(path).mkString + def json = ScalaJsonUtil.getJsonMapper + + def fetchListing(path: String, apiKey: Option[String] = None): ResourceListing = { + val str = path.startsWith("http") match { + case true => Source.fromURL(path + apiKey.getOrElse("")).mkString + case false => Source.fromFile(path).mkString + } + json.readValue(str, classOf[ResourceListing]) } - } } \ No newline at end of file diff --git a/src/main/scala/com/wordnik/swagger/codegen/util/ScalaJsonUtil.scala b/src/main/scala/com/wordnik/swagger/codegen/util/ScalaJsonUtil.scala index 7d8b080832ef..a26b7971b483 100644 --- a/src/main/scala/com/wordnik/swagger/codegen/util/ScalaJsonUtil.scala +++ b/src/main/scala/com/wordnik/swagger/codegen/util/ScalaJsonUtil.scala @@ -31,6 +31,8 @@ object ScalaJsonUtil { mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY) + mapper.configure(SerializationFeature.WRITE_NULL_MAP_VALUES, false) + mapper.configure(SerializationFeature.INDENT_OUTPUT, true) mapper } } \ No newline at end of file diff --git a/src/main/scala/com/wordnik/swagger/model/AllowableValues.java b/src/main/scala/com/wordnik/swagger/model/AllowableValues.java new file mode 100644 index 000000000000..9abd156127a2 --- /dev/null +++ b/src/main/scala/com/wordnik/swagger/model/AllowableValues.java @@ -0,0 +1,34 @@ +/** + * Copyright 2012 Wordnik, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.wordnik.swagger.model; + +import javax.xml.bind.annotation.*; + +import com.fasterxml.jackson.annotation.*; + +import static com.fasterxml.jackson.annotation.JsonTypeInfo.*; + +/** + * forgive me lord, for I used Java + * @SeeAlso https://issues.scala-lang.org/browse/SI-5165 + **/ +@JsonTypeInfo(use=Id.NAME, include=As.PROPERTY, property="valueType") +@JsonSubTypes({ + @JsonSubTypes.Type(value=AllowableListValues.class, name="LIST"), + @JsonSubTypes.Type(value=AllowableRangeValues.class, name="RANGE") +}) +public abstract class AllowableValues {} \ No newline at end of file diff --git a/src/main/scala/com/wordnik/swagger/model/SwaggerModels.scala b/src/main/scala/com/wordnik/swagger/model/SwaggerModels.scala new file mode 100644 index 000000000000..8da12f99e748 --- /dev/null +++ b/src/main/scala/com/wordnik/swagger/model/SwaggerModels.scala @@ -0,0 +1,88 @@ +/** + * Copyright 2012 Wordnik, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.wordnik.swagger.model + +import com.fasterxml.jackson.annotation.{JsonProperty, JsonIgnore} + +case class ResourceListing( + apiVersion: String, + swaggerVersion: String, + basePath: String, + apis: List[ApiListingReference] = List()) + +case class ApiListingReference(path:String, description: String) + +case object Any extends AllowableValues +case class AllowableListValues (values: List[String] = List(), valueType: String = "LIST") extends AllowableValues +case class AllowableRangeValues(min: Int, max: Int) extends AllowableValues + +// using java.util.Map because Jackon 2 isn't deserializing ListMap correctly, and ordered +// insertion is required +case class Model( + var id: String, + var name: String, + var properties: java.util.Map[String, ModelProperty], + description: Option[String] = None) + +case class ModelProperty( + var `type`: String, + required: Boolean = false, + description: Option[String] = None, + allowableValues: AllowableValues = Any, + var items: Option[ModelRef] = None) + +case class ModelRef( + @JsonProperty("$ref") ref: String = null, + `type`: String = null) + +case class ApiListing ( + apiVersion: String, + swaggerVersion: String, + basePath: String, + var resourcePath: String, + apis: List[ApiDescription] = List(), + models: Map[String, Model] = Map()) + +case class ApiDescription ( + path: String, + description: String, + operations: List[Operation] = List()) + +case class Operation ( + httpMethod: String, + summary: String, + notes: String, + var responseClass: String, + nickname: String, + parameters: List[Parameter] = List.empty, + errorResponses: List[ErrorResponse] = List.empty, + `deprecated`: Option[String] = None) + +case class Parameter ( + name: String, + description: String, + defaultValue: String, + required: Boolean, + allowMultiple: Boolean, + var dataType: String, + allowableValues: AllowableValues = Any, + paramType: String) + +case class ErrorResponse ( + code: Int, + reason: String) + diff --git a/src/test/scala/BasicGeneratorTest.scala b/src/test/scala/BasicGeneratorTest.scala index 8d4f35f4363c..3c211f06a4a9 100644 --- a/src/test/scala/BasicGeneratorTest.scala +++ b/src/test/scala/BasicGeneratorTest.scala @@ -1,16 +1,31 @@ -import com.wordnik.swagger.core.util.JsonUtil +/** + * Copyright 2012 Wordnik, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import com.wordnik.swagger.codegen.BasicGenerator import com.wordnik.swagger.codegen.util._ -import com.wordnik.swagger.core.{Documentation, DocumentationSchema} +import com.wordnik.swagger.model._ import org.junit.runner.RunWith import org.scalatest.junit.JUnitRunner import org.scalatest.FlatSpec import org.scalatest.matchers.ShouldMatchers -import scala.collection.mutable.HashMap import scala.collection.JavaConverters._ -import scala.reflect.BeanProperty +import scala.collection.mutable.HashMap +import scala.collection.immutable.ListMap @RunWith(classOf[JUnitRunner]) class BasicGeneratorTest extends FlatSpec with ShouldMatchers { @@ -30,45 +45,44 @@ class BasicGeneratorTest extends FlatSpec with ShouldMatchers { behavior of "BasicGenerator" it should "get operations" in { - val resourceListing = json.readValue(ResourceExtractor.extractListing("src/test/resources/petstore/resources.json", None), classOf[Documentation]) - val subDocs = ApiExtractor.extractApiDocs("src/test/resources/petstore", resourceListing.getApis.asScala.toList) - val allModels = new HashMap[String, DocumentationSchema]() + val resourceListing = ResourceExtractor.fetchListing("src/test/resources/petstore/resources.json") + val subDocs = ApiExtractor.fetchApiListings("src/test/resources/petstore", resourceListing.apis) + val allModels = new HashMap[String, Model]() implicit val basePath = "http://localhost:8080/api" val generator = new SampleGenerator - val ops = generator.extractOperations(subDocs, allModels) + val ops = generator.extractApiOperations(subDocs, allModels) + allModels.size should be (5) + ops.size should be (16) - allModels.size should be (5) - ops.size should be (16) + val operations = ops.map(op => (op._2, op._3)).toMap - val operations = ops.map(op => (op._2, op._3)).toMap + (operations.keys.toSet & + Set("/pet.{format}/findByTags", "/user.{format}/createWithArray", "/user.{format}/createWithList", + "/store.{format}/order", "/user.{format}", "/pet.{format}/findByStatus", "/user.{format}/{username}", + "/user.{format}/logout", "/user.{format}/login", "/pet.{format}/{petId}", "/store.{format}/order/{orderId}", + "/pet.{format}")).size should be (12) - (operations.keys.toSet & - Set("/pet.{format}/findByTags", "/user.{format}/createWithArray", "/user.{format}/createWithList", - "/store.{format}/order", "/user.{format}", "/pet.{format}/findByStatus", "/user.{format}/{username}", - "/user.{format}/logout", "/user.{format}/login", "/pet.{format}/{petId}", "/store.{format}/order/{orderId}", - "/pet.{format}")).size should be (12) + // pick apart the /store/order api + val orderApi = operations("/store.{format}/order") - // pick apart the /store/order api - val orderApi = operations("/store.{format}/order") - - orderApi.httpMethod should be ("POST") - orderApi.summary should be ("Place an order for a pet") - orderApi.responseClass should be ("void") - orderApi.nickname should be ("placeOrder") - orderApi.getParameters.size should be (1) - orderApi.getErrorResponses.size should be (1) + orderApi.httpMethod should be ("POST") + orderApi.summary should be ("Place an order for a pet") + orderApi.responseClass should be ("void") + orderApi.nickname should be ("placeOrder") + orderApi.parameters.size should be (1) + orderApi.errorResponses.size should be (1) } it should "verify ops are grouped by path correctly" in { - val resourceListing = json.readValue(ResourceExtractor.extractListing("src/test/resources/petstore/resources.json", None), classOf[Documentation]) - val subDocs = ApiExtractor.extractApiDocs("src/test/resources/petstore", resourceListing.getApis.asScala.toList) - val allModels = new HashMap[String, DocumentationSchema]() + val resourceListing = ResourceExtractor.fetchListing("src/test/resources/petstore/resources.json") + val subDocs = ApiExtractor.fetchApiListings("src/test/resources/petstore", resourceListing.apis) + val allModels = new HashMap[String, Model]() implicit val basePath = "http://localhost:8080/api" val generator = new SampleGenerator - val ops = generator.extractOperations(subDocs, allModels) - val apiMap = generator.groupApisToFiles(ops) + val ops = generator.extractApiOperations(subDocs, allModels) + val apiMap = generator.groupOperationsToFiles(ops) // verify all apis are there (apiMap.keys.map(m => m._2).toSet & Set("user", "pet", "store")).size should be (3) @@ -84,11 +98,11 @@ class BasicGeneratorTest extends FlatSpec with ShouldMatchers { } it should "create a model map" in { - implicit val basePath = "http://localhost:8080/api" + implicit val basePath = "http://localhost:8080/api" val generator = new SampleGenerator - val model = getSampleModel + val model = sampleModel - val bundle = generator.prepareModelBundle(Map(model.id -> model)).head + val bundle = generator.prepareModelMap(Map(model.id -> model)).head // inspect properties bundle("name") should be ("SampleObject") @@ -97,20 +111,19 @@ class BasicGeneratorTest extends FlatSpec with ShouldMatchers { bundle("package") should be (Some("com.wordnik.client.model")) // inspect models - val modelList = bundle("models").asInstanceOf[List[(String, DocumentationSchema)]] + val modelList = bundle("models").asInstanceOf[List[(String, Model)]] modelList.size should be (1) modelList.head._1 should be ("SampleObject") - modelList.head._2.getClass should be (classOf[DocumentationSchema]) + modelList.head._2.getClass should be (classOf[Model]) } it should "create a model file" in { - implicit val basePath = "http://localhost:8080/api" + implicit val basePath = "http://localhost:8080/api" val generator = new SampleGenerator - val model = getSampleModel - val bundle = generator.prepareModelBundle(Map(model.id -> model)) + val model = sampleModel + val bundle = generator.prepareModelMap(Map(model.id -> model)) val modelFile = generator.bundleToSource(bundle, generator.modelTemplateFiles.toMap).head - // modelFile._1 should be ("SampleObject.test") val fileContents = modelFile._2 fileContents.indexOf("case class SampleObject") should not be (-1) @@ -121,40 +134,17 @@ class BasicGeneratorTest extends FlatSpec with ShouldMatchers { fileContents.indexOf("floatValue: Float") should not be (-1) } - def getSampleModel = { - val model = new DocumentationSchema - model.id = "SampleObject" - model.name = "SampleObject" - model.properties = { - val list = new HashMap[String, DocumentationSchema] - - val stringProperty = new DocumentationSchema - stringProperty.name = "stringValue" - stringProperty.setType("string") - list += "stringValue" -> stringProperty - - val intProperty = new DocumentationSchema - intProperty.name = "intValue" - intProperty.setType("int") - list += "intValue" -> intProperty - - val longProperty = new DocumentationSchema - longProperty.name = "longValue" - longProperty.setType("long") - list += "longValue" -> longProperty - - val floatProperty = new DocumentationSchema - floatProperty.name = "floatValue" - floatProperty.setType("float") - list += "floatValue" -> floatProperty - - val doubleProperty = new DocumentationSchema - doubleProperty.name = "doubleValue" - doubleProperty.setType("double") - list += "doubleValue" -> doubleProperty - - list.asJava - } - model + def sampleModel = { + Model( + "SampleObject", + "SampleObject", + Map( + "stringValue" -> ModelProperty("string"), + "intValue" -> ModelProperty("int"), + "longValue" -> ModelProperty("long"), + "floatValue" -> ModelProperty("float"), + "doubleValue" -> ModelProperty("double")).asJava, + Some("a sample object")) } -} \ No newline at end of file +} + diff --git a/src/test/scala/JavaCodegenConfigTest.scala b/src/test/scala/BasicJavaGeneratorTest.scala similarity index 73% rename from src/test/scala/JavaCodegenConfigTest.scala rename to src/test/scala/BasicJavaGeneratorTest.scala index b62ca60406ed..8b76bc12c14a 100644 --- a/src/test/scala/JavaCodegenConfigTest.scala +++ b/src/test/scala/BasicJavaGeneratorTest.scala @@ -1,23 +1,34 @@ -import com.wordnik.swagger.core.util.JsonUtil -import com.wordnik.swagger.core.{Documentation, DocumentationSchema} +/** + * Copyright 2012 Wordnik, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -import com.wordnik.swagger.codegen.BasicJavaGenerator +import com.wordnik.swagger.model._ +import com.wordnik.swagger.codegen.{BasicJavaGenerator, PathUtil} import com.wordnik.swagger.codegen.util._ import com.wordnik.swagger.codegen.language._ -import com.wordnik.swagger.codegen.PathUtil import org.junit.runner.RunWith import org.scalatest.junit.JUnitRunner import org.scalatest.FlatSpec import org.scalatest.matchers.ShouldMatchers -import scala.collection.JavaConverters._ import scala.reflect.BeanProperty @RunWith(classOf[JUnitRunner]) class BasicJavaGeneratorTest extends FlatSpec with ShouldMatchers { val json = ScalaJsonUtil.getJsonMapper - val config = new BasicJavaGenerator behavior of "BasicJavaGenerator" @@ -90,9 +101,7 @@ class BasicJavaGeneratorTest extends FlatSpec with ShouldMatchers { "double" -> ("Double", "null"), "object" -> ("Object", "null")) expected.map(e => { - val model = new DocumentationSchema - model.name = "simple_" + e._1 - model.setType(e._1) + val model = ModelProperty(e._1) config.toDeclaration(model) should be (e._2) }) } @@ -119,13 +128,10 @@ class BasicJavaGeneratorTest extends FlatSpec with ShouldMatchers { * support list declarations with string inner value and the correct default value */ it should "create a declaration with a List of strings" in { - val model = new DocumentationSchema - model.name = "arrayOfStrings" - model.setType("Array") - model.items = new DocumentationSchema - model.items.setType("string") - - val m = config.toDeclaration(model) + val property = ModelProperty( + "Array", + items=Some(ModelRef(`type`= "string"))) + val m = config.toDeclaration(property) m._1 should be ("List") m._2 should be ("new ArrayList()") } @@ -134,13 +140,10 @@ class BasicJavaGeneratorTest extends FlatSpec with ShouldMatchers { * support list declarations with int inner value and the correct default value */ it should "create a declaration with a List of ints" in { - val model = new DocumentationSchema - model.name = "arrayOfInts" - model.setType("Array") - model.items = new DocumentationSchema - model.items.setType("int") - - val m = config.toDeclaration(model) + val property = ModelProperty( + "Array", + items=Some(ModelRef(`type`= "int"))) + val m = config.toDeclaration(property) m._1 should be ("List") m._2 should be ("new ArrayList()") } @@ -149,13 +152,10 @@ class BasicJavaGeneratorTest extends FlatSpec with ShouldMatchers { * support list declarations with float inner value and the correct default value */ it should "create a declaration with a List of floats" in { - val model = new DocumentationSchema - model.name = "arrayOfFloats" - model.setType("Array") - model.items = new DocumentationSchema - model.items.setType("float") - - val m = config.toDeclaration(model) + val property = ModelProperty( + "Array", + items=Some(ModelRef(`type`= "float"))) + val m = config.toDeclaration(property) m._1 should be ("List") m._2 should be ("new ArrayList()") } @@ -164,13 +164,10 @@ class BasicJavaGeneratorTest extends FlatSpec with ShouldMatchers { * support list declarations with double inner value and the correct default value */ it should "create a declaration with a List of doubles" in { - val model = new DocumentationSchema - model.name = "arrayOfDoubles" - model.setType("Array") - model.items = new DocumentationSchema - model.items.setType("double") - - val m = config.toDeclaration(model) + val property = ModelProperty( + "Array", + items=Some(ModelRef(`type`= "double"))) + val m = config.toDeclaration(property) m._1 should be ("List") m._2 should be ("new ArrayList()") } @@ -179,13 +176,10 @@ class BasicJavaGeneratorTest extends FlatSpec with ShouldMatchers { * support list declarations with complex inner value and the correct default value */ it should "create a declaration with a List of complex objects" in { - val model = new DocumentationSchema - model.name = "arrayOfFloats" - model.setType("Array") - model.items = new DocumentationSchema - model.items.setType("User") - - val m = config.toDeclaration(model) + val property = ModelProperty( + "Array", + items=Some(ModelRef(`type`= "User"))) + val m = config.toDeclaration(property) m._1 should be ("List") m._2 should be ("new ArrayList()") } diff --git a/src/test/scala/ObjcCodegenConfigTest.scala b/src/test/scala/BasicObjcGeneratorTest.scala similarity index 66% rename from src/test/scala/ObjcCodegenConfigTest.scala rename to src/test/scala/BasicObjcGeneratorTest.scala index fe77673905f2..29214d7322d7 100644 --- a/src/test/scala/ObjcCodegenConfigTest.scala +++ b/src/test/scala/BasicObjcGeneratorTest.scala @@ -1,10 +1,23 @@ -import com.wordnik.swagger.core.util.JsonUtil -import com.wordnik.swagger.core.{Documentation, DocumentationSchema} +/** + * Copyright 2012 Wordnik, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -import com.wordnik.swagger.codegen.{BasicObjcGenerator, Codegen} +import com.wordnik.swagger.model._ +import com.wordnik.swagger.codegen.{BasicObjcGenerator, Codegen, PathUtil} import com.wordnik.swagger.codegen.util._ import com.wordnik.swagger.codegen.language._ -import com.wordnik.swagger.codegen.PathUtil import org.junit.runner.RunWith import org.scalatest.junit.JUnitRunner @@ -12,7 +25,6 @@ import org.scalatest.FlatSpec import org.scalatest.matchers.ShouldMatchers import scala.collection.mutable.HashMap -import scala.collection.JavaConverters._ @RunWith(classOf[JUnitRunner]) class BasicObjcGeneratorTest extends FlatSpec with ShouldMatchers { @@ -90,9 +102,7 @@ class BasicObjcGeneratorTest extends FlatSpec with ShouldMatchers { "double" -> ("NSNumber*", "null"), "object" -> ("NSObject*", "null")) expected.map(e => { - val model = new DocumentationSchema - model.name = "simple_" + e._1 - model.setType(e._1) + val model = ModelProperty(e._1) config.toDeclaration(model) should be (e._2) }) } @@ -116,13 +126,10 @@ class BasicObjcGeneratorTest extends FlatSpec with ShouldMatchers { * support list declarations with string inner value and the correct default value */ it should "create a declaration with a List of strings" in { - val model = new DocumentationSchema - model.name = "arrayOfStrings" - model.setType("Array") - model.items = new DocumentationSchema - model.items.setType("string") - - val m = config.toDeclaration(model) + val property = ModelProperty( + "Array", + items=Some(ModelRef(`type`= "string"))) + val m = config.toDeclaration(property) m._1 should be ("NSArray*") m._2 should be ("null") } @@ -131,13 +138,10 @@ class BasicObjcGeneratorTest extends FlatSpec with ShouldMatchers { * support list declarations with int inner value and the correct default value */ it should "create a declaration with a List of ints" in { - val model = new DocumentationSchema - model.name = "arrayOfInts" - model.setType("Array") - model.items = new DocumentationSchema - model.items.setType("int") - - val m = config.toDeclaration(model) + val property = ModelProperty( + "Array", + items=Some(ModelRef(`type`= "int"))) + val m = config.toDeclaration(property) m._1 should be ("NSArray*") m._2 should be ("null") } @@ -146,13 +150,10 @@ class BasicObjcGeneratorTest extends FlatSpec with ShouldMatchers { * support list declarations with float inner value and the correct default value */ it should "create a declaration with a List of floats" in { - val model = new DocumentationSchema - model.name = "arrayOfFloats" - model.setType("Array") - model.items = new DocumentationSchema - model.items.setType("float") - - val m = config.toDeclaration(model) + val property = ModelProperty( + "Array", + items=Some(ModelRef(`type`= "float"))) + val m = config.toDeclaration(property) m._1 should be ("NSArray*") m._2 should be ("null") } @@ -161,13 +162,10 @@ class BasicObjcGeneratorTest extends FlatSpec with ShouldMatchers { * support list declarations with double inner value and the correct default value */ it should "create a declaration with a List of doubles" in { - val model = new DocumentationSchema - model.name = "arrayOfDoubles" - model.setType("Array") - model.items = new DocumentationSchema - model.items.setType("double") - - val m = config.toDeclaration(model) + val property = ModelProperty( + "Array", + items=Some(ModelRef(`type`= "double"))) + val m = config.toDeclaration(property) m._1 should be ("NSArray*") m._2 should be ("null") } @@ -176,26 +174,22 @@ class BasicObjcGeneratorTest extends FlatSpec with ShouldMatchers { * support list declarations with complex inner value and the correct default value */ it should "create a declaration with a List of complex objects" in { - val model = new DocumentationSchema - model.name = "arrayOfFloats" - model.setType("Array") - model.items = new DocumentationSchema - model.items.setType("User") - - val m = config.toDeclaration(model) + val property = ModelProperty( + "Array", + items=Some(ModelRef(`type`= "User"))) + val m = config.toDeclaration(property) m._1 should be ("NSArray*") m._2 should be ("null") } it should "verify an api map with path param" in { - val resourceListing = json.readValue(ResourceExtractor.extractListing("src/test/resources/petstore/resources.json", None), classOf[Documentation]) - - val subDocs = ApiExtractor.extractApiDocs("src/test/resources/petstore", resourceListing.getApis.asScala.toList) + val resourceListing = ResourceExtractor.fetchListing("src/test/resources/petstore/resources.json", None) + val apis = ApiExtractor.extractApiOperations("src/test/resources/petstore", resourceListing.apis) val codegen = new Codegen(config) - val petApi = subDocs.filter(doc => doc.getResourcePath == "/pet").head + val petApi = apis.filter(doc => doc.resourcePath == "/pet").head - val endpoint = petApi.getApis().asScala.filter(api => api.path == "/pet.{format}/{petId}").head - val operation = endpoint.getOperations.asScala.filter(op => op.httpMethod == "GET").head + val endpoint = petApi.apis.filter(api => api.path == "/pet.{format}/{petId}").head + val operation = endpoint.operations.filter(op => op.httpMethod == "GET").head val m = codegen.apiToMap("http://my.api.com/api", operation) m("path") should be ("http://my.api.com/api") @@ -218,14 +212,13 @@ class BasicObjcGeneratorTest extends FlatSpec with ShouldMatchers { } it should "verify an api map with query params" in { - val resourceListing = json.readValue(ResourceExtractor.extractListing("src/test/resources/petstore/resources.json", None), classOf[Documentation]) - - val subDocs = ApiExtractor.extractApiDocs("src/test/resources/petstore", resourceListing.getApis.asScala.toList) + val resourceListing = ResourceExtractor.fetchListing("src/test/resources/petstore/resources.json", None) + val apis = ApiExtractor.extractApiOperations("src/test/resources/petstore", resourceListing.apis) val codegen = new Codegen(config) - val petApi = subDocs.filter(doc => doc.getResourcePath == "/pet").head + val petApi = apis.filter(doc => doc.resourcePath == "/pet").head - val endpoint = petApi.getApis().asScala.filter(api => api.path == "/pet.{format}/findByTags").head - val operation = endpoint.getOperations.asScala.filter(op => op.httpMethod == "GET").head + val endpoint = petApi.apis.filter(api => api.path == "/pet.{format}/findByTags").head + val operation = endpoint.operations.filter(op => op.httpMethod == "GET").head val m = codegen.apiToMap("http://my.api.com/api", operation) m("path") should be ("http://my.api.com/api") @@ -253,22 +246,21 @@ class BasicObjcGeneratorTest extends FlatSpec with ShouldMatchers { } it should "create an api file" in { + implicit val basePath = "http://localhost:8080/api" val codegen = new Codegen(config) - val resourceListing = json.readValue(ResourceExtractor.extractListing("src/test/resources/petstore/resources.json", None), classOf[Documentation]) + val resourceListing = ResourceExtractor.fetchListing("src/test/resources/petstore/resources.json", None) - val subDocs = ApiExtractor.extractApiDocs("src/test/resources/petstore", resourceListing.getApis.asScala.toList) - val petApi = subDocs.filter(doc => doc.getResourcePath == "/pet").head + val apis = ApiExtractor.extractApiOperations("src/test/resources/petstore", resourceListing.apis) + val petApi = apis.filter(doc => doc.resourcePath == "/pet").head - val endpoint = petApi.getApis().asScala.filter(api => api.path == "/pet.{format}/findByTags").head - val operation = endpoint.getOperations.asScala.filter(op => op.httpMethod == "GET").head + val endpoint = petApi.apis.filter(api => api.path == "/pet.{format}/findByTags").head + val operation = endpoint.operations.filter(op => op.httpMethod == "GET").head val m = codegen.apiToMap("http://my.api.com/api", operation) - implicit val basePath = "http://localhost:8080/api" + val allModels = new HashMap[String, Model] + val operations = config.extractApiOperations(apis, allModels) - val allModels = new HashMap[String, DocumentationSchema] - val operations = config.extractOperations(subDocs, allModels) - - val apiMap = config.groupApisToFiles(operations) + val apiMap = config.groupOperationsToFiles(operations) val bundle = config.prepareApiBundle(apiMap) val apiFiles = config.bundleToSource(bundle, config.apiTemplateFiles.toMap) diff --git a/src/test/scala/ScalaCodegenConfigTest.scala b/src/test/scala/BasicScalaGeneratorTest.scala similarity index 66% rename from src/test/scala/ScalaCodegenConfigTest.scala rename to src/test/scala/BasicScalaGeneratorTest.scala index 7fa8f908484d..ef77f5c70471 100644 --- a/src/test/scala/ScalaCodegenConfigTest.scala +++ b/src/test/scala/BasicScalaGeneratorTest.scala @@ -1,10 +1,23 @@ -import com.wordnik.swagger.core.util.JsonUtil -import com.wordnik.swagger.core.{Documentation, DocumentationSchema} +/** + * Copyright 2012 Wordnik, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -import com.wordnik.swagger.codegen.{BasicScalaGenerator, Codegen} +import com.wordnik.swagger.model._ +import com.wordnik.swagger.codegen.{BasicScalaGenerator, Codegen, PathUtil} import com.wordnik.swagger.codegen.util._ import com.wordnik.swagger.codegen.language._ -import com.wordnik.swagger.codegen.PathUtil import org.junit.runner.RunWith import org.scalatest.junit.JUnitRunner @@ -12,7 +25,6 @@ import org.scalatest.FlatSpec import org.scalatest.matchers.ShouldMatchers import scala.collection.mutable.HashMap -import scala.collection.JavaConverters._ @RunWith(classOf[JUnitRunner]) class BasicScalaGeneratorTest extends FlatSpec with ShouldMatchers { @@ -90,9 +102,7 @@ class BasicScalaGeneratorTest extends FlatSpec with ShouldMatchers { "double" -> ("Double", "0.0"), "object" -> ("Any", "_")) expected.map(e => { - val model = new DocumentationSchema - model.name = "simple_" + e._1 - model.setType(e._1) + val model = ModelProperty(e._1) config.toDeclaration(model) should be (e._2) }) } @@ -116,13 +126,10 @@ class BasicScalaGeneratorTest extends FlatSpec with ShouldMatchers { * support list declarations with string inner value and the correct default value */ it should "create a declaration with a List of strings" in { - val model = new DocumentationSchema - model.name = "arrayOfStrings" - model.setType("Array") - model.items = new DocumentationSchema - model.items.setType("string") - - val m = config.toDeclaration(model) + val property = ModelProperty( + "Array", + items=Some(ModelRef(`type`= "string"))) + val m = config.toDeclaration(property) m._1 should be ("List[String]") m._2 should be ("_") } @@ -131,13 +138,10 @@ class BasicScalaGeneratorTest extends FlatSpec with ShouldMatchers { * support list declarations with int inner value and the correct default value */ it should "create a declaration with a List of ints" in { - val model = new DocumentationSchema - model.name = "arrayOfInts" - model.setType("Array") - model.items = new DocumentationSchema - model.items.setType("int") - - val m = config.toDeclaration(model) + val property = ModelProperty( + "Array", + items=Some(ModelRef(`type`= "int"))) + val m = config.toDeclaration(property) m._1 should be ("List[Int]") m._2 should be ("0") } @@ -146,13 +150,10 @@ class BasicScalaGeneratorTest extends FlatSpec with ShouldMatchers { * support list declarations with float inner value and the correct default value */ it should "create a declaration with a List of floats" in { - val model = new DocumentationSchema - model.name = "arrayOfFloats" - model.setType("Array") - model.items = new DocumentationSchema - model.items.setType("float") - - val m = config.toDeclaration(model) + val property = ModelProperty( + "Array", + items=Some(ModelRef(`type`= "float"))) + val m = config.toDeclaration(property) m._1 should be ("List[Float]") m._2 should be ("0f") } @@ -161,13 +162,10 @@ class BasicScalaGeneratorTest extends FlatSpec with ShouldMatchers { * support list declarations with double inner value and the correct default value */ it should "create a declaration with a List of doubles" in { - val model = new DocumentationSchema - model.name = "arrayOfDoubles" - model.setType("Array") - model.items = new DocumentationSchema - model.items.setType("double") - - val m = config.toDeclaration(model) + val property = ModelProperty( + "Array", + items=Some(ModelRef(`type`= "double"))) + val m = config.toDeclaration(property) m._1 should be ("List[Double]") m._2 should be ("0.0") } @@ -176,26 +174,22 @@ class BasicScalaGeneratorTest extends FlatSpec with ShouldMatchers { * support list declarations with complex inner value and the correct default value */ it should "create a declaration with a List of complex objects" in { - val model = new DocumentationSchema - model.name = "arrayOfFloats" - model.setType("Array") - model.items = new DocumentationSchema - model.items.setType("User") - - val m = config.toDeclaration(model) + val property = ModelProperty( + "Array", + items=Some(ModelRef(`type`= "User"))) + val m = config.toDeclaration(property) m._1 should be ("List[User]") m._2 should be ("_") } it should "verify an api map with query params" in { - val resourceListing = json.readValue(ResourceExtractor.extractListing("src/test/resources/petstore/resources.json", None), classOf[Documentation]) - - val subDocs = ApiExtractor.extractApiDocs("src/test/resources/petstore", resourceListing.getApis.asScala.toList) + val resourceListing = ResourceExtractor.fetchListing("src/test/resources/petstore/resources.json", None) + val apis = ApiExtractor.extractApiOperations("src/test/resources/petstore", resourceListing.apis) val codegen = new Codegen(config) - val petApi = subDocs.filter(doc => doc.getResourcePath == "/pet").head + val petApi = apis.filter(doc => doc.resourcePath == "/pet").head - val endpoint = petApi.getApis().asScala.filter(api => api.path == "/pet.{format}/findByTags").head - val operation = endpoint.getOperations.asScala.filter(op => op.httpMethod == "GET").head + val endpoint = petApi.apis.filter(api => api.path == "/pet.{format}/findByTags").head + val operation = endpoint.operations.filter(op => op.httpMethod == "GET").head val m = codegen.apiToMap("http://my.api.com/api", operation) m("path") should be ("http://my.api.com/api") @@ -224,22 +218,24 @@ class BasicScalaGeneratorTest extends FlatSpec with ShouldMatchers { } it should "verify an api map with query params with default values" in { - val resourceListing = json.readValue(ResourceExtractor.extractListing("src/test/resources/petstore/resources.json", None), classOf[Documentation]) - - val subDocs = ApiExtractor.extractApiDocs("src/test/resources/petstore", resourceListing.getApis.asScala.toList) + val resourceListing = ResourceExtractor.fetchListing("src/test/resources/petstore/resources.json", None) + val apis = ApiExtractor.extractApiOperations("src/test/resources/petstore", resourceListing.apis) val codegen = new Codegen(config) - val petApi = subDocs.filter(doc => doc.getResourcePath == "/pet").head + val petApi = apis.filter(doc => doc.resourcePath == "/pet").head - val endpoint = petApi.getApis().asScala.filter(api => api.path == "/pet.{format}/findByStatus").head - val operation = endpoint.getOperations.asScala.filter(op => op.httpMethod == "GET").head + val endpoint = petApi.apis.filter(api => api.path == "/pet.{format}/findByStatus").head + val operation = endpoint.operations.filter(op => op.httpMethod == "GET").head val m = codegen.apiToMap("http://my.api.com/api", operation) m("path") should be ("http://my.api.com/api") m("bodyParams").asInstanceOf[List[_]].size should be (0) m("httpMethod") should be ("GET") - // Pet => NIKPet + // Pet => Pet m("returnBaseType") should be (Some("Pet")) + + // problem here + println("the operation has response class " + operation.responseClass) m("returnType") should be (Some("List[Pet]")) m("returnTypeIsPrimitive") should be (None) m("pathParams").asInstanceOf[List[_]].size should be (0) @@ -261,27 +257,26 @@ class BasicScalaGeneratorTest extends FlatSpec with ShouldMatchers { } it should "create an api file" in { + implicit val basePath = "http://localhost:8080/api" val codegen = new Codegen(config) - val resourceListing = json.readValue(ResourceExtractor.extractListing("src/test/resources/petstore/resources.json", None), classOf[Documentation]) + val resourceListing = ResourceExtractor.fetchListing("src/test/resources/petstore/resources.json", None) - val subDocs = ApiExtractor.extractApiDocs("src/test/resources/petstore", resourceListing.getApis.asScala.toList) - val petApi = subDocs.filter(doc => doc.getResourcePath == "/pet").head + val apis = ApiExtractor.extractApiOperations("src/test/resources/petstore", resourceListing.apis) + val petApi = apis.filter(doc => doc.resourcePath == "/pet").head - val endpoint = petApi.getApis().asScala.filter(api => api.path == "/pet.{format}/findByTags").head - val operation = endpoint.getOperations.asScala.filter(op => op.httpMethod == "GET").head + val endpoint = petApi.apis.filter(api => api.path == "/pet.{format}/findByTags").head + val operation = endpoint.operations.filter(op => op.httpMethod == "GET").head val m = codegen.apiToMap("http://my.api.com/api", operation) - implicit val basePath = "http://localhost:8080/api" + val allModels = new HashMap[String, Model] + val operations = config.extractApiOperations(List(petApi), allModels) - val allModels = new HashMap[String, DocumentationSchema] - val operations = config.extractOperations(subDocs, allModels) - - val apiMap = config.groupApisToFiles(operations).filter(m => m._1 == ("http://petstore.swagger.wordnik.com/api","pet")).toMap + val apiMap = config.groupOperationsToFiles(operations) val bundle = config.prepareApiBundle(apiMap) - val files = config.bundleToSource(bundle, config.apiTemplateFiles.toMap) - - files.size should be (1) - val file = files.head + val apiFiles = config.bundleToSource(bundle, config.apiTemplateFiles.toMap) + + apiFiles.size should be (1) + val file = apiFiles.head // verify the filename is set file._1.indexOf("""PetApi.scala""") should not be (-1) diff --git a/src/test/scala/CodegenConfigTest.scala b/src/test/scala/CodegenConfigTest.scala index 3d0dcdf78681..4b798d05c31f 100644 --- a/src/test/scala/CodegenConfigTest.scala +++ b/src/test/scala/CodegenConfigTest.scala @@ -1,5 +1,18 @@ -import com.wordnik.swagger.core.util.JsonUtil -import com.wordnik.swagger.core.Documentation +/** + * Copyright 2012 Wordnik, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ import com.wordnik.swagger.codegen.util._ import com.wordnik.swagger.codegen.language._ @@ -10,7 +23,6 @@ import org.scalatest.junit.JUnitRunner import org.scalatest.FlatSpec import org.scalatest.matchers.ShouldMatchers -import scala.collection.JavaConverters._ import scala.reflect.BeanProperty @RunWith(classOf[JUnitRunner]) diff --git a/src/test/scala/PathUtilTest.scala b/src/test/scala/PathUtilTest.scala index e700de83b934..1c95ce15680e 100644 --- a/src/test/scala/PathUtilTest.scala +++ b/src/test/scala/PathUtilTest.scala @@ -1,3 +1,19 @@ +/** + * Copyright 2012 Wordnik, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + import com.wordnik.swagger.codegen.BasicScalaGenerator import org.junit.runner.RunWith diff --git a/src/test/scala/SwaggerModelTest.scala b/src/test/scala/SwaggerModelTest.scala new file mode 100644 index 000000000000..ab814e58c4f3 --- /dev/null +++ b/src/test/scala/SwaggerModelTest.scala @@ -0,0 +1,164 @@ +/** + * Copyright 2012 Wordnik, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.wordnik.swagger.model._ +import com.wordnik.swagger.codegen.util.ScalaJsonUtil + +import org.junit.runner.RunWith +import org.scalatest.junit.JUnitRunner +import org.scalatest.FlatSpec +import org.scalatest.matchers.ShouldMatchers + +import scala.io._ + +import scala.collection.JavaConverters._ + +@RunWith(classOf[JUnitRunner]) +class SwaggerModelTest extends FlatSpec with ShouldMatchers { + val json = ScalaJsonUtil.getJsonMapper + behavior of "Swagger Model" + + it should "deserialize ResourceListing" in { + val str = Source.fromFile("src/test/resources/petstore/resources.json").mkString + val listing = json.readValue(str, classOf[ResourceListing]) + + listing.apiVersion should be ("0.2") + listing.swaggerVersion should be ("1.1") + listing.basePath should be ("http://petstore.swagger.wordnik.com/api") + listing.apis.size should be (3) + + val apis = listing.apis.map(api => (api.path, api.description)).toMap + + apis("/store.{format}") should be ("Operations about store") + apis("/pet.{format}") should be ("Operations about pets") + apis("/user.{format}") should be ("Operations about user") + } + + it should "deserialize ApiListing" in { + val str = Source.fromFile("src/test/resources/petstore/pet.json").mkString + val apiListing = json.readValue(str, classOf[ApiListing]) + + apiListing.apiVersion should be ("0.2") + apiListing.swaggerVersion should be ("1.1") + apiListing.basePath should be ("http://petstore.swagger.wordnik.com/api") + apiListing.resourcePath should be ("/pet") + apiListing.apis.size should be (4) + apiListing.models.size should be (3) + + val apiMap = apiListing.apis.map(api => (api.path, api)).toMap + val petBaseApi = apiMap("/pet.{format}/{petId}") + petBaseApi.description should be ("Operations about pets") + petBaseApi.operations.size should be (1) + + val getPetById = petBaseApi.operations.head + getPetById.httpMethod should be ("GET") + getPetById.summary should be ("Find pet by ID") + getPetById.notes should be ("Returns a pet based on ID") + getPetById.responseClass should be ("Pet") + + getPetById.nickname should be ("getPetById") + getPetById.parameters.size should be (1) + + val param = getPetById.parameters.head + param.name should be ("petId") + param.description should be ("ID of pet that needs to be fetched") + param.paramType should be ("path") + param.required should be (true) + param.allowMultiple should be (false) + param.dataType should be ("string") + + getPetById.errorResponses.size should be (2) + val errors = getPetById.errorResponses.map(error => (error.code, error.reason)).toMap + + errors(400) should be ("Invalid ID supplied") + errors(404) should be ("Pet not found") + } + + it should "deserialize ApiListing with AllowableValues" in { + val str = Source.fromFile("src/test/resources/petstore/pet.json").mkString + val apiListing = json.readValue(str, classOf[ApiListing]) + val apiMap = apiListing.apis.map(api => (api.path, api)).toMap + val petBaseApi = apiMap("/pet.{format}/findByStatus") + val findPetsByStatus = petBaseApi.operations.head + val param = findPetsByStatus.parameters.head + + param.name should be ("status") + param.description should be ("Status values that need to be considered for filter") + param.paramType should be ("query") + param.required should be (true) + param.allowMultiple should be (true) + param.dataType should be ("string") + param.allowableValues should not be (null) + + param.allowableValues.isInstanceOf[AllowableListValues] should be (true) + val allowableValues = param.allowableValues.asInstanceOf[AllowableListValues] + allowableValues.valueType should be ("LIST") + allowableValues.values.size should be (3) + (allowableValues.values.toSet & Set("available", "pending", "sold")).size should be (3) + } + + it should "maintain model property order when deserializing" in { + val str = Source.fromFile("src/test/resources/petstore/pet.json").mkString + val apiListing = json.readValue(str, classOf[ApiListing]) + + val models = apiListing.models + models.size should be (3) + val pet = models("Pet") + + val petProperties = pet.properties.asScala.toList + + petProperties.size should be (6) + petProperties(0)._1 should be ("tags") + petProperties(1)._1 should be ("id") + petProperties(2)._1 should be ("category") + petProperties(3)._1 should be ("status") + petProperties(4)._1 should be ("name") + petProperties(5)._1 should be ("photoUrls") + } + + it should "deserialize models" in { + val str = Source.fromFile("src/test/resources/petstore/pet.json").mkString + val apiListing = json.readValue(str, classOf[ApiListing]) + + val models = apiListing.models + models.size should be (3) + + val pet = models("Pet") + pet.id should be ("Pet") + pet.properties.size should be (6) + + val properties = pet.properties.asScala + val tags = properties("tags") + tags.`type` should be ("Array") + tags.items should not be (None) + tags.items.get.ref should be ("Tag") + + val id = properties("id") + // id.`type` shoud be ("long") + + val category = properties("category") + category.`type` should be ("Category") + + val status = properties("status") + status.`type` should be ("string") + status.description should be (Some("pet status in the store")) + status.allowableValues should not be (null) + status.allowableValues.isInstanceOf[AllowableListValues] should be (true) + val allowableValues = status.allowableValues.asInstanceOf[AllowableListValues] + allowableValues.valueType should be ("LIST") + (allowableValues.values.toSet & Set("available", "pending", "sold")).size should be (3) + } +} \ No newline at end of file diff --git a/src/test/scala/UtilsTest.scala b/src/test/scala/UtilsTest.scala index 94c0cabe1f10..6a5897b2593c 100644 --- a/src/test/scala/UtilsTest.scala +++ b/src/test/scala/UtilsTest.scala @@ -1,7 +1,21 @@ -import com.wordnik.swagger.core.util.JsonUtil -import com.wordnik.swagger.codegen.util._ +/** + * Copyright 2012 Wordnik, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -import com.wordnik.swagger.core.Documentation +import com.wordnik.swagger.model._ +import com.wordnik.swagger.codegen.util._ import org.junit.runner.RunWith import org.scalatest.junit.JUnitRunner @@ -17,11 +31,9 @@ class ResourceExtractorTest extends FlatSpec with ShouldMatchers { behavior of "ResourceExtractor" it should "get 3 apis from a resource listing" in { - // todo: change to return documentation object - val jsonString = ResourceExtractor.extractListing("src/test/resources/petstore/resources.json") - val resourceListing = json.readValue(jsonString, classOf[Documentation]) + val resourceListing = ResourceExtractor.fetchListing("src/test/resources/petstore/resources.json") resourceListing should not be(null) - resourceListing.getApis.size should be (3) + resourceListing.apis.size should be (3) } } @@ -31,26 +43,26 @@ class ApiExtractorTest extends FlatSpec with ShouldMatchers { behavior of "ApiExtractor" it should "verify the deserialization of the store api" in { - val resourceListing = json.readValue(ResourceExtractor.extractListing("src/test/resources/petstore/resources.json", None), classOf[Documentation]) - val docs = ApiExtractor.extractApiDocs("src/test/resources/petstore", resourceListing.getApis.asScala.toList) + val resourceListing = ResourceExtractor.fetchListing("src/test/resources/petstore/resources.json") + val docs = ApiExtractor.extractApiOperations("src/test/resources/petstore", resourceListing.apis) val m = docs.map(t => (t.resourcePath, t)).toMap val storeApi = m("/store") storeApi should not be (null) - storeApi.getApis.size should be (2) + storeApi.apis.size should be (2) - val f = storeApi.getApis.asScala.map(m => (m.path, m)).toMap + val f = storeApi.apis.map(m => (m.path, m)).toMap (f.keys.toSet & Set("/store.{format}/order/{orderId}","/store.{format}/order")).size should be (2) val storeOps = f("/store.{format}/order/{orderId}") - val ops = storeOps.getOperations.asScala.map(o => (o.nickname, o)).toMap + val ops = storeOps.operations.map(o => (o.nickname, o)).toMap val getOrderById = ops("getOrderById") getOrderById should not be null getOrderById.httpMethod should be ("GET") - getOrderById.getParameters.size should be (1) - getOrderById.getErrorResponses.size should be (2) + getOrderById.parameters.size should be (1) + getOrderById.errorResponses.size should be (2) } } @@ -62,22 +74,22 @@ class CoreUtilsTest extends FlatSpec with ShouldMatchers { behavior of "CoreUtils" it should "verify models are extracted" in { - val jsonString = ResourceExtractor.extractListing("src/test/resources/petstore/resources.json") - val resourceListing = json.readValue(jsonString, classOf[Documentation]) + val resourceListing = ResourceExtractor.fetchListing("src/test/resources/petstore/resources.json") + val apis = ApiExtractor.extractApiOperations("src/test/resources/petstore", resourceListing.apis) - val apis = ApiExtractor.extractApiDocs("src/test/resources/petstore", resourceListing.getApis.asScala.toList) - - val cu = CoreUtils.extractAllModels(apis) + val cu = CoreUtils.extractAllModels2(apis) cu.size should be (5) (cu.keys.toSet & Set("User", "Tag", "Pet", "Category", "Order")).size should be (5) } it should "verify operation names" in { - val jsonString = ResourceExtractor.extractListing("src/test/resources/petstore/pet.json") - val petApi = json.readValue(jsonString, classOf[Documentation]) - val eps = petApi.getApis.asScala.map(api => (api.path, api)).toMap - val ops = eps("/pet.{format}").getOperations.asScala.map(ep => (ep.nickname, ep)).toMap + val resourceListing = ResourceExtractor.fetchListing("src/test/resources/petstore/resources.json") + val apis = ApiExtractor.extractApiOperations("src/test/resources/petstore", resourceListing.apis) + + val petApi = apis.filter(api => api.resourcePath == "/pet").head + val eps = petApi.apis.map(api => (api.path, api)).toMap + val ops = eps("/pet.{format}").operations.map(ep => (ep.nickname, ep)).toMap ops.size should be (2)