From f4fc88c6be064d02b29acf19f1834ab8441c3c2e Mon Sep 17 00:00:00 2001 From: Kristof Vrolijkx Date: Tue, 19 Apr 2016 11:02:36 +0200 Subject: [PATCH] #1809 Adding basic angular2 typescript codegen. --- .../TypeScriptAngularClientCodegen.java | 16 +- .../TypescriptAngular2ClientCodegen.java | 81 ++++++++ .../services/io.swagger.codegen.CodegenConfig | 1 + .../typescript-angular2/api.mustache | 88 +++++++++ .../typescript-angular2/model.d.mustache | 17 ++ .../typescript-angular2/model.mustache | 37 ++++ ...peScriptAngular2ClientOptionsProvider.java | 32 +++ .../TypeScriptAngular2ClientOptionsTest.java | 35 ++++ .../TypeScriptAngular2ModelTest.java | 183 ++++++++++++++++++ 9 files changed, 482 insertions(+), 8 deletions(-) create mode 100644 modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypescriptAngular2ClientCodegen.java create mode 100644 modules/swagger-codegen/src/main/resources/typescript-angular2/api.mustache create mode 100644 modules/swagger-codegen/src/main/resources/typescript-angular2/model.d.mustache create mode 100644 modules/swagger-codegen/src/main/resources/typescript-angular2/model.mustache create mode 100644 modules/swagger-codegen/src/test/java/io/swagger/codegen/options/TypeScriptAngular2ClientOptionsProvider.java create mode 100644 modules/swagger-codegen/src/test/java/io/swagger/codegen/typescriptangular2/TypeScriptAngular2ClientOptionsTest.java create mode 100644 modules/swagger-codegen/src/test/java/io/swagger/codegen/typescriptangular2/TypeScriptAngular2ModelTest.java diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptAngularClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptAngularClientCodegen.java index 6237331cf80..670805efafd 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptAngularClientCodegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypeScriptAngularClientCodegen.java @@ -1,8 +1,9 @@ package io.swagger.codegen.languages; -import io.swagger.codegen.SupportingFile; import java.io.File; +import io.swagger.codegen.SupportingFile; + public class TypeScriptAngularClientCodegen extends AbstractTypeScriptClientCodegen { @Override @@ -14,14 +15,13 @@ public class TypeScriptAngularClientCodegen extends AbstractTypeScriptClientCode public String getHelp() { return "Generates a TypeScript AngularJS client library."; } - - @Override - public void processOpts() { - super.processOpts(); - supportingFiles.add(new SupportingFile("api.d.mustache", apiPackage().replace('.', File.separatorChar), "api.d.ts")); - supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); - //supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); + @Override + public void processOpts() { + super.processOpts(); + supportingFiles.add(new SupportingFile("api.d.mustache", apiPackage().replace('.', File.separatorChar), "api.d.ts")); + supportingFiles.add(new SupportingFile("git_push.sh.mustache", "", "git_push.sh")); + //supportingFiles.add(new SupportingFile("gitignore.mustache", "", ".gitignore")); } diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypescriptAngular2ClientCodegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypescriptAngular2ClientCodegen.java new file mode 100644 index 00000000000..676fabc5556 --- /dev/null +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/TypescriptAngular2ClientCodegen.java @@ -0,0 +1,81 @@ +package io.swagger.codegen.languages; + +import java.io.File; + +import io.swagger.codegen.CodegenParameter; +import io.swagger.codegen.SupportingFile; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.FileProperty; +import io.swagger.models.properties.MapProperty; +import io.swagger.models.properties.Property; + +public class TypeScriptAngular2ClientCodegen extends AbstractTypeScriptClientCodegen { + + public TypeScriptAngular2ClientCodegen() { + super(); + this.outputFolder = "generated-code/typescript-angular2"; + + embeddedTemplateDir = templateDir = "typescript-angular2"; + modelTemplateFiles.put("model.mustache", ".ts"); + apiTemplateFiles.put("api.mustache", ".ts"); + typeMapping.put("Date","Date"); + apiPackage = "api"; + modelPackage = "model"; + } + + @Override + public String getName() { + return "TypeScript-Angular2"; + } + + @Override + public String getHelp() { + return "Generates a TypeScript Angular2 client library."; + } + + @Override + public void processOpts() { + super.processOpts(); + supportingFiles.clear(); + supportingFiles.add(new SupportingFile("model.d.mustache", modelPackage().replace('.', File.separatorChar), "model.d.ts")); + } + + @Override + public String getTypeDeclaration(Property p) { + Property inner; + if(p instanceof ArrayProperty) { + ArrayProperty mp1 = (ArrayProperty)p; + inner = mp1.getItems(); + return this.getSwaggerType(p) + "<" + this.getTypeDeclaration(inner) + ">"; + } else if(p instanceof MapProperty) { + MapProperty mp = (MapProperty)p; + inner = mp.getAdditionalProperties(); + return "{ [key: string]: " + this.getTypeDeclaration(inner) + "; }"; + } else { + return p instanceof FileProperty ? "any" : super.getTypeDeclaration(p); + } + } + + @Override + public String getSwaggerType(Property p) { + String swaggerType = super.getSwaggerType(p); + return addModelPrefix(swaggerType); + } + + private String addModelPrefix(String swaggerType) { + String type = null; + if (typeMapping.containsKey(swaggerType)) { + type = typeMapping.get(swaggerType); + if (languageSpecificPrimitives.contains(type)) + return type; + } else + type = "model." + swaggerType; + return type; + } + + @Override + public void postProcessParameter(CodegenParameter parameter) { + super.postProcessParameter(parameter); + parameter.dataType = addModelPrefix(parameter.dataType); + } +} diff --git a/modules/swagger-codegen/src/main/resources/META-INF/services/io.swagger.codegen.CodegenConfig b/modules/swagger-codegen/src/main/resources/META-INF/services/io.swagger.codegen.CodegenConfig index 070b084fdb6..50d5357285d 100644 --- a/modules/swagger-codegen/src/main/resources/META-INF/services/io.swagger.codegen.CodegenConfig +++ b/modules/swagger-codegen/src/main/resources/META-INF/services/io.swagger.codegen.CodegenConfig @@ -34,6 +34,7 @@ io.swagger.codegen.languages.SwaggerGenerator io.swagger.codegen.languages.SwaggerYamlGenerator io.swagger.codegen.languages.SwiftCodegen io.swagger.codegen.languages.TizenClientCodegen +io.swagger.codegen.languages.TypeScriptAngular2ClientCodegen io.swagger.codegen.languages.TypeScriptAngularClientCodegen io.swagger.codegen.languages.TypeScriptNodeClientCodegen io.swagger.codegen.languages.AkkaScalaClientCodegen diff --git a/modules/swagger-codegen/src/main/resources/typescript-angular2/api.mustache b/modules/swagger-codegen/src/main/resources/typescript-angular2/api.mustache new file mode 100644 index 00000000000..5c2d412d5c0 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/typescript-angular2/api.mustache @@ -0,0 +1,88 @@ +import {Http, Headers, RequestOptionsArgs, Response, URLSearchParams} from 'angular2/http'; +import {Injectable} from 'angular2/core'; +import {Observable} from 'rxjs/Observable'; +import * as model from "../model/model.d.ts" +import {AppSettings} from "../../AppSettings" + +/* tslint:disable:no-unused-variable member-ordering */ + +{{#operations}} +'use strict'; + +{{#description}} +/** + * {{&description}} + */ +{{/description}} +@Injectable() +export class {{classname}} { + protected basePath = '{{basePath}}'; + public defaultHeaders : Headers = new Headers(); + + constructor(protected http: Http, appSettigs: AppSettings) { + if (appSettigs && appSettigs.apiBaseUrl) { + this.basePath = appSettigs.apiBaseUrl; + } + } + +{{#operation}} + /** + * {{summary}} + * {{notes}} + {{#allParams}}* @param {{paramName}} {{description}} + {{/allParams}}*/ + public {{nickname}} ({{#allParams}}{{paramName}}{{^required}}?{{/required}}: {{{dataType}}}, {{/allParams}}extraHttpRequestParams?: any ) : Observable<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}{}{{/returnType}}> { + const path = this.basePath + '{{path}}'{{#pathParams}} + .replace('{' + '{{baseName}}' + '}', String({{paramName}})){{/pathParams}}; + + let queryParameters: any = ""; // This should probably be an object in the future + let headerParams = this.defaultHeaders; +{{#hasFormParams}} + let formParams = new URLSearchParams(); + +{{/hasFormParams}} +{{#allParams}} +{{#required}} + // verify required parameter '{{paramName}}' is set + if (!{{paramName}}) { + throw new Error('Missing required parameter {{paramName}} when calling {{nickname}}'); + } +{{/required}} +{{/allParams}} +{{#queryParams}} + if ({{paramName}} !== undefined) { + queryParameters['{{baseName}}'] = {{paramName}}; + } + +{{/queryParams}} +{{#headerParams}} + headerParams.set('{{baseName}}', {{paramName }}); + +{{/headerParams}} +{{#hasFormParams}} + headerParams.set('Content-Type', 'application/x-www-form-urlencoded'); + +{{/hasFormParams}} +{{#formParams}} + formParams['{{baseName}}'] = {{paramName}}; + +{{/formParams}} + let requestOptions: RequestOptionsArgs = { + method: '{{httpMethod}}', + headers: headerParams, + search: queryParameters + }; + {{#bodyParam}} + requestOptions.body = JSOG.stringify({{paramName}}); + {{/bodyParam}} + {{#hasFormParams}} + requestOptions.body = formParams.toString(); + {{/hasFormParams}} + + return this.http.request(path, requestOptions) + .map(response => response.json()); + } + +{{/operation}} +} +{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/typescript-angular2/model.d.mustache b/modules/swagger-codegen/src/main/resources/typescript-angular2/model.d.mustache new file mode 100644 index 00000000000..8973ef01ecd --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/typescript-angular2/model.d.mustache @@ -0,0 +1,17 @@ +{{#models}} +{{#model}} +export { {{{classname}}} } from './{{{ classname }}}'; +{{/model}} +{{/models}} + + +/* +{{#apiInfo}} +{{#apis}} +{{#operations}} +export * from './{{classname}}'; +{{/operations}} +{{/apis}} +{{/apiInfo}} +*/ + diff --git a/modules/swagger-codegen/src/main/resources/typescript-angular2/model.mustache b/modules/swagger-codegen/src/main/resources/typescript-angular2/model.mustache new file mode 100644 index 00000000000..ff36a380e94 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/typescript-angular2/model.mustache @@ -0,0 +1,37 @@ +{{#models}} +{{#model}} +'use strict'; +import * as model from "./model.d.ts" + +{{#description}} +/** + * {{{description}}} + */ +{{/description}} +export interface {{classname}} {{#parent}}extends model.{{{parent}}} {{/parent}}{ +{{#vars}} + +{{#description}} + /** + * {{{description}}} + */ +{{/description}} + + {{name}}?: {{#isEnum}}{{classname}}.{{{datatypeWithEnum}}}{{/isEnum}}{{^isEnum}}{{{datatype}}}{{/isEnum}}; +{{/vars}} +} + +{{#hasEnums}} +export namespace {{classname}} { +{{#vars}} +{{#isEnum}} + + export enum {{datatypeWithEnum}} { {{#allowableValues}}{{#values}} + {{.}} = '{{.}}',{{/values}}{{/allowableValues}} + } +{{/isEnum}} +{{/vars}} +} +{{/hasEnums}} +{{/model}} +{{/models}} diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/TypeScriptAngular2ClientOptionsProvider.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/TypeScriptAngular2ClientOptionsProvider.java new file mode 100644 index 00000000000..c7bddc9f663 --- /dev/null +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/TypeScriptAngular2ClientOptionsProvider.java @@ -0,0 +1,32 @@ +package io.swagger.codegen.options; + +import com.google.common.collect.ImmutableMap; + +import java.util.Map; + +import io.swagger.codegen.CodegenConstants; + +public class TypeScriptAngular2ClientOptionsProvider implements OptionsProvider { + public static final String SORT_PARAMS_VALUE = "false"; + public static final String ENSURE_UNIQUE_PARAMS_VALUE = "true"; + public static final String MODEL_PROPERTY_NAMING_VALUE = "camelCase"; + + @Override + public String getLanguage() { + return "typescript-angular2"; + } + + @Override + public Map createOptions() { + ImmutableMap.Builder builder = new ImmutableMap.Builder(); + return builder.put(CodegenConstants.SORT_PARAMS_BY_REQUIRED_FLAG, SORT_PARAMS_VALUE) + .put(CodegenConstants.ENSURE_UNIQUE_PARAMS, ENSURE_UNIQUE_PARAMS_VALUE) + .put(CodegenConstants.MODEL_PROPERTY_NAMING, MODEL_PROPERTY_NAMING_VALUE) + .build(); + } + + @Override + public boolean isServer() { + return false; + } +} diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/typescriptangular2/TypeScriptAngular2ClientOptionsTest.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/typescriptangular2/TypeScriptAngular2ClientOptionsTest.java new file mode 100644 index 00000000000..4c56a7dfab2 --- /dev/null +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/typescriptangular2/TypeScriptAngular2ClientOptionsTest.java @@ -0,0 +1,35 @@ +package io.swagger.codegen.typescriptangular2; + +import io.swagger.codegen.AbstractOptionsTest; +import io.swagger.codegen.CodegenConfig; +import io.swagger.codegen.languages.TypeScriptAngular2ClientCodegen; +import io.swagger.codegen.options.TypeScriptAngular2ClientOptionsProvider; +import io.swagger.codegen.options.TypeScriptAngularClientOptionsProvider; +import mockit.Expectations; +import mockit.Tested; + +public class TypeScriptAngular2ClientOptionsTest extends AbstractOptionsTest { + + @Tested + private TypeScriptAngular2ClientCodegen clientCodegen; + + public TypeScriptAngular2ClientOptionsTest() { + super(new TypeScriptAngular2ClientOptionsProvider()); + } + + @Override + protected CodegenConfig getCodegenConfig() { + return clientCodegen; + } + + @SuppressWarnings("unused") + @Override + protected void setExpectations() { + new Expectations(clientCodegen) {{ + clientCodegen.setSortParamsByRequiredFlag(Boolean.valueOf(TypeScriptAngularClientOptionsProvider.SORT_PARAMS_VALUE)); + times = 1; + clientCodegen.setModelPropertyNaming(TypeScriptAngularClientOptionsProvider.MODEL_PROPERTY_NAMING_VALUE); + times = 1; + }}; + } +} diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/typescriptangular2/TypeScriptAngular2ModelTest.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/typescriptangular2/TypeScriptAngular2ModelTest.java new file mode 100644 index 00000000000..8f696801202 --- /dev/null +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/typescriptangular2/TypeScriptAngular2ModelTest.java @@ -0,0 +1,183 @@ +package io.swagger.codegen.typescriptangular2; + +import com.google.common.collect.Sets; + +import org.testng.Assert; +import org.testng.annotations.Test; + +import io.swagger.codegen.CodegenModel; +import io.swagger.codegen.CodegenProperty; +import io.swagger.codegen.DefaultCodegen; +import io.swagger.codegen.languages.TypeScriptAngular2ClientCodegen; +import io.swagger.models.ArrayModel; +import io.swagger.models.Model; +import io.swagger.models.ModelImpl; +import io.swagger.models.properties.ArrayProperty; +import io.swagger.models.properties.DateTimeProperty; +import io.swagger.models.properties.LongProperty; +import io.swagger.models.properties.RefProperty; +import io.swagger.models.properties.StringProperty; + +@SuppressWarnings("static-method") +public class TypeScriptAngular2ModelTest { + + @Test(description = "convert a simple TypeScript Angular2 model") + public void simpleModelTest() { + final Model model = new ModelImpl() + .description("a sample model") + .property("id", new LongProperty()) + .property("name", new StringProperty()) + .property("createdAt", new DateTimeProperty()) + .required("id") + .required("name"); + final DefaultCodegen codegen = new TypeScriptAngular2ClientCodegen(); + final CodegenModel cm = codegen.fromModel("sample", model); + + Assert.assertEquals(cm.name, "sample"); + Assert.assertEquals(cm.classname, "Sample"); + Assert.assertEquals(cm.description, "a sample model"); + Assert.assertEquals(cm.vars.size(), 3); + + final CodegenProperty property1 = cm.vars.get(0); + Assert.assertEquals(property1.baseName, "id"); + Assert.assertEquals(property1.datatype, "number"); + Assert.assertEquals(property1.name, "id"); + Assert.assertEquals(property1.defaultValue, "null"); + Assert.assertEquals(property1.baseType, "number"); + Assert.assertTrue(property1.hasMore); + Assert.assertTrue(property1.required); + Assert.assertTrue(property1.isNotContainer); + + final CodegenProperty property2 = cm.vars.get(1); + Assert.assertEquals(property2.baseName, "name"); + Assert.assertEquals(property2.datatype, "string"); + Assert.assertEquals(property2.name, "name"); + Assert.assertEquals(property2.defaultValue, "null"); + Assert.assertEquals(property2.baseType, "string"); + Assert.assertTrue(property2.hasMore); + Assert.assertTrue(property2.required); + Assert.assertTrue(property2.isNotContainer); + + final CodegenProperty property3 = cm.vars.get(2); + Assert.assertEquals(property3.baseName, "createdAt"); + Assert.assertEquals(property3.complexType, null); + Assert.assertEquals(property3.datatype, "Date"); + Assert.assertEquals(property3.name, "createdAt"); + Assert.assertEquals(property3.defaultValue, "null"); + Assert.assertNull(property3.hasMore); + Assert.assertNull(property3.required); + Assert.assertTrue(property3.isNotContainer); + } + + @Test(description = "convert a model with list property") + public void listPropertyTest() { + final Model model = new ModelImpl() + .description("a sample model") + .property("id", new LongProperty()) + .property("urls", new ArrayProperty().items(new StringProperty())) + .required("id"); + final DefaultCodegen codegen = new TypeScriptAngular2ClientCodegen(); + final CodegenModel cm = codegen.fromModel("sample", model); + + Assert.assertEquals(cm.name, "sample"); + Assert.assertEquals(cm.classname, "Sample"); + Assert.assertEquals(cm.description, "a sample model"); + Assert.assertEquals(cm.vars.size(), 2); + + final CodegenProperty property1 = cm.vars.get(0); + Assert.assertEquals(property1.baseName, "id"); + Assert.assertEquals(property1.datatype, "number"); + Assert.assertEquals(property1.name, "id"); + Assert.assertEquals(property1.defaultValue, "null"); + Assert.assertEquals(property1.baseType, "number"); + Assert.assertTrue(property1.hasMore); + Assert.assertTrue(property1.required); + Assert.assertTrue(property1.isNotContainer); + + final CodegenProperty property2 = cm.vars.get(1); + Assert.assertEquals(property2.baseName, "urls"); + Assert.assertEquals(property2.datatype, "Array"); + Assert.assertEquals(property2.name, "urls"); + Assert.assertEquals(property2.baseType, "Array"); + Assert.assertNull(property2.hasMore); + Assert.assertNull(property2.required); + Assert.assertTrue(property2.isContainer); + } + + @Test(description = "convert a model with complex property") + public void complexPropertyTest() { + final Model model = new ModelImpl() + .description("a sample model") + .property("children", new RefProperty("#/definitions/Children")); + final DefaultCodegen codegen = new TypeScriptAngular2ClientCodegen(); + final CodegenModel cm = codegen.fromModel("sample", model); + + Assert.assertEquals(cm.name, "sample"); + Assert.assertEquals(cm.classname, "Sample"); + Assert.assertEquals(cm.description, "a sample model"); + Assert.assertEquals(cm.vars.size(), 1); + + final CodegenProperty property1 = cm.vars.get(0); + Assert.assertEquals(property1.baseName, "children"); + Assert.assertEquals(property1.datatype, "model.Children"); + Assert.assertEquals(property1.name, "children"); + Assert.assertEquals(property1.defaultValue, "null"); + Assert.assertEquals(property1.baseType, "model.Children"); + Assert.assertNull(property1.required); + Assert.assertTrue(property1.isNotContainer); + } + + @Test(description = "convert a model with complex list property") + public void complexListPropertyTest() { + final Model model = new ModelImpl() + .description("a sample model") + .property("children", new ArrayProperty() + .items(new RefProperty("#/definitions/Children"))); + final DefaultCodegen codegen = new TypeScriptAngular2ClientCodegen(); + final CodegenModel cm = codegen.fromModel("sample", model); + + Assert.assertEquals(cm.name, "sample"); + Assert.assertEquals(cm.classname, "Sample"); + Assert.assertEquals(cm.description, "a sample model"); + Assert.assertEquals(cm.vars.size(), 1); + + final CodegenProperty property1 = cm.vars.get(0); + Assert.assertEquals(property1.baseName, "children"); + Assert.assertEquals(property1.complexType, "model.Children"); + Assert.assertEquals(property1.datatype, "Array"); + Assert.assertEquals(property1.name, "children"); + Assert.assertEquals(property1.baseType, "Array"); + Assert.assertNull(property1.required); + Assert.assertTrue(property1.isContainer); + } + + @Test(description = "convert an array model") + public void arrayModelTest() { + final Model model = new ArrayModel() + .description("an array model") + .items(new RefProperty("#/definitions/Children")); + final DefaultCodegen codegen = new TypeScriptAngular2ClientCodegen(); + final CodegenModel cm = codegen.fromModel("sample", model); + + Assert.assertEquals(cm.name, "sample"); + Assert.assertEquals(cm.classname, "Sample"); + Assert.assertEquals(cm.description, "an array model"); + Assert.assertEquals(cm.vars.size(), 0); + } + + @Test(description = "convert a map model") + public void mapModelTest() { + final Model model = new ModelImpl() + .description("a map model") + .additionalProperties(new RefProperty("#/definitions/Children")); + final DefaultCodegen codegen = new TypeScriptAngular2ClientCodegen(); + final CodegenModel cm = codegen.fromModel("sample", model); + + Assert.assertEquals(cm.name, "sample"); + Assert.assertEquals(cm.classname, "Sample"); + Assert.assertEquals(cm.description, "a map model"); + Assert.assertEquals(cm.vars.size(), 0); + Assert.assertEquals(cm.imports.size(), 1); + Assert.assertEquals(Sets.intersection(cm.imports, Sets.newHashSet("model.Children")).size(), 1); + } +}