Mogen - Converts Kotlin models to other languages

Overview

Mogen logo

Mogen is a small library that converts Kotlin's models to other programming languages:

  • Dart 2.12+,
  • OpenApi 3.0 YML (generates schemas node),
  • Swift,
  • TypeScript.

Motivation

In elevup we usually write backends in Kotlin. Unfortunately, frontends are whole other story. So the main goal was to find a way to transfer the knowledge efficiently, and we went for OpenApi. But rewriting each diff in models to OpenApi was more-less pain and literally no one wanted to do it.

Thanks to Mogen we were able to take all Kotlin models and convert them to OpenApi format. We felt that it was not enough cause all frontend devs had to rewrite them once again. So eventually we added convertors to more languages.

At this moment we generate frontend API models from backend models.

Features

Supported features:

  • primitives - conversion from Kotlin to target language,
  • homogenous iterables - select collections and arrays are converted,
  • null-safety,
  • enums - including custom serialisation name,
  • classes - all public properties are transformed,
  • nested classes,
  • type aliases - only trivial cases, for example typealias UserId = Long.

Unsupported features:

  • maps,
  • generics,
  • inheritance,
  • abstract classes,
  • packages (= output is supposed to be printed into one file).

Have a look at sample/src/main where in kotlin folder are source files and in resources folder you can find generated output.

How to use

First you need to gather all classes that need to be transformed. Simple create list of them.

val classes: List<KClass<*>> = listOf(Foo::class, Bar::class)

Generate definitions

Depending on desired output pick a generator:

  • DartGenerator ... Dart 2.12+
  • OpenApiGenerator ... OpenApi 3.0
  • SwiftGenerator ... Swift
  • TypeScriptGenerator ... TypeScript
val generator: CachedGenerator = TypeScriptGenerator()
    .appendClass(SomeClass::class)
    .appendClasses(listOf(Foo::class, Bar::class))

// Access generated definitions
val classes = generator.generatedClasses
val enums = generator.generatedEnums
val types = generator.generatedTypeAliases

Type aliases

When adding classes Mogen automatically detects presence of type aliases. However, in some cases additional mapping is required. Imagine you want to serialise LocalDateTime. In some implementations it will become String (ISO representation), in some Long (UNIX timestamp). This conversion is out of the scope of this library but your frontend models must be compatible. In order to do so, you must define target language type.

For example in TypeScript date is represented by Date. To map it correctly, use appendTypealias method:

TypeScriptGenerator()
    .appendClass(User::class)
    .appendTypealias(Typealias(localClass = LocalDateTime::class, name = "Date"))

Input (Kotlin):

typealias UserId = Long

data class User(
    val id: UserId,
    val birthday: LocalDateTime,
)

Output (TypeScript):

export type LocalDateTime = Date
export type UserId = number // copied automatically

export interface User {
    birthday: LocalDateTime; // Kotlin's class name is kept, type is created
    id: UserId;
}

Indents

Everyone uses different indentation rules. To make it easier for you Mogen lets you configure basic indentation unit. To adjust default indentation pass your own Indentation in generator's constructor. You can create your own instance or use one of pre-defined indents:

  • GenericIndents - designed for Dart, Swift and TypeScript,
  • OpenApiIndents - designed for OpenApi.
TypeScriptGenerator(indents = GenericIndents(indent = "\t")) // For 'tabs' crew
OpenApiGenerator(indents = OpenApiIndents(indent = "  ")) // Fore 'spaces' crew

Pretty print

In case you do not want to deal with formatting use Printer to convert generated definitions to single String Library comes with bundled:

  • GenericPrinter that deals well with Dart, Swift and TypeScript,
  • OpenApiPrinter that is designed for OpenApi.
val allDefinitions: String = generator.print(GenericPrinter)

Annotation processors

Sometimes it's useful to keep some metadata. Imagine you have just deprecated some property in backed, and you want to let others know that they should migrate their code. In that case you want to use DeprecatedAnnotationProcessor! Annotation processors are passed in generator's constructor.

Deprecation

TypeScriptGenerator(annotationProcessors = listOf(DeprecatedAnnotationProcessor())) // <- pass them here
       .appendClass(User::class)
       .appendTypeAlias(Typealias(localClass = LocalDateTime::class, name = "Date"))
       .print(GenericPrinter)
       .let { println(it) }

Input (Kotlin):

data class User(
    val id: Long,
    @Deprecated("Not time-zone bulletproof, calculate it on your own")
    val age: Int,
    val birthday: LocalDateTime,
)

Output (TypeScript):

export type LocalDateTime = Date

export interface User {
  /**
   * @deprecated Not time-zone bulletproof, calculate it on your own
   */
  age: number;
  birthday: LocalDateTime;
  id: number;
}

JavaX annotations

For validations like min, max, etc. on server; during conversion it will append comment where validation constraints are listed.

Input (Kotlin):

data class User(
    val id: Long,
    @field:Size(min = 1, max = 100)
    val firstName: String,
)

Output (TypeScript):

export interface User {
  /**
   * min: 1
   * max: 100
   */
  firstName: string;
  id: number;
}

Jackson annotations

Handles @JsonProperty and @JsonValue annotations that can modify name of serialised property.

Input (Kotlin):

data class User(
    @JsonProperty("user_id")
    val id: Long,
    @JsonProperty("tea")
    val favouriteTea: Tea? = null,
) {
    enum class Tea {
        @JsonProperty("g") GREEN, 
        @JsonProperty("b") BLACK,
    }
}

Output (TypeScript):

export enum UserTea {
  GREEN = 'g',
  BLACK = 'b',
}

export interface User {
    tea: UserTea | null;
    user_id: number;
}

How to collect all models

If all models are in one package then Reflections library can be helpful! Then you can use this code to get all classes:

val sourcePackage = "com.foo.bar" // Enter correct value
val reflections = Reflections(
    ConfigurationBuilder()
        .filterInputsBy(FilterBuilder().includePackage(sourcePackage))
        .setUrls(ClasspathHelper.forPackage(sourcePackage))
        .setScanners(SubTypesScanner(false))
)

val typeList = reflections.getSubTypesOf(Object::class.java) + reflections.getSubTypesOf(Enum::class.java)
val classes = typeList.map { c -> c.kotlin }.distinct()

License

Copyright 2021 elevup

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

   https://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.
You might also like...
A somewhat copy past of Jetbrain's code from the kotlin plugin repo to make it humanly possible to test Intellij IDEA kotlin plugins that work on kotlin

A somewhat copy past of Jetbrain's code from the kotlin plugin repo to make it humanly possible to test Intellij IDEA kotlin plugins that work on kotlin

Real life Kotlin Multiplatform project with an iOS application developed in Swift with SwiftUI, an Android application developed in Kotlin with Jetpack Compose and a backed in Kotlin hosted on AppEngine.

Conferences4Hall Real life Kotlin Multiplatform project with an iOS application developed in Swift with SwiftUI, an Android application developed in K

Android + Kotlin + Github Actions + ktlint + Detekt + Gradle Kotlin DSL + buildSrc = ❤️

kotlin-android-template 🤖 A simple Github template that lets you create an Android/Kotlin project and be up and running in a few seconds. This templa

LifecycleMvp 1.2 0.0 Kotlin  is MVP architecture implementation with Android Architecture Components and Kotlin language features
LifecycleMvp 1.2 0.0 Kotlin is MVP architecture implementation with Android Architecture Components and Kotlin language features

MinSDK 14+ Download Gradle Add to project level build.gradle allprojects { repositories { ... maven { url 'https://jitpack.io' }

Opinionated Redux-like implementation backed by Kotlin Coroutines and Kotlin Multiplatform Mobile

CoRed CoRed is Redux-like implementation that maintains the benefits of Redux's core idea without the boilerplate. No more action types, action creato

👋 A common toolkit (utils) ⚒️ built to help you further reduce Kotlin boilerplate code and improve development efficiency. Do you think 'kotlin-stdlib' or 'android-ktx' is not sweet enough? You need this! 🍭

Toolkit [ 🚧 Work in progress ⛏ 👷 🔧️ 🚧 ] Snapshot version: repositories { maven("https://s01.oss.sonatype.org/content/repositories/snapshots") }

An app architecture for Kotlin/Native on Android/iOS. Use Kotlin Multiplatform Mobile.
An app architecture for Kotlin/Native on Android/iOS. Use Kotlin Multiplatform Mobile.

An app architecture for Kotlin/Native on Android/iOS. Use Kotlin Multiplatform Mobile. 项目架构主要分为原生系统层、Android/iOS业务SDK层、KMM SDK层、KMM业务逻辑SDK层、iOS sdkfra

Provides Kotlin libs and some features for building Kotlin plugins
Provides Kotlin libs and some features for building Kotlin plugins

Kotlin Plugin Provides Kotlin libs and some features for building awesome Kotlin plugins. Can be used instead of CreeperFace's KotlinLib (don't use to

Notes-App-Kotlin - Notes App Built Using Kotlin
Notes-App-Kotlin - Notes App Built Using Kotlin

Notes-App-Kotlin Splash Screen Home Page Adding New Notes Filter Feature Search

This program will read from your android application string.xml file and generate translated strings.xml files in your preferred languages using google sheet.

Localize your application content This program will read from your application string.xml file and generate translated strings.xml files in your prefe

DhiWise 4 Jul 29, 2022
RecyclerView Adapter Library with different models and different layouts as convenient as possible.

RecyclerView Presenter Convenience library to handle different view types with different presenters in a single RecyclerView. How to install repositor

Jan Rabe 86 Dec 26, 2022
Clean MVVM with eliminating the usage of context from view models by introducing hilt for DI and sealed classes for displaying Errors in views using shared flows (one time event), and Stateflow for data

Clean ViewModel with Sealed Classes Following are the purposes of this repo Showing how you can remove the need of context in ViewModels. I. By using

Kashif Mehmood 22 Oct 26, 2022
Simplify mutating "immutable" state models

Mutekt (Pronunciation: /mjuːˈteɪt/, 'k' is silent) "Simplify mutating "immutable" state models" Generates mutable models from immutable model definiti

Shreyas Patil 179 Nov 30, 2022
Built with Jetpack compose, multi modules MVVM clean architecture, coroutines + flow, dependency injection, jetpack navigation and other jetpack components

RickAndMortyCompose - Work in progress A simple app using Jetpack compose, clean architecture, multi modules, coroutines + flows, dependency injection

Daniel Waiguru 9 Jul 13, 2022
Double Open license classification for OSS Review Toolkit (ORT) and other uses.

Double Open Policy Configuration This repository is used to maintain the license classification (license-classifications.yml) created by Double Open.

Double Open 8 Nov 7, 2022
To illustrate the clean architecture and modularisation with other components.

CleanNews A news app that provides data from mediastack API using clean architecture, kotlin coroutines, architectural pattern (MVI) with Mavericks. .

Yves Kalume 4 Feb 13, 2022
Com.hhvvg.anytext - An application provides features to modify any TextView in any other applications

AnyText What's this This application provides features to modify any TextView in

null 6 Dec 2, 2022
A personal project made using Jetpack Compose, Clean-MVVM architecture and other jetpack libraries

A basic CRUD for recording your personal credit and debit transactions. Made using Jetpack Compose, Clean-MVVM and other Jetpack libraries, incorporated with Unit Tests.

Shoaib Ahmed 3 Dec 6, 2022
Run Kotlin/JS libraries in Kotlin/JVM and Kotlin/Native programs

Zipline This library streamlines using Kotlin/JS libraries from Kotlin/JVM and Kotlin/Native programs. It makes it possible to do continuous deploymen

Cash App 1.5k Dec 30, 2022