Skip to content

Commit

Permalink
extract codegen cli as separate module
Browse files Browse the repository at this point in the history
  • Loading branch information
kdabir committed Jul 21, 2020
1 parent 86c66fa commit d825ebb
Show file tree
Hide file tree
Showing 11 changed files with 136 additions and 83 deletions.
38 changes: 28 additions & 10 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,35 @@
name: Test
name: Build the Distribution

on: [push]
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
build:

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v1
- name: Set up JDK
uses: actions/[email protected]
with:
java-version: 11
- name: Build with Gradle
run: ./gradlew test
- name: Checkout code
uses: actions/checkout@v2

- name: Set up JDK 11
uses: actions/setup-java@v1
with:
java-version: 11

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Build with Gradle
run: gradle clean build

- name: Dist
run: gradle cli:distZip

- name: Upload binary
uses: actions/upload-artifact@v2
with:
name: apifi-codegen
path: cli/build/distributions/apifi-codegen.zip
31 changes: 2 additions & 29 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,6 @@ Spec driven HTTP APIs

![Test](https://github.com/medly/apifi/workflows/Test/badge.svg)

## Include in gradle project
1\. Add jitpack repository
```
maven {
url 'https://jitpack.io'
content {
includeGroup "com.github.medly"
}
}
```
2\. Add dependency
```
configurations {
apifi
}
dependencies {
apifi 'com.github.medly:apifi:<version>'
}
```
With CLI

3\. Add gradle task to generate
```
task generate(type: JavaExec) {
classpath = configurations.apifi
main = "apifi.AppKt"
args "${rootProject.rootDir}/<path-to-yml>"
args "${rootProject.rootDir}/<output-path>"
args "<base-package-name>"
}
```
`apifi-codegen -f codegen/petstore.yml -o src/generated/ -p "com.foo.petstore"`
16 changes: 15 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
plugins {
id 'org.jetbrains.kotlin.jvm' version '1.3.72' apply false
id "com.kdabir.mksrc" version "1.1.0"
}

subprojects {
apply plugin: 'org.jetbrains.kotlin.jvm'
apply plugin: "com.kdabir.mksrc"

group = 'com.medly.apifi'

Expand Down Expand Up @@ -46,4 +48,16 @@ project(":codegen") {
implementation 'org.slf4j:slf4j-simple:1.7.30'
implementation 'org.apache.commons:commons-text:1.8'
}
}
}

project(":cli") {
apply plugin: 'application'

applicationName = 'apifi-codegen'
mainClassName = "apifi.cli.ApifiCliKt"

dependencies {
implementation project(":codegen")
implementation "com.github.ajalt:clikt:2.7.1"
}
}
44 changes: 44 additions & 0 deletions cli/src/main/kotlin/apifi/cli/ApifiCli.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package apifi.cli

import apifi.codegen.CodegenIO
import com.github.ajalt.clikt.core.CliktCommand
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.options.required
import com.github.ajalt.clikt.parameters.types.file


/**
* Entry point - The main function
*/
fun main(args: Array<String>) = ApifiCli().main(args)


/**
* Implementation of CLI using Apifi API
*
* Can use env variable to pass in sensitive information
*/
class ApifiCli : CliktCommand( // command name is inferred as apifi-cli
name = "apifi-codegen",
help = """
Generates Kotlin Source files for given Open API Spec file
"""
) {
private val inputFile by option("-f", "--input-file", help = "Open API Spec file that is entry point, this file can refer to another yaml files")
.file(canBeFile = true, canBeDir = false, mustExist = true)
.required()

private val outDir by option("-o", "--out-dir", help = "Output dir where source should be generated")
.file(canBeFile = false, canBeDir = true, mustExist = true)
.required()

private val basePackage by option("-p", "--base-package", help = "package name for generated classes")
.required()


override fun run() {
CodegenIO().execute(inputFile, outDir, basePackage)
}
}


30 changes: 0 additions & 30 deletions codegen/src/main/kotlin/apifi/App.kt

This file was deleted.

2 changes: 1 addition & 1 deletion codegen/src/main/kotlin/apifi/codegen/CodeGenerator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import apifi.parser.models.Spec
import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.TypeSpec

object CodeGenerator {
class CodeGenerator {
fun generate(spec: Spec, basePackageName: String): List<FileSpec> {
val modelFiles: List<FileSpec> = if (spec.models.isNotEmpty()) listOf(ModelFileBuilder.build(spec.models, basePackageName)) else emptyList()
val modelMapping = modelFiles.flatMap { it.members.mapNotNull { m -> (m as TypeSpec).name }.map { name -> name to "${it.packageName}.$name" } }.toMap()
Expand Down
32 changes: 32 additions & 0 deletions codegen/src/main/kotlin/apifi/codegen/Codegen.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package apifi.codegen

import apifi.parser.OpenApiSpecReader
import io.swagger.v3.parser.OpenAPIV3Parser
import java.io.File


class CodegenIO {
private val openAPIV3Parser = OpenAPIV3Parser()
private val openApiSpecReader = OpenApiSpecReader()
private val codeGenerator = CodeGenerator()

fun execute(specFile: File, outputDir: File, basePackageName: String) {
outputDir.mkdirs()

if (!specFile.isFile || !outputDir.isDirectory) {
throw Exception("invalid spec file or output directory")
}

val openApi = openAPIV3Parser.read(specFile.absolutePath)
val spec = openApiSpecReader.read(openApi)
val fileSpecs = codeGenerator.generate(spec, "$basePackageName.${specFile.nameWithoutExtension}")

fileSpecs.forEach { fileSpec ->
val outFileParentDir = File(outputDir, fileSpec.packageName.replace(".", File.separator))
outFileParentDir.mkdirs()
File(outFileParentDir, fileSpec.name).writeText(fileSpec.toString())
}
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import apifi.parser.models.SecurityDefinitionType
import apifi.parser.models.Spec
import io.swagger.v3.oas.models.OpenAPI

object SpecFileParser {
fun parse(openApiSpec: OpenAPI): Spec {
class OpenApiSpecReader {
fun read(openApiSpec: OpenAPI): Spec {
val paths = PathsParser.parse(openApiSpec.paths)
val models = (openApiSpec.components?.schemas?.flatMap { (name, schema) -> ModelParser.modelsFromSchema(name, schema) }
?: emptyList()) + paths.models
val securitySchemes = openApiSpec.components?.securitySchemes?.entries?.map { scheme -> SecurityDefinition(scheme.key, SecurityDefinitionType.fromTypeAndScheme(scheme.value.type, scheme.value.scheme)) } ?: emptyList()
val securityRequirements = openApiSpec.security?.flatMap { it.keys } ?: emptyList()
return Spec(paths.result, models, securityRequirements, securitySchemes)
}
}
}
8 changes: 4 additions & 4 deletions codegen/src/test/kotlin/apifi/codegen/CodeGeneratorTest.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package apifi.codegen

import apifi.parser.SpecFileParser
import apifi.parser.OpenApiSpecReader
import io.kotest.matchers.collections.shouldNotContainInOrder
import io.kotest.matchers.shouldBe
import io.kotest.core.spec.style.DescribeSpec
Expand All @@ -13,8 +13,8 @@ class CodeGeneratorTest : DescribeSpec({
it("should generate files according to spec") {
val file = FileUtils.getFile("src", "test-res", "codegen", "all-paths.yml").readText().trimIndent()
val openApi = OpenAPIV3Parser().readContents(file).openAPI
val spec = SpecFileParser.parse(openApi)
val fileSpecs = CodeGenerator.generate(spec, "com.pets")
val spec = OpenApiSpecReader().read(openApi)
val fileSpecs = CodeGenerator().generate(spec, "com.pets")
fileSpecs.size shouldBe 8

val expectedPetApi = FileUtils.getFile("src", "test-res", "codegen", "expected-pet-api").readText()
Expand All @@ -27,4 +27,4 @@ class CodeGeneratorTest : DescribeSpec({
}
}
}
)
)
8 changes: 4 additions & 4 deletions codegen/src/test/kotlin/apifi/parser/SpecFileParserTest.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package apifi.parser

import io.kotest.matchers.shouldBe
import io.kotest.core.spec.style.DescribeSpec
import io.kotest.matchers.shouldBe
import io.swagger.v3.parser.OpenAPIV3Parser
import org.apache.commons.io.FileUtils

Expand All @@ -12,17 +12,17 @@ class SpecFileParserTest : DescribeSpec({
it("should parse models & security requirements") {
val file = FileUtils.getFile("src", "test-res", "parser", "securityschemes", "with-basic-auth-security-scheme.yml").readText().trimIndent()
val openApi = OpenAPIV3Parser().readContents(file).openAPI
val spec = SpecFileParser.parse(openApi)
val spec = OpenApiSpecReader().read(openApi)
spec.securityRequirements shouldBe listOf("httpBasic")
}

it("should not throw errors when no security scheme present") {
val file = FileUtils.getFile("src", "test-res", "parser", "params", "with-query-params.yml").readText().trimIndent()
val openApi = OpenAPIV3Parser().readContents(file).openAPI
val spec = SpecFileParser.parse(openApi)
val spec = OpenApiSpecReader().read(openApi)
spec.securityRequirements shouldBe emptyList()
spec.models shouldBe emptyList()
}
}

})
})
4 changes: 3 additions & 1 deletion settings.gradle
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
rootProject.name = 'apifi'
include 'codegen'
include 'codegen'
include 'cli'
include 'runtime'

0 comments on commit d825ebb

Please sign in to comment.