Swagger: AS3 sdk - templates, structure and updates for generating as3 library

This commit is contained in:
Deepak Michael 2011-08-17 20:46:44 +05:30
parent 717e73ca1a
commit d11d82b9e0
30 changed files with 1282 additions and 10 deletions

View File

@ -104,7 +104,7 @@
<target name="generate-java" depends="resolve, compile" description="generates APIs and model classes for java language">
<fail unless="apiConfiguration.set">
Must specify the parameter for apiConfiguration
eg. -DapiConfiguration==../api-server-lib/java/config/apiConfiguration.json
eg. -DapiConfiguration=../api-server-lib/java/config/apiConfiguration.json
</fail>
<echo>
apiConfiguration to be used : ${apiConfiguration}
@ -120,6 +120,26 @@
</java>
</target>
<!-- generates the classes -->
<target name="generate-as3" depends="resolve, compile" description="generates APIs and model classes for java language">
<fail unless="apiConfiguration.set">
Must specify the parameter for apiConfiguration
eg. -DapiConfiguration=../api-server-lib/as3/config/apiConfiguration.json
</fail>
<echo>
apiConfiguration to be used : ${apiConfiguration}
</echo>
<java classname="com.wordnik.swagger.codegen.config.as3.As3LibCodeGen">
<classpath>
<pathelement location="build/main/java" />
<fileset dir="lib">
<include name="**/*.jar"/>
</fileset>
</classpath>
<arg value="${apiConfiguration}"/>
</java>
</target>
<target name="deploy" depends="resolve, compile" description="builds and copies the artifact to the local ivy cache">
<echo message="building pom file" />
<property name="ivy.pom.version" value="${version.identifier}" />

View File

@ -0,0 +1,30 @@
{
"apiUrl":"http://swagr.api.wordnik.com/v4/",
"apiKey":"special-key",
"defaultServiceBaseClass":"SwaggerApi",
"defaultModelBaseClass":"Object",
"serviceBaseClasses":{},
"defaultModelImports":[],
"defaultServiceImports":["mx.rpc.AsyncToken","mx.utils.UIDUtil",
"flash.utils.Dictionary","flash.events.EventDispatcher",
"com.wordnik.swagger.common.ApiUserCredentials","com.wordnik.swagger.event.Response",
"com.wordnik.swagger.common.SwaggerApi"],
"modelPackageName":"com.wordnik.swagger.model",
"apiPackageName":"com.wordnik.swagger.api",
"ignoreMethods":["WordAPI.getWordFrequency","WordAPI.getAudio","WordAPI.getWordStats","WordAPI.getFlickrImages"],
"ignoreModels":["wordStats","photo","sizes"],
"outputDirectory":"../api-server-temp/as3ApiSDK/src/main/as3/com/wordnik/swagger/",
"libraryHome":"../api-server-temp/as3ApiSDK"
}

Binary file not shown.

View File

@ -0,0 +1,223 @@
package com.wordnik.swagger.common
{
import asaxb.xml.bind.ASAXBContext;
import asaxb.xml.bind.Unmarshaller;
import com.wordnik.swagger.event.ApiClientEvent;
import com.wordnik.swagger.event.Response;
import com.wordnik.swagger.common.ApiUserCredentials;
import flash.events.EventDispatcher;
import flash.utils.Dictionary;
import flash.utils.describeType;
import flash.xml.XMLDocument;
import flash.xml.XMLNode;
import mx.messaging.ChannelSet;
import mx.messaging.channels.HTTPChannel;
import mx.messaging.messages.HTTPRequestMessage;
import mx.rpc.AsyncToken;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
import mx.rpc.xml.SimpleXMLEncoder;
import mx.utils.ObjectUtil;
public class ApiInvoker extends EventDispatcher
{
private var _apiUsageCredentials:ApiUserCredentials;
internal var _apiProxyServerUrl:String = "http://apihost.wordnik.com/";
private var _baseUrl: String = "http://beta.wordnik.com/api/";
internal var _useProxyServer: Boolean = true;
private var _proxyHostName:String = "api.wordnik.com";
private var _apiPath: String = "/v4";
public var _apiEventNotifier:EventDispatcher;
private static const DELETE_DATA_DUMMY:String = "dummyDataRequiredForDeleteOverride";
private static const X_HTTP_OVERRIDE_KEY:String = "X-HTTP-Method-Override";
private static const CONTENT_TYPE_HEADER_KEY:String = "Content-Type";
public function ApiInvoker(apiUsageCredentials: ApiUserCredentials, eventNotifier: EventDispatcher, useProxy: Boolean = true) {
_apiUsageCredentials = apiUsageCredentials;
_useProxyServer = useProxy;
if(_apiUsageCredentials.hostName != null){
_proxyHostName = _apiUsageCredentials.hostName;
}
_apiPath = _apiUsageCredentials.apiPath;
_apiProxyServerUrl = _apiUsageCredentials.apiProxyServerUrl;
_apiEventNotifier = eventNotifier;
}
public function invokeAPI(authToken: String , resourceURL: String, method: String, queryParams: Dictionary, postObject: Object): AsyncToken {
//make the communication
if(_useProxyServer) {
resourceURL = resourceURL = _apiProxyServerUrl + resourceURL;
}
else{
resourceURL = resourceURL = "http://"+ _proxyHostName + _apiPath + resourceURL;
}
var counter: int = 0;
var symbol: String = "&";
var paramValue: Object;
for (var paramName:String in queryParams) {
paramValue = queryParams[paramName];
//var key:String = paramName;
// do stuff
symbol = "&";
if(counter == 0){
symbol = "?";
}
resourceURL = resourceURL + symbol + paramName + "=" + paramValue.toString();
counter++;
}
//create a httpservice and invoke the rest url waiting for response
var requestHeader:Object = new Object();
resourceURL = ApiUrlHelper.appendTokenInfo(resourceURL, requestHeader, _apiUsageCredentials);
var bodyData:String = marshal( postObject);//restRequest.postData;
return doRestCall(resourceURL, onApiRequestResult, onApiRequestFault, method, bodyData, requestHeader, "application/xml");
}
private function doRestCall( url : String, resultFunction : Function, faultFunction : Function = null,
restMethod : String = "GET",
bodyData : Object = null, headers: Object = null, contentType:String = "application/xml" ) : AsyncToken
{
var httpService : HTTPService = new HTTPService( );
if(headers == null){
headers = new Object();
}
httpService.method = restMethod;
if ( restMethod.toUpperCase() != HTTPRequestMessage.GET_METHOD )
{
//httpService.method = HTTPRequestMessage.POST_METHOD; - not required as we're using the proxy
if( bodyData == null )
{
bodyData = new Object();
}
if(restMethod == HTTPRequestMessage.DELETE_METHOD){
headers[X_HTTP_OVERRIDE_KEY]= HTTPRequestMessage.DELETE_METHOD;
bodyData = DELETE_DATA_DUMMY;
}
else{
headers[CONTENT_TYPE_HEADER_KEY]= contentType;
}
}
else
{
//if the request type is GET and content type is xml then the Flex HTTPService converts it to a POST ... yeah
contentType = null;
}
httpService.url = url;
httpService.contentType = contentType;
httpService.resultFormat = "e4x";
httpService.headers = headers;
httpService.addEventListener( ResultEvent.RESULT, resultFunction );
if( faultFunction != null )
{
httpService.addEventListener( FaultEvent.FAULT, faultFunction );
}
if(_useProxyServer){
httpService.useProxy = true;
var channelSet: ChannelSet = new ChannelSet();
var httpChannel: HTTPChannel = new HTTPChannel();
httpChannel.uri = ApiUrlHelper.getProxyUrl(_proxyHostName);
channelSet.addChannel(httpChannel);
httpService.channelSet = channelSet;
}
return httpService.send( bodyData );
}
private function onApiRequestResult(event:ResultEvent):void
{
var completionListener: Function = event.token.completionListener;
var result: Object = event.result;
var resultType: Class = event.token.returnType;
var resultObject:Object;
if(resultType != null) {
var context:ASAXBContext = ASAXBContext.newInstance(resultType);
var unmarshaller:Unmarshaller = context.createUnmarshaller();
var resultXML: XML = new XML(event.result);
resultObject = unmarshaller.unmarshal(resultXML);
}
var response : Response = new Response(true, resultObject);
response.requestId = event.token.requestId;
var successEventType: String = event.token.completionEventType != null ? event.token.completionEventType : ApiClientEvent.SUCCESS_EVENT;
if (_apiEventNotifier != null) { //dispatch event via assigned dispatcher
var successEvent: ApiClientEvent = new ApiClientEvent(successEventType);
successEvent.response = response;
_apiEventNotifier.dispatchEvent(successEvent);
}
}
private function onApiRequestFault(event:FaultEvent):void
{
var completionListener: Function = event.token.completionListener;
if(completionListener != null){
completionListener.call( null, new Response( false, null, event.fault.faultString) );
}
var failureEventType: String = event.token.completionEventType != null ? event.token.completionEventType : ApiClientEvent.FAILURE_EVENT;
if (_apiEventNotifier != null) { //dispatch event via assigned dispatcher
var failureEvent: ApiClientEvent = new ApiClientEvent(failureEventType);
failureEvent.response = new Response( false, null, event.fault.faultString);
_apiEventNotifier.dispatchEvent(failureEvent);
}
}
public function marshal(source:Object):XML
{
var writer:XMLWriter=new XMLWriter();
var objDescriptor:XML=describeType(source);
var property:XML;
var propertyType:String;
var propertyValue:Object;
var qualifiedClassName:String=objDescriptor.@name;
qualifiedClassName=qualifiedClassName.replace("::",".");
writer.xml.setName(qualifiedClassName);
for each(property in objDescriptor.elements("variable")){
propertyValue=source[property.@name];
if (propertyValue!=null){
if (ObjectUtil.isSimple(propertyValue)){
writer.addProperty(property.@name, propertyValue.toString());
}
else {
writer.addProperty(property.@name, marshal(propertyValue).toXMLString());
}
}
}
for each(property in objDescriptor.elements("accessor")){
if (property.@access=="readonly"){
continue;
}
propertyValue=source[property.@name];
if (source[property.@name]!=null){
if (ObjectUtil.isSimple(propertyValue)){
writer.addProperty(property.@name, propertyValue.toString());
}
else {
writer.addProperty(property.@name, marshal(propertyValue).toXMLString());
}
}
}
return writer.xml;
}
}
}

View File

@ -0,0 +1,42 @@
package com.wordnik.swagger.common {
import com.wordnik.swagger.common.ApiUserCredentials;
/**
* @private
* Internal class for the Rest client
*/
internal class ApiUrlHelper {
private static const API_URL_KEY:String = "api_key";
private static const AUTH_TOKEN_URL_KEY:String = "auth_token";
private static const HOST_PROXY_PATH:String = "/v4/messagebroker/restproxy";
private static const HTTP_URL_PREFIX:String = "http://";
internal static function appendTokenInfo(restUrl:String, requestHeader: Object, credentials: ApiUserCredentials): String {
//wordnik credentials presence validated on client initialization and not here
if(restUrl.indexOf("?") == -1){
restUrl += ( "?" + API_URL_KEY + "=" + credentials.apiToken );
}
else{
restUrl += ( "&" + API_URL_KEY + "=" + credentials.apiToken );
}
requestHeader.api_key = credentials.apiToken;
if(credentials.authToken != null && credentials.authToken != ""){
restUrl += ( "&" + AUTH_TOKEN_URL_KEY + "=" + credentials.authToken );
requestHeader.auth_token = credentials.authToken;
}
return restUrl;
}
internal static function getProxyUrl(hostName: String): String{
if (hostName.charAt(hostName.length - 1) == "/") //remove trailing slash
{
hostName = hostName.substring(0, hostName.length - 1);
}
return HTTP_URL_PREFIX + hostName + HOST_PROXY_PATH;
}
}
}

View File

@ -0,0 +1,55 @@
package com.wordnik.swagger.common {
/**
* Wordnik Api account credentials. The info is used to authenticate with the Wordnik API and perform
* account-specific user actions
*/
public class ApiUserCredentials {
/**
* All requests must be signed with your Wordnik API key
*/
public var apiToken:String;
/**
* A valid auth_token which is necessary for certain operations - currently, user accounts and list-related CRUD operations
*/
public var authToken:String;
/**
* The userId which is required for certain operations - currently, get user lists
*/
public var userId:Number;
/**
* The host name for the Wordnik Rest API eg. api.wordnik.com
*/
public var hostName:String;
/**
* The base path to the api resources - used along with the hostname
* eg. /v4
*/
public var apiPath: String;
/**
* If a proxy server has been set up for the services specify the URL here. This value is used when the Api is invoked with
* the value useProxy as true
*/
public var apiProxyServerUrl: String;
/**
* Constructor of ApiUserCredentials
* @param apiToken All requests must be signed with your Wordnik API key
* @param authToken A valid auth_token which is necessary for certain operations - currently, user accounts and list-related CRUD operations
* @param hostName The host name for the Wordnik Rest API eg. api.wordnik.com
* @param userId The userId which is required for certain operations - currently, get user lists
*/
public function ApiUserCredentials(apiToken: String, authToken: String = null, hostName: String = null, userId: Number = -1,
apiPath: String = "", apiProxyServerUrl: String="") {
this.hostName = hostName;
this.apiToken = apiToken;
this.authToken = authToken;
this.userId = userId;
this.apiPath = apiPath;
this.apiProxyServerUrl = apiProxyServerUrl;
}
}
}

View File

@ -0,0 +1,82 @@
package com.wordnik.swagger.common
{
import com.wordnik.swagger.common.ApiUserCredentials;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import mx.utils.UIDUtil;
public class SwaggerApi extends EventDispatcher
{
protected var _apiUsageCredentials:ApiUserCredentials;
protected var _apiEventNotifier:EventDispatcher;
protected var _apiInvoker: ApiInvoker;
protected var apiProxyServerUrl:String = "http://apihost.wordnik.com/";
protected var _baseUrl: String = "http://beta.wordnik.com/api/";
protected var _useProxyServer: Boolean = true;
protected var proxyHostName:String = "api.wordnik.com";
protected static const DELETE_DATA_DUMMY:String = "dummyDataRequiredForDeleteOverride";
protected static const X_HTTP_OVERRIDE_KEY:String = "X-HTTP-Method-Override";
protected static const CONTENT_TYPE_HEADER_KEY:String = "Content-Type";
/**
* Constructor for the api client
* @param apiCredentials Wrapper object for tokens and hostName required towards authentication
* @param eventDispatcher Optional event dispatcher that when provided is used by the SDK to dispatch any Response
*/
public function SwaggerApi(apiCredentials: ApiUserCredentials, eventDispatcher: EventDispatcher = null) {
super();
_apiUsageCredentials = apiCredentials;
_apiEventNotifier = eventDispatcher;
}
public function useProxyServer(value:Boolean, proxyServerUrl: String = null):void {
_useProxyServer = value;
}
protected function getApiInvoker():ApiInvoker {
if(_apiInvoker == null){
if(_apiEventNotifier == null){
_apiEventNotifier = this;
}
_apiInvoker = new ApiInvoker(_apiUsageCredentials, _apiEventNotifier, _useProxyServer);
}
return _apiInvoker;
}
protected function getUniqueId():String {
return UIDUtil.createUID();
}
/**
* Method for returning the path value
* For a string value an empty value is returned if the value is null
* @param value
* @return
*/
protected static function toPathValue(value: Object): String {
if(value is Array){
return arrayToPathValue(value as Array);
}
return value == null ? "" : value.toString();
}
/**
* Method for returning a path value
* For a list of objects a comma separated string is returned
* @param objects
* @return
*/
protected static function arrayToPathValue(objects: Array): String {
var out: String = "";
return objects.join(",");
}
}
}

View File

@ -0,0 +1,28 @@
package com.wordnik.swagger.common
{
public class XMLWriter
{
public var xml:XML;
public function XMLWriter()
{
xml=<obj/>;
}
public function reset():void {
xml=new XML();
}
public function addProperty(propertyName:String, propertyValue:String):XML {
var xmlProperty:XML=<new/>
xmlProperty.setName(propertyName);
xmlProperty.appendChild(propertyValue);
xml.appendChild(xmlProperty);
return xmlProperty;
}
public function addAttribute(propertyName:String, attribute:String, attributeValue:String):void {
xml.elements(propertyName)[0].@[attribute]=attributeValue;
}
}
}

View File

@ -0,0 +1,36 @@
package com.wordnik.swagger.event {
import com.wordnik.swagger.event.Response;
import flash.events.Event;
/**
* Event dispatched by the SDK to communicate success events and failure events.
* If a custom dispatcher has been assigned by the consumer on the WordnikClient then the dispatcher dispatches
* the ApiClientEvent to indicate success or failure of the invocation using the Response
*/
public class ApiClientEvent extends Event{
/**
* Event type to indicate a unsuccessful invocation
*/
public static const FAILURE_EVENT:String = "unsuccesfulInvocation";
/**
* Event type to indicate a successful invocation
*/
public static const SUCCESS_EVENT:String = "successfulInvocation";
/**
* The Response object which contains response info
*/
public var response: Response;
/**
* Any additional info
*/
public var message:String;
public function ApiClientEvent(type:String,bubbles:Boolean = false,cancelable:Boolean = false) {
super(type, bubbles, cancelable);
}
}
}

View File

@ -0,0 +1,52 @@
package com.wordnik.swagger.event {
/**
* Response contains info on the result of a Wordnik API invocation.
* A completion listener will expect this WNResult object as a parameter.
*/
public class Response {
/**
* Indicates whether the invoked operation failed or succeeded
*/
public var isSuccess:Boolean;
/**
* The payload of the succesful operation eg. a Word in a WordRequest
*/
public var payload:Object;
/**
* Error message in case of failure
*/
public var errorMessage:String;
/**
* A request Id that was passed in by the user as a param when invoking the operation
*/
public var requestId:String;
private static const API_ERROR_MSG:String = "Api error response: ";
public function Response(isSuccessful: Boolean, payload: Object = null, errorMessage: String = null, requestId: String = null) {
this.isSuccess = isSuccessful;
this.payload = payload;
this.errorMessage = getFriendlyMessage(errorMessage);
}
private static function getFriendlyMessage(errorMessage: String): String{
var result: String = errorMessage;
if(errorMessage == null)
return null;
var errorCode: String;
var errorCodeArray: Array = errorMessage.match(/(?<=HTTP\/1.1 )[0-9][0-9][0-9]/);
if(errorCodeArray != null && errorCodeArray.length == 1){
errorCode = String(errorCodeArray[0]);
}
var msgArray: Array = errorMessage.match(/(?<=HTTP\/1.1 [0-9][0-9][0-9] )[^]*/);
if(msgArray != null && msgArray.length == 1){
result = API_ERROR_MSG + String(msgArray[0]);
}
return result;
}
}
}

View File

@ -0,0 +1,10 @@
package com.wordnik.swagger.exception
{
public class ApiError extends Error
{
public function ApiError(id:*=0, message:*="")
{
super(message,id);
}
}
}

View File

@ -0,0 +1,34 @@
package com.wordnik.swagger.exception
{
public class ApiErrorCodes
{
/**
* System exception.
*/
public static const SYSTEM_EXCEPTION: Number = 0;
/**
* With Arguments as current key.
*/
public static const API_KEY_NOT_VALID: Number = 1000;
/**
* With arguments as current token value
*/
public static const AUTH_TOKEN_NOT_VALID: Number = 1001;
/**
* With arguments as input JSON and output class anme
*/
public static const ERROR_CONVERTING_JSON_TO_JAVA: Number = 1002;
/**
* With arguments as JAVA class name
*/
public static const ERROR_CONVERTING_JAVA_TO_JSON: Number = 1003;
public static const ERROR_FROM_WEBSERVICE_CALL: Number = 1004;
/**
* With arguments as current API server name
*/
public static const API_SERVER_NOT_VALID: Number = 1005;
}
}

View File

@ -0,0 +1,13 @@
package com.wordnik.swagger.model
{
public class DefinitionWrapper
{
/**
*
*
*
*/
[XmlElements(name="definition", type="com.wordnik.swagger.model.Definition")]
public var definition: Array = new Array();
}
}

View File

@ -0,0 +1,30 @@
package $packageName$;
$imports:{ import |
import $import$;
}$
/**
* $enum.description$
* NOTE: This class is auto generated by the drive code generator program so please do not edit the class manually.
* @author deepak
*
*/
public enum $className$ {
$values: { value | $value.name$($value.value$)};separator=", "$;
final $enumValueType$ value;
$className$($enumValueType$ value) {
this.value = value;
}
public $enumValueType$ getValue() {
return value;
}
@Override public String toString() {
return String.valueOf(this.getValue());
}
};

View File

@ -0,0 +1,29 @@
package $packageName$ {
$imports:{ import |
import $import$;
}$
/**
* $model.description$
* NOTE: This class is auto generated by the drive code generator program so please do not edit the class manually.
* @author deepak
*
*/
public class $className$ extends $extends$ {
$fields:{ field |
/**
* $field.description$
* $if(field.required)$@Required$endif$
* $if(field.allowableValues)$[AllowableValues(value="$field.allowedValuesString$"]$endif$
*/
$if(!field.fieldDefinition.collectionItemType)$
[XmlElement(name="$field.fieldDefinition.name$")]$endif$
$if(field.fieldDefinition.collectionItemType)$
[XmlElements(name="$field.fieldDefinition.name$", type="$field.fieldDefinition.collectionItemType$")]$endif$
public var $field.fieldDefinition.name$: $field.fieldDefinition.returnType$ $field.fieldDefinition.initialization$;$\r$}$
}
}

View File

@ -0,0 +1,111 @@
package $packageName$ {
import $exceptionPackageName$.ApiErrorCodes;
import $exceptionPackageName$.ApiError;
import $modelPackageName$.*;
$imports:{ import |
import $import$;
}$
/**
* NOTE: This class is auto generated by the drive code generator program so please do not edit the class manually.
* @author deepak
*
*/
public class $resource$ extends $extends$ {
/**
* Constructor for the $resource$ api client
* @param apiCredentials Wrapper object for tokens and hostName required towards authentication
* @param eventDispatcher Optional event dispatcher that when provided is used by the SDK to dispatch any Response
*/
public function $resource$(apiCredentials: ApiUserCredentials, eventDispatcher: EventDispatcher = null) {
super(apiCredentials, eventDispatcher);
}
$methods:{ method |
/**
* $method.description$
$method.arguments:{ argument |
* @param $argument.name$ $argument.description$
$if(argument.allowedValues)$
* Allowed values are - $argument.allowedValues$
$endif$}$
*
$if(!method.responseVoid)$
* @return $method.returnValue$ {@link $method.returnClassName$} $endif$
*/
$if(method.hasArguments)$
[MethodArgumentNames(value="$method.argumentNames; separator=", "$")]$endif$
public function $method.name$($method.argumentDefinitions; separator=", "$): String {
$if(method.authToken)$
if(authToken == null || authToken.length == 0) {
throw new ApiError(ApiErrorCodes.AUTH_TOKEN_NOT_VALID);
}$endif$
var requestId: String = getUniqueId();
//parse inputs
var resourcePath: String = "$method.resourcePath$";
resourcePath = resourcePath.replace("{format}","xml");
var method: String = "$method.methodType$";
var queryParams:Dictionary = new Dictionary();
$if(!method.inputModel)$
$method.queryParameters:{ argument |
if( $argument.name$ != null) {
queryParams["$argument.name$"] = toPathValue($argument.name$);
}
}$
$method.pathParameters:{ argument |
if( $argument.name$ != null) {
resourcePath = resourcePath.replace("{$argument.name$}", $argument.name$);
}
}$
$endif$
$if(method.inputModel)$
$method.queryParameters:{ argument |
if( $argument.inputModelClassArgument$ != null && $argument.methodNameFromModelClass$ != null) {
queryParams["$argument.name$"] = $argument.methodNameFromModelClass$;
}
}$
$method.pathParameters:{ argument |
if( $argument.inputModelClassArgument$ != null && $argument.methodNameFromModelClass$ != null) {
resourcePath = resourcePath.replace("{$argument.name$}", $argument.methodNameFromModelClass$);
}
}$
$endif$
//make the API Call
$if(method.postObject)$
$if(method.authToken)$
var token:AsyncToken = getApiInvoker().invokeAPI(authToken, resourcePath, method, queryParams, postData);
$endif$
$if(!method.authToken)$
var token:AsyncToken = getApiInvoker().invokeAPI(null, resourcePath, method, queryParams, postData);
$endif$
$endif$
$if(!method.postObject)$
$if(method.authToken)$
var token:AsyncToken = getApiInvoker().invokeAPI(authToken, resourcePath, method, queryParams, null);
$endif$
$if(!method.authToken)$
var token:AsyncToken = getApiInvoker().invokeAPI(null, resourcePath, method, queryParams, null);
$endif$
$endif$
token.requestId = requestId;
token.completionEventType = "$method.name$";
//create output objects if the response has more than one object
$if(!method.responseVoid)$
token.returnType = $method.returnClassName$;
$endif$
return requestId;
}
}$
}
}

View File

@ -0,0 +1,20 @@
package $packageName$ {
/**
* Maintains the compatible server version against which the drive is written
* @author deepak
*
*/
public class VersionChecker {
private var compatibleVersion: String = "$apiVersion$";
/**
* Gets the version against which the driver code was written
*/
public function getCompatibleVersion(): String {
return compatibleVersion;
}
}
}

View File

@ -28,6 +28,8 @@ public class FieldDefinition {
private String initialization;
private List<String> importDefinitions = new ArrayList<String>();
private String collectionItemType;
public String getReturnType() {
return returnType;
@ -61,5 +63,11 @@ public class FieldDefinition {
return name.substring(0,1).toUpperCase() + name.substring(1);
}
public void setCollectionItemType(String collectionItemType) {
this.collectionItemType = collectionItemType;
}
public String getCollectionItemType() {
return collectionItemType;
}
}

View File

@ -106,7 +106,13 @@ public class LibraryCodeGenerator {
}
generateModelClasses(resources, aTemplateGroup);
generateModelClassesForInput(resources, aTemplateGroup);
generateEnumForAllowedValues(resources, aTemplateGroup);
if(languageConfig.isGenerateHelperEnums()){
generateEnumForAllowedValues(resources, aTemplateGroup);
}
if(languageConfig.isGenerateOutputWrappers()) {
generateOutputWrappers(resources, aTemplateGroup);
}
generateAPIClasses(resources, aTemplateGroup);
}
@ -139,7 +145,7 @@ public class LibraryCodeGenerator {
List<String> imports = new ArrayList<String>();
imports.addAll(this.config.getDefaultModelImports());
for(ModelField param : model.getFields()){
for(String importDef : param.getFieldDefinition(this.getDataTypeMappingProvider()).getImportDefinitions()){
for(String importDef : param.getFieldDefinition(this.getDataTypeMappingProvider(), config, nameGenerator).getImportDefinitions()){
if(!imports.contains(importDef)){
imports.add(importDef);
}
@ -182,7 +188,7 @@ public class LibraryCodeGenerator {
List<String> imports = new ArrayList<String>();
imports.addAll(this.config.getDefaultModelImports());
for(ModelField param : model.getFields()){
for(String importDef : param.getFieldDefinition(this.getDataTypeMappingProvider()).getImportDefinitions()){
for(String importDef : param.getFieldDefinition(this.getDataTypeMappingProvider(), config, nameGenerator).getImportDefinitions()){
if(!imports.contains(importDef)){
imports.add(importDef);
}
@ -265,6 +271,57 @@ public class LibraryCodeGenerator {
}
}
private void generateOutputWrappers(List<Resource> resources, StringTemplateGroup templateGroup) {
List<String> generatedClasses = new ArrayList<String>();
StringTemplate template;
for(Resource resource: resources) {
if(resource.getEndPoints() != null) {
for(Endpoint endpoint : resource.getEndPoints()){
if(endpoint.getOperations() != null) {
for(EndpointOperation operation : endpoint.getOperations()){
ResourceMethod method = operation.generateMethod(endpoint, resource, dataTypeMappingProvider, nameGenerator);
if(codeGenRulesProvider.isModelIgnored( nameGenerator.applyMethodNamingPolicy( method.getReturnClassName() ))){
continue;
}
if(method.getOutputWrapperModel() != null) {
Model model = method.getOutputWrapperModel();
method.setReturnClassName(model.getName());
if(model != null){
if(!generatedClasses.contains(model.getName())) {
List<String> imports = new ArrayList<String>();
imports.addAll(this.config.getDefaultModelImports());
for(ModelField param : model.getFields()){
for(String importDef : param.getFieldDefinition(this.getDataTypeMappingProvider(), config, nameGenerator).getImportDefinitions()){
if(!imports.contains(importDef)){
imports.add(importDef);
}
}
}
template = templateGroup.getInstanceOf(MODEL_OBJECT_TEMPLATE);
template.setAttribute("fields", model.getFields());
template.setAttribute("imports", imports);
template.setAttribute("extends", config.getDefaultModelBaseClass());
template.setAttribute("annotationPackageName", languageConfig.getAnnotationPackageName());
template.setAttribute("className", model.getGenratedClassName());
template.setAttribute(PACKAGE_NAME, config.getModelPackageName());
File aFile = new File(languageConfig.getModelClassLocation()+model.getGenratedClassName()+languageConfig.getClassFileExtension());
writeFile(aFile, template.toString(), "Output wrapper model class");
generatedClasses.add(model.getName());
}
}
}
}
}
}
}
}
}
/**
* Generates one API class for each resource and each end point in the resource is translated as method.
* @param resources
@ -328,7 +385,7 @@ public class LibraryCodeGenerator {
imports.addAll(this.config.getDefaultModelImports());
imports.addAll(this.getDataTypeMappingProvider().getListIncludes());
for(ModelField param : model.getFields()){
for(String importDef : param.getFieldDefinition(this.getDataTypeMappingProvider()).getImportDefinitions()){
for(String importDef : param.getFieldDefinition(this.getDataTypeMappingProvider(), config, nameGenerator).getImportDefinitions()){
if(!imports.contains(importDef)){
imports.add(importDef);
}

View File

@ -53,7 +53,8 @@ public class ResourceMethod {
private boolean postObject;
private Model inputModel;
private Model outputWrapperModel;
public String getTitle() {
return title;
@ -200,4 +201,12 @@ public class ResourceMethod {
}
return false;
}
public void setOutputWrapperModel(Model outputWrapperModel) {
this.outputWrapperModel = outputWrapperModel;
}
public Model getOutputWrapperModel() {
return outputWrapperModel;
}
}

View File

@ -187,4 +187,14 @@ public interface DataTypeMappingProvider {
* @return
*/
public String getGenericType(String type);
/**
* Returns the syntax for defintion of an object of type and name
*
* @param argumentType
* @param argumentName
* @return
*/
public String getArgumentDefinition(String argumentType, String argumentName);
}

View File

@ -41,6 +41,10 @@ public class LanguageConfiguration {
private String annotationPackageName;
private boolean generateHelperEnums = true;
private boolean generateOutputWrappers = false;
public String getClassFileExtension() {
return classFileExtension;
}
@ -109,4 +113,20 @@ public class LanguageConfiguration {
public void setLibraryHome(String libraryHome) {
this.libraryHome = libraryHome;
}
public void setGenerateHelperEnums(boolean generateHelperEnums) {
this.generateHelperEnums = generateHelperEnums;
}
public boolean isGenerateHelperEnums() {
return generateHelperEnums;
}
public void setGenerateOutputWrappers(boolean generateOutputWrappers) {
this.generateOutputWrappers = generateOutputWrappers;
}
public boolean isGenerateOutputWrappers() {
return generateOutputWrappers;
}
}

View File

@ -97,6 +97,18 @@ public interface NamingPolicyProvider {
*/
public String getInputObjectName(String serviceName, String resourcePath);
/**
* Generate the name of the wrapper class used as a wrapper for a list of items returned by a service
*
* Example: get definitions API returns a list of definition objects as the result. This will be wrapped by an
* object. The object's name will be determined by invoking this service.
* eg. DefinitionList for a wrapper of 'definition's
*
* @param wrapperItemName
* @return
*/
public String getOutputWrapperName(String wrapperItemName);
/**
* Generates a name for an enum for the param or field name.
*

View File

@ -0,0 +1,185 @@
package com.wordnik.swagger.codegen.config.as3;
import com.wordnik.swagger.codegen.config.DataTypeMappingProvider;
import com.wordnik.swagger.codegen.config.NamingPolicyProvider;
import com.wordnik.swagger.codegen.config.common.CamelCaseNamingPolicyProvider;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* User: ramesh
* Date: 5/31/11
* Time: 7:03 AM
*/
public class As3DataTypeMappingProvider implements DataTypeMappingProvider {
public static Map<String, String> primitiveValueMap = new HashMap<String, String>();
static{
primitiveValueMap.put("string", "String");
primitiveValueMap.put("String", "String");
primitiveValueMap.put("int", "Number");
primitiveValueMap.put("integer", "int");
primitiveValueMap.put("Integer", "int");
primitiveValueMap.put("boolean", "Boolean");
primitiveValueMap.put("Boolean", "Boolean");
primitiveValueMap.put("long", "Number");
primitiveValueMap.put("Long", "Number");
primitiveValueMap.put("double", "Number");
primitiveValueMap.put("Double", "Number");
primitiveValueMap.put("float", "Number");
primitiveValueMap.put("Float", "Number");
primitiveValueMap.put("Date", "Date");
primitiveValueMap.put("date", "Date");
}
public static Map<String, String> primitiveObjectMap = new HashMap<String, String>();
static{
primitiveObjectMap.put("string", "String");
primitiveObjectMap.put("String", "String");
primitiveObjectMap.put("int", "int");
primitiveObjectMap.put("integer", "int");
primitiveObjectMap.put("Integer", "int");
primitiveObjectMap.put("boolean", "Boolean");
primitiveObjectMap.put("Boolean", "Boolean");
primitiveObjectMap.put("long", "Number");
primitiveObjectMap.put("Long", "Number");
primitiveObjectMap.put("double", "Number");
primitiveObjectMap.put("Double", "Number");
primitiveObjectMap.put("float", "Number");
primitiveObjectMap.put("Float", "Number");
primitiveObjectMap.put("Date", "Date");
primitiveObjectMap.put("date", "Date");
}
private NamingPolicyProvider nameGenerator = new CamelCaseNamingPolicyProvider();
public boolean isPrimitiveType(String type) {
if(type.equalsIgnoreCase("String") || type.equalsIgnoreCase("int") || type.equalsIgnoreCase("integer") || type.equalsIgnoreCase("double") ||
type.equalsIgnoreCase("boolean") || type.equalsIgnoreCase("float")|| type.equalsIgnoreCase("long") || type.equalsIgnoreCase("Number") ){
return true;
}
return false;
}
/**
* If the data type is primitive and it is expecting object structure then return primitive objects
* else return primitive types
* @param type
* @param primitiveObject -- indicates if the object is primitive or not
* @return
*/
public String getObjectType(String type, boolean primitiveObject) {
if(isPrimitiveType(type)){
if(primitiveObject){
return primitiveObjectMap.get(type);
}else{
return primitiveValueMap.get(type);
}
}else{
return nameGenerator.applyClassNamingPolicy(type);
}
}
public String getListReturnTypeSignature(String typeClass) {
return "Array";
}
public String getReturnTypeForVoidMethods() {
return "void";
}
public String getMapReturnTypeSignature(String typeClass) {
return "Object";
}
public String getSetReturnTypeSignature(String typeClass) {
return "Array";
}
public String generateListInitialization(String typeClass) {
return " new Array()";
}
public String generateMapInitialization(String typeClass) {
return " new Object()";
}
public String generateSetInitialization(String typeClass) {
return " new Array()";
}
public List<String> getListIncludes() {
List<String> imports = new ArrayList<String>();
return imports;
}
public List<String> getMapIncludes() {
List<String> imports = new ArrayList<String>();
imports.add("flash.utils.Dictionary");
return imports;
}
public List<String> getSetIncludes() {
List<String> imports = new ArrayList<String>();
return imports; }
public List<String> getDateIncludes() {
List<String> imports = new ArrayList<String>();
return imports;
}
/**
* Gets the short name of the class the class.
* Input can be MAP, LIST or regular string. In case of map or list the class name will be class name
* that map or list is returning.
* @param type
* @return
*/
public String getClassType(String type, boolean primitiveObject) {
String classShortName = "";
if(type.startsWith("List[")){
classShortName = type.substring(5, type.length()-1);
classShortName = getObjectType(classShortName, true);
}else if (type.startsWith("Map[")) {
classShortName = type.substring(4, type.length()-1);
classShortName = getObjectType(classShortName, true);
}else if (type.startsWith("Set[")) {
classShortName = type.substring(4, type.length()-1);
classShortName = getObjectType(classShortName, true);
}else if (type.equals("ok")) {
classShortName = "void";
}else{
classShortName = getObjectType(type, true);
}
return classShortName;
}
public String getArgumentDefinition(String argumentType, String argumentName) {
return argumentName + ": " + argumentType;
}
/**
* Gets the class of the expected return value for a type string. Examples of type Strings are int, User, List[User]
* If the type string is a collection type like a map or list the string value returned would be the class
* that map or list is returning.
*
* @param type
* @return
*/
public String getGenericType(String type) {
if(type.equalsIgnoreCase("void")|| type.equalsIgnoreCase("ok")){
return "void";
}
String classShortName = "";
if(type.startsWith("List[") || type.startsWith("Map[") || type.startsWith("Set[")){
classShortName = "Array";
}else{
classShortName = getObjectType(type, true);
}
return classShortName;
}
}

View File

@ -0,0 +1,85 @@
package com.wordnik.swagger.codegen.config.as3;
import com.wordnik.swagger.codegen.LibraryCodeGenerator;
import com.wordnik.swagger.codegen.config.LanguageConfiguration;
import com.wordnik.swagger.codegen.config.common.CamelCaseNamingPolicyProvider;
import com.wordnik.swagger.codegen.exception.CodeGenerationException;
import com.wordnik.swagger.codegen.util.FileUtil;
import java.io.File;
/**
* User: deepakmichael
* Date: 17/08/11
* Time: 5:02 PM
*/
public class As3LibCodeGen extends LibraryCodeGenerator{
public static void main(String[] args) {
if(args.length < 1){
throw new CodeGenerationException("Invalid number of arguments passed: No command line argument was passed to the program for config json");
}
if(args.length == 1) {
String configPath = args[0];
As3LibCodeGen codeGenerator = new As3LibCodeGen(configPath);
codeGenerator.generateCode();
}
if(args.length == 4) {
String apiServerURL = args[0];
if(!apiServerURL.endsWith("/")){
apiServerURL = apiServerURL + "/";
}
String apiKey = args[1];
String packageName = args[2];
String libraryHome = args[3];
if(libraryHome.endsWith("/")){
libraryHome = libraryHome.substring(0, libraryHome.length()-1);
}
String modelPackageName = packageName+".model";
String apiPackageName = packageName+".api";
String classOutputDir = libraryHome + "/src/main/as3/" + packageName.replace(".","/");
As3LibCodeGen codeGenerator = new As3LibCodeGen(apiServerURL, apiKey, modelPackageName,
apiPackageName, classOutputDir, libraryHome);
codeGenerator.generateCode();
}
}
public As3LibCodeGen(String apiServerURL, String apiKey, String modelPackageName, String apiPackageName,
String classOutputDir, String libraryHome){
super(apiServerURL, apiKey, modelPackageName, apiPackageName, classOutputDir, libraryHome);
this.setDataTypeMappingProvider(new As3DataTypeMappingProvider());
this.setNameGenerator(new As3NamingPolicyProvider());
}
public As3LibCodeGen(String configPath){
super(configPath);
this.setDataTypeMappingProvider(new As3DataTypeMappingProvider());
this.setNameGenerator(new As3NamingPolicyProvider());
}
@Override
protected LanguageConfiguration initializeLangConfig(LanguageConfiguration as3Configuration) {
as3Configuration.setClassFileExtension(".as");
as3Configuration.setTemplateLocation("conf/as3/templates");
as3Configuration.setStructureLocation("conf/as3/structure");
as3Configuration.setExceptionPackageName("com.wordnik.swagger.exception");
as3Configuration.setAnnotationPackageName("com.wordnik.swagger.annotations");
//create ouput directories
FileUtil.createOutputDirectories(as3Configuration.getModelClassLocation(), as3Configuration.getClassFileExtension());
FileUtil.createOutputDirectories(as3Configuration.getResourceClassLocation(), as3Configuration.getClassFileExtension());
/*
FileUtil.clearFolder(as3Configuration.getLibraryHome() + "/src/main/java/com/wordnik/swagger/runtime");
FileUtil.createOutputDirectories(as3Configuration.getLibraryHome() + "/src/main/java/com/wordnik/swagger/runtime", "as");
*/
FileUtil.clearFolder(as3Configuration.getLibraryHome() + "/src/main/as3/com/wordnik/swagger/common");
FileUtil.clearFolder(as3Configuration.getLibraryHome() + "/src/main/as3/com/wordnik/swagger/exception");
FileUtil.clearFolder(as3Configuration.getLibraryHome() + "/src/main/as3/com/wordnik/swagger/event");
FileUtil.copyDirectory(new File(as3Configuration.getStructureLocation()), new File(as3Configuration.getLibraryHome()));
as3Configuration.setGenerateHelperEnums(false);
as3Configuration.setGenerateOutputWrappers(true);
return as3Configuration;
}
}

View File

@ -0,0 +1,25 @@
package com.wordnik.swagger.codegen.config.as3;
import com.wordnik.swagger.codegen.config.common.CamelCaseNamingPolicyProvider;
/**
* User: deepakmichael
* Date: 16/08/11
* Time: 11:01 AM
*/
public class As3NamingPolicyProvider extends CamelCaseNamingPolicyProvider {
/**
* Gets the signature of the method that gets value for give attribute name.
*
* Example: If class name is user and attibute name is email the out in java language will be
* <code>user.getEmail()</code>
*
* @param className
* @param attributeName
* @return
*/
public String createGetterMethodName(String className, String attributeName) {
return className+"."+ attributeName;
}
}

View File

@ -141,6 +141,20 @@ public class CamelCaseNamingPolicyProvider implements NamingPolicyProvider {
return inputobjectName;
}
/**
* Generate the name of the wrapper class used as a wrapper for a list of items returned by a service
* <p/>
* Example: get definitions API returns a list of definition objects as the result. This will be wrapped by an
* object. The object's name will be determined by invoking this service.
* eg. DefinitionList for a wrapper of 'definition's
*
* @param wrapperItemName
* @return
*/
public String getOutputWrapperName(String wrapperItemName) {
return applyClassNamingPolicy(wrapperItemName) + "Wrapper";
}
/**
* Generates a name for an enum for the param or field name.
* <p/>

View File

@ -158,7 +158,18 @@ public class JavaDataTypeMappingProvider implements DataTypeMappingProvider {
return classShortName;
}
/**
/**
* Returns the syntax for defintion of an object of type and name
*
* @param argumentType
* @param argumentName
* @return
*/
public String getArgumentDefinition(String argumentType, String argumentName) {
return argumentType + " " + argumentName;
}
/**
* Gets the class of the expected return value for a type string. Examples of type Strings are int, User, List[User]
* If the type string is a collection type like a map or list the string value returned would be the class
* that map or list is returning.

View File

@ -291,7 +291,7 @@ public class EndpointOperation {
if (method.getArguments() != null && method.getArguments().size() > 0) {
for(MethodArgument arg: method.getArguments()) {
if(!arg.getName().equalsIgnoreCase(FORMAT_PARAM_NAME)){
argumentDefinitions.add(arg.getDataType() + " " + arg.getName());
argumentDefinitions.add( dataTypeMapper.getArgumentDefinition(arg.getDataType(), arg.getName()) );
argumentNames.add(arg.getName());
}
}
@ -306,7 +306,21 @@ public class EndpointOperation {
method.setReturnValue(dataTypeMapper.getClassType(responseClass, false));
method.setReturnClassName(dataTypeMapper.getGenericType(responseClass));
//if this is a list return type
if(method.getReturnClassName().equals(dataTypeMapper.getListReturnTypeSignature(responseClass))){
String returnValueTypeName = method.getReturnValue();
Model outputWrapperModel = new Model();
outputWrapperModel.setName(nameGenerator.getOutputWrapperName(returnValueTypeName));
List<ModelField> fields = new ArrayList<ModelField>();
ModelField aModelField = new ModelField();
aModelField.setName(nameGenerator.applyMethodNamingPolicy(returnValueTypeName));
aModelField.setParamType(responseClass);
fields.add(aModelField);
outputWrapperModel.setFields(fields);
method.setOutputWrapperModel(outputWrapperModel);
//method.setReturnClassName(outputWrapperModel.getName());
}
//get description string for exception
method.setExceptionDescription(calculateExceptionMessage());
}

View File

@ -17,7 +17,9 @@
package com.wordnik.swagger.codegen.resource;
import com.wordnik.swagger.codegen.FieldDefinition;
import com.wordnik.swagger.codegen.config.ApiConfiguration;
import com.wordnik.swagger.codegen.config.DataTypeMappingProvider;
import com.wordnik.swagger.codegen.config.NamingPolicyProvider;
import java.util.ArrayList;
import java.util.List;
@ -170,7 +172,7 @@ public class ModelField {
return fieldDefinition;
}
public FieldDefinition getFieldDefinition(DataTypeMappingProvider dataTypeMapper) {
public FieldDefinition getFieldDefinition(DataTypeMappingProvider dataTypeMapper, ApiConfiguration config, NamingPolicyProvider nameGenerator) {
if(fieldDefinition == null) {
fieldDefinition = new FieldDefinition();
String type = paramType.trim();
@ -180,6 +182,11 @@ public class ModelField {
if(type.startsWith("List[")){
fieldDefinition.getImportDefinitions().addAll(dataTypeMapper.getListIncludes());
String entryType = type.substring(5, type.length()-1);
if (dataTypeMapper.isPrimitiveType(entryType)) {
fieldDefinition.setCollectionItemType(entryType);
} else {
fieldDefinition.setCollectionItemType(config.getModelPackageName() + "." + nameGenerator.applyClassNamingPolicy(entryType));
}
entryType = dataTypeMapper.getClassType(entryType, true);
String returnType = dataTypeMapper.getListReturnTypeSignature(entryType);
fieldDefinition.setReturnType(returnType);