micronaut-projects / micronaut-guides

Guides and Tutorials on how to use Micronaut including sample code
https://guides.micronaut.io
Creative Commons Attribution 4.0 International
35 stars 29 forks source link
groovy java kotlin micronaut tutorials

Micronaut Guides

This is the main repository for the Micronaut Guides.

Build the guides

To build all the guides run:

$ ./gradlew build

This will generate all the projects and guides in build/dist and this is what needs to be published to GitHub Pages.

To build a single guide, run the dynamic task created by GuidesPlugin; convert the kabab case guide directory name to lowerCamelCase and add "Build", e.g. to build micronaut-http-client, run

./gradlew micronautHttpClientBuild

Create a new guide

For a high level overview of the Guides Infrastructure, take a look at this blog post.

All the guides leverage Micronaut Starter core to create the projects. The idea is that one guide can generate up to six different projects, one per language (Java, Groovy and Kotlin) and build tool (Gradle and Maven).

Guide structure

All the guides are in the guides directory in separate subdirectories. Inside the directory, the main file is metadata.json that describes the guide. All the fields are declared in GuideMetadata class.

{
  "title": "Micronaut HTTP Client",
  "intro": "Learn how to use Micronaut low-level HTTP Client. Simplify your code with the declarative HTTP client.",
  "authors": ["Sergio del Amo", "Iván López"],
  "tags": ["client", "rx", "flowable", "json-streams"],
  "category": "Getting Started",
  "publicationDate": "2018-07-02",
  "apps": [
    {
      "name": "default",
      "features": ["graalvm", "reactor"]
    }
  ]
}

Besides, the obvious fields that doesn't need any further explanation, the other are:

Inside the specific guide directory there should be a directory per language with the appropriate directory structure. All these files will be copied into the final guide directory after the guide is generated.

micronaut-http-client
├── groovy
│     └── src
│         ├── main
│         │     └── groovy
│         │         └── example
│         │             └── micronaut
│         └── test
│             └── groovy
│                 └── example
│                     └── micronaut
├── java
│     └── src
│         ├── main
│         │     └── java
│         │         └── example
│         │             └── micronaut
│         └── test
│             └── java
│                 └── example
│                     └── micronaut
├── kotlin
│     └── src
│         ├── main
│         │     └── kotlin
│         │         └── example
│         │             └── micronaut
│         └── test
│             └── kotlin
│                 └── example
│                     └── micronaut
└── src
    └── main
        └── resources

For multi-applications guides there needs to be an additional directory with the name of the application declared in metadata.json file:

micronaut-microservices-distributed-tracing-zipkin
├── bookcatalogue
│     ├── groovy
│     │    ...
│     ├── java
│     │    ...
│     └── kotlin
│          ...
├── bookinventory
│     ├── groovy
│     │    ...
│     ├── java
│     │    ...
│     └── kotlin
│          ...
└── bookrecommendation
      ├── groovy
      │    ...
      ├── java
      │    ...
      └── kotlin

Writing the guide

There is only one Asciidoctor file per guide in the root directory of the guide (sibling to metadata.json). This unique file is used to generate all the combinations for the guide (language and build tool) so we need to take that into account when writing the guide. Name the Asciidoctor file the same as the directory, with an "adoc" extension, e.g. micronaut-http-client.adoc for the micronaut-http-client guide directory.

We don't really write a valid Asciidoctor file but our "own" Asciidoctor with custom kind-of-macros. Then during the build process we render the final HTML for the guide in two phases. In the first one we evaluate all of our custom macros and include and generate a new language-build tool version of the guide in src/doc/asciidoc. This directory is excluded from source control and needs to be considered temporary. Then we render the final HTML of the (up to) six guides from that generated and valid Asciidoctor file.

Placeholders

You can use the following placeholders while writing a guide:

Common snippets

We have small pieces of text that are used in different guides. To avoid the duplication we have common snippets in the src/docs/common directory. For example the file common-header-top.adoc:

= @guideTitle@

@guideIntro@

Authors: @authors@

Micronaut Version: @micronaut@

Will render the title, description, authors and version of all the guides. The variables defined between @ signs will be evaluated and replaced during the first stage of the asciidoctor render. For example, for the Micronaut HTTP Client guide, the previous common snippet will generate:

// Start: common-header-top.adoc
= Micronaut HTTP Client

Learn how to use Micronaut low-level HTTP Client. Simplify your code with the declarative HTTP client.

Authors: Sergio del Amo, Iván López

Micronaut Version: 3.2.7

// End: common-header-top.adoc

Custom macros

There are a number of custom macros available to make it easy writing a single asciidoctor file for all the guides and include the necessary source files, resources,... This is really important because when we include a source code snippet the base directory will change for every language the guide is written.

The following snippet from the HTTP Client guide:

source:GithubConfiguration[]

Will generate the following Asciidoctor depending on the language of the guide:

[source,java]
.src/main/java/example/micronaut/GithubConfiguration.java
----
include::{sourceDir}/micronaut-http-client-gradle-java/src/main/java/example/micronaut/GithubConfiguration.java[]
----

As you can see, the macro takes care of the directories (src/main/java vs src/main/groovy vs src/main/kotlin) and the file extension.

Following this same approach there are macros like:

In all the cases it is possible to pass additional parameters to the macros to customise them. For example, to extract a custom tag from a snippet, we can do resource:application.yml[tag=githubconfig]. Look for usages of those macros in the guides directory to find more examples.

Special custom blocks

There are also special custom blocks to exclude some code to be included in the generated guide based on some condition. This is useful when explaining something specific of the build tool (like how to run the tests with Gradle or Maven) or to exclude something depending on the language (for example do not render the GraalVM section in Groovy guides, as Groovy is not compatible with GraalVM).

Example:

:exclude-for-languages:kotlin
<2> The Micronaut framework will not load the bean unless configuration properties are set.
:exclude-for-languages:

:exclude-for-languages:java,groovy
<2> Kotlin doesn't support runtime repeatable annotations (see https://youtrack.jetbrains.com/issue/KT-12794[KT-12794]. We use a custom condition to enable the bean where appropriate.
:exclude-for-languages:

For Java and Groovy guides the first block will be included. For Kotlin guide, the second block will be included.

Example for build tool:

:exclude-for-build:maven

Now start the application. Execute the `./gradlew run` command, which will start the application on port 8080.

:exclude-for-build:

:exclude-for-build:gradle

Now start the application. Execute the `./mvnw mn:run` command, which will start the application on port 8080.

:exclude-for-build:

For a Gradle guide, the first block will be included. For a Maven guide, the second one will be included.

As before, look for usages of the macro in the guides directory for more examples.

New Guide Template

To create a new guide use the following template as the base asciidoc file:

common:header.adoc[]

common:requirements.adoc[]

common:completesolution.adoc[]

common:create-app.adoc[]

TODO: Describe the user step by step how to write the app. Use includes to reference real code: 

Example of a Controller

source:HelloController[]

Example of a Test

test:HelloControllerTest[]

common:testApp.adoc[]

common:runapp.adoc[]

common:graal-with-plugins.adoc[]

:exclude-for-languages:groovy

TODO describe how you consume the endpoints exposed by the native executable with curl

:exclude-for-languages:

TODO Use the generic next step 

common:next.adoc[]

TODO or a personalised guide for the guide:

== Next steps

TODO: link to the documentation modules you used in the guide

Testing the guide

When working on a new guide, generate it as explained before. The guide will be available in the build/dist directory and the applications will be in the build/code directory. You can open any directory in build/code directly in your IDE to make any changes but keep in mind copying the code back to the appropriate directory.

In the build/code directory a file test.sh is created to run all the tests for the guides generated. Run it locally to make sure it passes before submitting a new pull request.

You can run this test with a gradle task

./gradlew :____RunTestScript

where ____ is the camel-case name of your guide. eg:

./gradlew micronautFlywayRunTestScript

to run all the tests for the micronaut-flyway guide.

Upgrade Micronaut version

When a new Micronaut version is released, update the version.txt file in the root directory. Submit a new pull request and if the build passes, merge it. A few minutes later all the guides will be upgraded to the new version.

Deployment

Guides are published to gh-pages following the same branch structure as Micronaut Core:

GitHub Actions

There are two main jobs: