Skip to content

Commit

Permalink
Merge pull request #32 from Yelp/fix-31-brackets-in-param-names
Browse files Browse the repository at this point in the history
Fix for square brackets in the operation name
  • Loading branch information
cortinico committed Jun 4, 2019
2 parents cf332b2 + d5b95f1 commit e607915
Show file tree
Hide file tree
Showing 19 changed files with 660 additions and 4 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ You can find some **examples** in this repository to help you set up your genera

* [samples/kotlin-android](/samples/kotlin-android) Contains an example of an Android Library configured with a `build.gradle.kts` file, using Kotlin as scripting language.

* [samples/junit-tests](/samples/junit-tests) This sample contains specs used to test edge cases and scenarios that have been reported in the issue tracker or that are worth testing.

## How the generated code will look like

[Here](/SAMPLES.md) you can find some examples of how the generated code will look like in your project.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
@file:Suppress("Unused")

object PublishingVersions {
const val PLUGIN_VERSION = "1.0.0"
const val PLUGIN_VERSION = "1.1.0-SNAPSHOT"
const val PLUGIN_GROUP = "com.yelp.codegen"
const val PLUGIN_ARTIFACT = "plugin"
}
11 changes: 11 additions & 0 deletions plugin/src/main/java/com/yelp/codegen/KotlinGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -425,4 +425,15 @@ class KotlinGenerator : SharedCodegen() {
// Override the swagger version with the one provided from command line.
swagger.info.version = additionalProperties[SPEC_VERSION] as String
}

/**
* Function used to sanitize the name for operation generations.
* The superclass is not providing the correct string pattern (See #31).
*
* Here we override the provided pattern to include also square brackets in during the
* parameter name generation.
*/
override fun removeNonNameElementToCamelCase(name: String?): String {
return super.removeNonNameElementToCamelCase(name, "[-_:;#\\[\\]]")
}
}
10 changes: 10 additions & 0 deletions plugin/src/test/java/com/yelp/codegen/KotlinGeneratorTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,14 @@ class KotlinGeneratorTest {
assert(KotlinGenerator().toModelName("model with dot.s") == "ModelWithDotS")
assert(KotlinGenerator().toModelName("model with userscore_s") == "ModelWithUserscoreS")
}

@Test
fun removeNonNameElementToCamelCase_withSquareBrackets() {
assert(KotlinGenerator().removeNonNameElementToCamelCase("type[]") == "type")
assert(KotlinGenerator().removeNonNameElementToCamelCase("type[value]") == "typeValue")
assert(KotlinGenerator().removeNonNameElementToCamelCase("type[") == "type")
assert(KotlinGenerator().removeNonNameElementToCamelCase("type]") == "type")
assert(KotlinGenerator().removeNonNameElementToCamelCase("[type]") == "type")
assert(KotlinGenerator().removeNonNameElementToCamelCase("[type]key") == "typeKey")
}
}
65 changes: 65 additions & 0 deletions samples/junit-tests/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
buildscript {
repositories {
mavenLocal()
gradlePluginPortal()
google()
mavenCentral()
jcenter()
}

dependencies {
classpath "com.android.tools.build:gradle:3.2.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.21"
classpath "com.yelp.codegen:plugin:1.1.0-SNAPSHOT"
}
}

apply plugin: "com.android.library"
apply plugin: "kotlin-android"
apply plugin: "com.yelp.codegen.plugin"

android {
compileSdkVersion = 28
defaultConfig {
minSdkVersion 21
targetSdkVersion 28
versionCode = 1
versionName = "1.0"
}
}

dependencies {
// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.21"
implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.21"

// Moshi
implementation "com.squareup.moshi:moshi:1.8.0"
implementation "com.squareup.moshi:moshi-adapters:1.8.0"
implementation "com.squareup.moshi:moshi-kotlin:1.8.0"
implementation "com.squareup.retrofit2:converter-moshi:2.5.0"
implementation "com.squareup.retrofit2:adapter-rxjava2:2.5.0"

// Date Support
implementation "com.jakewharton.threetenabp:threetenabp:1.1.1"

// RxJava
implementation "io.reactivex.rxjava2:rxjava:2.2.4"
implementation "io.reactivex.rxjava2:rxandroid:2.1.0"

// Testing Dependencies
testImplementation "junit:junit:4.12"
testImplementation "com.squareup.okhttp3:mockwebserver:3.12.3"
}

generateSwagger {
platform = "kotlin"
packageName = "com.yelp.codegen.generatecodesamples"
specName = "junittests"
inputFile = file("./junit_tests_specs.json")
outputDir = file("./src/main/java/")
}

repositories {
mavenCentral()
}
61 changes: 61 additions & 0 deletions samples/junit-tests/junit_tests_specs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"definitions": {},
"info": {
"description": "This spec is used to have JUnit Tests to check the generated code.",
"title": "JUnit Tests",
"version": "1.0.0"
},
"paths": {
"/brackets/in/parameter/name": {
"get": {
"description": "Make sure that brackets in parameter name are treated properly",
"operationId": "getBracketsInParameterName",
"parameters": [
{
"in": "query",
"name": "page",
"required": false,
"type": "string"
},
{
"in": "query",
"name": "page[]",
"required": false,
"type": "string"
},
{
"in": "query",
"name": "datePosted[before]",
"required": false,
"type": "string"
},
{
"in": "query",
"name": "datePosted[strictly_before]",
"required": false,
"type": "string"
},
{
"in": "query",
"name": "datePosted[after]",
"required": false,
"type": "string"
},
{
"in": "query",
"name": "datePosted[strictly_after]",
"required": false,
"type": "string"
}
],
"responses": {
"200": {
"description": "successful operation"
}
},
"summary": "Test brackets in parameter name"
}
}
},
"swagger": "2.0"
}
2 changes: 2 additions & 0 deletions samples/junit-tests/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yelp.samplelibrary"/>
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* NOTE: This class is auto generated by the Swagger Gradle Codegen for the following API: JUnit Tests
*
* More info on this tool is available on https://github.com/Yelp/swagger-gradle-codegen
*/

package com.yelp.codegen.generatecodesamples.apis

import io.reactivex.Completable
import retrofit2.http.GET
import retrofit2.http.Headers

@JvmSuppressWildcards
interface DefaultApi {
/**
* Test brackets in parameter name
* Make sure that brackets in parameter name are treated properly
* The endpoint is owned by junittests service owner
* @param page (optional)
* @param page2 (optional)
* @param datePostedBefore (optional)
* @param datePostedStrictlyBefore (optional)
* @param datePostedAfter (optional)
* @param datePostedStrictlyAfter (optional)
*/
@Headers(
"X-Operation-ID: getBracketsInParameterName"
)

@GET("/brackets/in/parameter/name")
fun getBracketsInParameterName(
@retrofit2.http.Query("page") page: String?,
@retrofit2.http.Query("page[]") page2: String?,
@retrofit2.http.Query("datePosted[before]") datePostedBefore: String?,
@retrofit2.http.Query("datePosted[strictly_before]") datePostedStrictlyBefore: String?,
@retrofit2.http.Query("datePosted[after]") datePostedAfter: String?,
@retrofit2.http.Query("datePosted[strictly_after]") datePostedStrictlyAfter: String?
): Completable
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.yelp.codegen.generatecodesamples.tools

import retrofit2.Converter
import retrofit2.Retrofit
import java.lang.reflect.Type

internal class CollectionFormatConverterFactory : Converter.Factory() {

override fun stringConverter(type: Type, annotations: Array<out Annotation>, retrofit: Retrofit): Converter<*, String>? {
val rawType = getRawType(type)
if (rawType == String::class.java || rawType == List::class.java)
annotations.forEach {
when (it) {
is CSV -> return CollectionFormatConverter(",")
is SSV -> return CollectionFormatConverter(" ")
is TSV -> return CollectionFormatConverter("\t")
is PIPES -> return CollectionFormatConverter("|")
}
}
return null
}

private class CollectionFormatConverter(private val separator: String) : Converter<Any, String> {
override fun convert(value: Any): String {
when (value) {
is String -> return value
is List<*> -> return value.joinToString(separator)
}
throw RuntimeException("Unsupported type")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.yelp.codegen.generatecodesamples.tools

@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class CSV

@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class SSV

@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class TSV

@Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
@Retention(AnnotationRetention.RUNTIME)
annotation class PIPES
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.yelp.codegen.generatecodesamples.tools

import com.squareup.moshi.Json
import retrofit2.Converter
import retrofit2.Retrofit
import java.lang.reflect.Type

internal class EnumToValueConverterFactory : Converter.Factory() {

private val enumConverter = EnumToValueConverter()

override fun stringConverter(type: Type, annotations: Array<out Annotation>, retrofit: Retrofit): Converter<*, String>? {
return if (type is Class<*> && type.isEnum) {
enumConverter
} else {
null
}
}

internal class EnumToValueConverter : Converter<Any, String> {
override fun convert(enum: Any): String? {
val enumName = (enum as Enum<*>).name
val jsonAnnotation: Json? = enum.javaClass.getField(enumName).getAnnotation(Json::class.java)

// Checking if the Enum is annotated with @Json to get the name.
// If not, fallback to enum default (.toString())
return if (jsonAnnotation != null) {
jsonAnnotation.name
} else {
enum.toString()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.yelp.codegen.generatecodesamples.tools

import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import retrofit2.Converter
import retrofit2.converter.moshi.MoshiConverterFactory

object GeneratedCodeConverters {
private val moshi = Moshi.Builder()
.add(XNullableAdapterFactory())
.add(KotlinJsonAdapterFactory())
.add(TypesAdapterFactory())
.build()

/**
* Creates everything needed for retrofit to make it work with the client lib, including a
* [Moshi] instance. If you want to use your own instance of moshi, use
* converterFactory(moshi) instead, and add [XNullableAdapterFactory], [KotlinJsonAdapterFactory] and
* [TypesAdapterFactory] to your moshi builder (in a similar way how we are instantiating the `moshi` field here).
*/
@JvmStatic
fun converterFactory(): Converter.Factory {
return WrapperConverterFactory(
CollectionFormatConverterFactory(),
EnumToValueConverterFactory(),
MoshiConverterFactory.create(moshi)
)
}

@JvmStatic
fun converterFactory(moshi: Moshi): Converter.Factory {
return WrapperConverterFactory(
CollectionFormatConverterFactory(),
EnumToValueConverterFactory(),
MoshiConverterFactory.create(moshi)
)
}
}
Loading

0 comments on commit e607915

Please sign in to comment.