How to create CLI plugin from scratch

Prerequisites

First of all, you need to install CUBA CLI in your system.
Also, the JDK 10 installation required.

Develop your own plugin

We will use CUBA CLI Plugin Generator to create a new CUBA CLI plugin. Install this plugin as it is described below:

  1. Download the plugin.
  2. Copy .jar file into ~/.haulmont/cli/plugins/ directory.
  3. Open a terminal and start CUBA CLI using cuba-cli command.

It will print loaded plugins:

cuba:~/$ cuba-cli
Loaded com.haulmont.cli.plugingenerator.PluginGeneratorPlugin

After that, run the create-plugin command. CLI will ask you questions about the project. If a question provides a default answer, you can press enter to accept it. You should fill the following parameters:

  • Plugin project name – the project name. Enter the plugin name.
  • Group – the Maven project group
  • Plugin root package – the root package of Java classes
  • Plugin class name – This class will be our plugin entry point. Must end with Plugin postfix.
  • Plugin java module name – the Java module name

image

Import project into IDE:

  1. Open IntelliJ IDEA
  2. File -> Open, navigate to project directory, choose build.gradle, press OK.
  3. In Gradle settings choose Use gradle 'wrapper' task configuration.
  4. Press OK.

When the project is loaded open the plugin class to add new commands.
In our example, we will create a command that will add a new localization to the project.

In the beginning, we add the method that will register our plugin command.

@Subscribe
fun onInit(event: InitPluginEvent) {
    event.commandsRegistry {
        command("add-locale", AddLocale())
    }
}

First of all, there is no sense, if command invoking not in CUBA project directory. So add project existence check.

override fun preExecute() = checkProjectExistence()

Insert the rest of the code that checks the existing locales and allows users to add a new locale name and code to the project.

override fun run() {
    val foundProperties = listOf(
            projectStructure.getModule(ModuleStructure.WEB_MODULE).rootPackageDirectory.resolve("web-app.properties"),
            projectStructure.getModule(ModuleStructure.CORE_MODULE).rootPackageDirectory.resolve("app.properties")
    ).filter {
        Files.exists(it)
    }.map {
        Properties(it)
    }
    // Fing the existing locales in the project
    val foundLocales = foundProperties.flatMap {
        it["cuba.availableLocales"]?.split(";")?.map { localeString ->
            val nameCodeSplit = localeString.split("|")
            return@map if (nameCodeSplit.size == 2) {
                Locale(nameCodeSplit[0], nameCodeSplit[1])
            } else null
        }?.filterNotNull() ?: listOf()
    }.toSet()

    if (foundProperties.isEmpty())
        fail("Unable to find appropriate .properties files")

    val existingCodes = foundLocales.map { it.code }.toSet()
    val existingNames = foundLocales.map { it.name }.toSet()

    if (foundLocales.isNotEmpty()) {
        printWriter.println("There are following locales are found:")
        for (locale in foundLocales) {
            printWriter.println("${locale.name} - ${locale.code}")
        }
    }
    // New locales creating
    val newLocales = Prompts.create {
        repeating("locales", "Add new locale?") {
            question("code", "New locale code") {
                validate {
                    val promptedCodes = (answers["locales"] as List<Answers>).mapNotNull { it["code"] as String? }
                    if (value in existingCodes || value in promptedCodes)
                        fail("The code already exists")

                    checkRegex("[a-z]+", "Locale code must contain only lower case latin letters")
                }
            }

            question("name", "New locale name") {
                validate {
                    val promptedNames = (answers["locales"] as List<Answers>).mapNotNull { it["name"] as String? }
                    if (value in existingNames || value in promptedNames)
                        fail("The name already exists")

                    checkRegex("[A-Za-z]+", "Locale name must contain only latin letters")
                }
            }
        }
    }.ask().let {
        it["locales"] as List<Answers>
    }.map {
        Locale(it["name"] as String, it["code"] as String)
    }
    // Add newly created locales to the project properties
    if (newLocales.isNotEmpty()) {
        val localesStringToAppend = newLocales.joinToString(separator = ";") { locale -> "${locale.name}|${locale.code}" }

        for (properties in foundProperties) {
            properties["cuba.availableLocales"] = (properties["cuba.availableLocales"]?.let { "$it;" }
                    ?: "") + localesStringToAppend
            properties.save()
        }
    }
}

data class Locale(val name: String, val code: String)

In terminal execute ./gradlew installPlugin. Now, cli will load your plugin on startup. Let’s test it.

  1. In terminal execute cuba-cli.
  2. Run create-app. Fill the parameters.
  3. Run add-locale.
  4. Open the app.properties and web-app.properties file in your project
  5. Check, that the new localization is added.

This is a short example of the CLI plugin. For more advanced examples, please refer to the #cuba-cli tag on Github.

3 Likes