diff --git a/docs/guides/pom.xml b/docs/guides/pom.xml new file mode 100644 index 0000000000..2e43edfcee --- /dev/null +++ b/docs/guides/pom.xml @@ -0,0 +1,88 @@ + + + + keycloak-docs-parent + org.keycloak + 16.0.0-SNAPSHOT + ../pom.xml + + + 4.0.0 + + Keycloak Guides + keycloak-guides + Keycloak Guides + + + + org.keycloak + keycloak-guides-maven-plugin + 16.0.0-SNAPSHOT + + + + + + + org.keycloak + keycloak-guides-maven-plugin + + + generate-asciidoc + + keycloak-guide + + + + + + org.asciidoctor + asciidoctor-maven-plugin + 1.5.5 + + + asciidoc-to-html + generate-resources + + process-asciidoc + + + ${basedir}/target/generated-guides/server + index.adoc + html5 + coderay + + + ./ + left + left + font + true + + - + true + + + + + + + + + \ No newline at end of file diff --git a/docs/guides/src/main/server/all-config.adoc b/docs/guides/src/main/server/all-config.adoc new file mode 100644 index 0000000000..3420f9fa58 --- /dev/null +++ b/docs/guides/src/main/server/all-config.adoc @@ -0,0 +1,27 @@ +<#import "/templates/guide.adoc" as template> + +<@template.guide +title="All configuration" +summary="All the configuration you will ever need and want"> + +<#list ctx.options.categories as category> +<#assign categoryOptions=ctx.options.getValues(category)> +<#if categoryOptions?has_content> +== ${category.heading} +|=== +|Key|CLI|ENV|Description|Default|Values +<#list categoryOptions as option> +|${option.key} +|${option.keyCli} +|${option.keyEnv} +|${option.description} +|${option.defaultValue!} +|${option.expectedValues?join(", ")} +<#if option?has_next> + + + +|=== + + + diff --git a/docs/guides/src/main/server/db.adoc b/docs/guides/src/main/server/db.adoc new file mode 100644 index 0000000000..ccc60bfa03 --- /dev/null +++ b/docs/guides/src/main/server/db.adoc @@ -0,0 +1,23 @@ +<#import "/templates/guide.adoc" as tmpl> +<#import "/templates/kc.adoc" as kc> +<#import "/templates/options.adoc" as opts> + +<@tmpl.guide + title="Relational database setup" + summary="Understand how to configure different relational databases for Keycloak" + includedOptions="db db.* hostname"> + +First step is to decide which database vendor you are going to use. Keycloak has support for a number of different vendors. + +Selecting the database vendor is done at build-time rather than at runtime. To select the database vendor run: + +<@kc.build parameters="--db "/> + +Valid options for database vendors include: + +<@opts.expectedValues option="db"/> + +Once configured you can easily connect to the database with: + +<@kc.start parameters="--db-host --db-schema --db-user --db-password "/> + \ No newline at end of file diff --git a/docs/guides/src/main/server/index.adoc b/docs/guides/src/main/server/index.adoc new file mode 100644 index 0000000000..cf8571a861 --- /dev/null +++ b/docs/guides/src/main/server/index.adoc @@ -0,0 +1,5 @@ += Keycloak server guide + +<#list ctx.serverGuides as guide> +include::${guide}[leveloffset=+1] + diff --git a/docs/guides/src/main/templates/guide.adoc b/docs/guides/src/main/templates/guide.adoc new file mode 100644 index 0000000000..ced8c9e9aa --- /dev/null +++ b/docs/guides/src/main/templates/guide.adoc @@ -0,0 +1,30 @@ +<#macro guide title summary includedOptions=""> +:title: ${title} +:summary: ${summary} + +[[${ctx.getAnchor(title)}]] += {title} + +{summary} + +<#nested> + +<#if includedOptions?has_content> +== Relevant options + +|=== +|Key|CLI|ENV|Description|Default|Values +<#list ctx.options.getOptions(includedOptions) as option> +|${option.key} +|${option.keyCli} +|${option.keyEnv} +|${option.description} +|${option.defaultValue!} +|${option.expectedValues?join(", ")} +<#if option?has_next> + + + +|=== + + \ No newline at end of file diff --git a/docs/guides/src/main/templates/kc.adoc b/docs/guides/src/main/templates/kc.adoc new file mode 100644 index 0000000000..7b905d2d92 --- /dev/null +++ b/docs/guides/src/main/templates/kc.adoc @@ -0,0 +1,13 @@ +<#macro build parameters> +[source,bash] +---- +bin/kc.[sh|bat] build ${parameters} +---- + + +<#macro start parameters> +[source,bash] +---- +bin/kc.[sh|bat] start ${parameters} +---- + \ No newline at end of file diff --git a/docs/guides/src/main/templates/options.adoc b/docs/guides/src/main/templates/options.adoc new file mode 100644 index 0000000000..e14a8f6eb8 --- /dev/null +++ b/docs/guides/src/main/templates/options.adoc @@ -0,0 +1,5 @@ +<#macro expectedValues option> +<#list ctx.options.getOption(option).expectedValues as expectedValue> +* ${expectedValue} + + \ No newline at end of file diff --git a/docs/guides/src/test/java/org/keycloak/guides/DocsBuildDebugUtil.java b/docs/guides/src/test/java/org/keycloak/guides/DocsBuildDebugUtil.java new file mode 100644 index 0000000000..108c2a2c65 --- /dev/null +++ b/docs/guides/src/test/java/org/keycloak/guides/DocsBuildDebugUtil.java @@ -0,0 +1,22 @@ +package org.keycloak.guides; + +import freemarker.template.TemplateException; +import org.keycloak.guides.maven.GuideBuilder; + +import java.io.File; +import java.io.IOException; + +public class DocsBuildDebugUtil { + + public static void main(String[] args) throws IOException, TemplateException { + String userDir = System.getProperty("user.dir"); + File usrDir = new File(System.getProperty("user.dir")); + File srcDir = usrDir.toPath().resolve("docs/guides/src/main").toFile(); + File targetDir = usrDir.toPath().resolve("target/generated-guides-tests").toFile(); + targetDir.mkdirs(); + GuideBuilder builder = new GuideBuilder(srcDir, targetDir, null); + builder.server(); + System.out.println("Guides generated to: " + targetDir.getAbsolutePath().toString()); + } + +} diff --git a/docs/maven-plugin/pom.xml b/docs/maven-plugin/pom.xml new file mode 100644 index 0000000000..1cc8897bbd --- /dev/null +++ b/docs/maven-plugin/pom.xml @@ -0,0 +1,86 @@ + + + + + keycloak-docs-parent + org.keycloak + 16.0.0-SNAPSHOT + ../pom.xml + + + 4.0.0 + + Keycloak Guides Maven Plugin + keycloak-guides-maven-plugin + Keycloak Guides Maven Plugin + maven-plugin + + + + + io.quarkus + quarkus-bom + ${quarkus.version} + pom + import + + + + + + org.apache.maven + maven-plugin-api + 3.6.3 + + + org.apache.maven.plugin-tools + maven-plugin-annotations + 3.6.0 + provided + + + org.apache.maven + maven-project + 2.2.1 + + + org.keycloak + keycloak-quarkus-server + + + * + * + + + + + io.quarkus + quarkus-core + + + io.smallrye.config + smallrye-config + + + org.freemarker + freemarker + + + + \ No newline at end of file diff --git a/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/Context.java b/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/Context.java new file mode 100644 index 0000000000..9b3b248ca3 --- /dev/null +++ b/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/Context.java @@ -0,0 +1,29 @@ +package org.keycloak.guides.maven; + +import java.io.File; + +public class Context { + + private File srcDir; + private Options options; + private String[] serverGuides; + + public Context(File srcDir) { + this.srcDir = srcDir; + this.options = new Options(); + this.serverGuides = new File(srcDir, "server").list((dir, f) -> f.endsWith(".adoc") && !f.equals("index.adoc")); + } + + public String getAnchor(String title) { + return title.toLowerCase().replace(' ', '_'); + } + + public Options getOptions() { + return options; + } + + public String[] getServerGuides() { + return new File(srcDir, "server").list((dir, f) -> f.endsWith(".adoc") && !f.equals("index.adoc")); + } + +} diff --git a/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/FreeMarker.java b/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/FreeMarker.java new file mode 100644 index 0000000000..25e127a298 --- /dev/null +++ b/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/FreeMarker.java @@ -0,0 +1,44 @@ +package org.keycloak.guides.maven; + +import freemarker.template.Configuration; +import freemarker.template.Template; +import freemarker.template.TemplateException; +import freemarker.template.TemplateExceptionHandler; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.Map; + +public class FreeMarker { + + private File targetDir; + private Map attributes; + private Configuration configuration; + + public FreeMarker(File srcDir, File targetDir, Map attributes) throws IOException { + this.targetDir = targetDir; + this.attributes = attributes; + + configuration = new Configuration(Configuration.VERSION_2_3_31); + configuration.setDirectoryForTemplateLoading(srcDir); + configuration.setDefaultEncoding("UTF-8"); + configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER); + configuration.setLogTemplateExceptions(false); + } + + public void template(String template) throws IOException, TemplateException { + Template t = configuration.getTemplate(template); + File out = targetDir.toPath().resolve(template).toFile(); + + File parent = out.getParentFile(); + if (!parent.isDirectory()) { + parent.mkdir(); + } + + Writer w = new FileWriter(out); + t.process(attributes, w); + } + +} diff --git a/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/GuideBuilder.java b/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/GuideBuilder.java new file mode 100644 index 0000000000..3f11967dbf --- /dev/null +++ b/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/GuideBuilder.java @@ -0,0 +1,43 @@ +package org.keycloak.guides.maven; + +import freemarker.template.TemplateException; +import org.apache.maven.plugin.logging.Log; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class GuideBuilder { + + private final FreeMarker freeMarker; + private final File srcDir; + private final File targetDir; + private final Log log; + + public GuideBuilder(File srcDir, File targetDir, Log log) throws IOException { + this.srcDir = srcDir; + this.targetDir = targetDir; + this.log = log; + + Map globalAttributes = new HashMap<>(); + globalAttributes.put("ctx", new Context(srcDir)); + + this.freeMarker = new FreeMarker(srcDir, targetDir, globalAttributes); + } + + public void server() throws TemplateException, IOException { + File serverGuidesDir = new File(srcDir, "server"); + if (!serverGuidesDir.isDirectory()) { + serverGuidesDir.mkdir(); + } + + for (String t : serverGuidesDir.list((dir, name) -> name.endsWith(".adoc"))) { + freeMarker.template("server/" + t); + if (log != null) { + log.info("Templated: server/" + t); + } + } + } + +} diff --git a/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/GuideMojo.java b/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/GuideMojo.java new file mode 100644 index 0000000000..649fd6effc --- /dev/null +++ b/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/GuideMojo.java @@ -0,0 +1,43 @@ +package org.keycloak.guides.maven; + +import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugin.MojoExecutionException; +import org.apache.maven.plugin.MojoFailureException; +import org.apache.maven.plugin.logging.Log; +import org.apache.maven.plugins.annotations.LifecyclePhase; +import org.apache.maven.plugins.annotations.Mojo; +import org.apache.maven.plugins.annotations.Parameter; + +import java.io.File; + +@Mojo(name = "keycloak-guide", defaultPhase = LifecyclePhase.GENERATE_SOURCES) +public class GuideMojo extends AbstractMojo { + + @Parameter(property = "project.build.sourceDirectory") + private String sourceDir; + + @Parameter(property = "project.build.directory") + private String targetDir; + + @Override + public void execute() throws MojoFailureException { + try { + Log log = getLog(); + File srcDir = new File(sourceDir).getParentFile(); + File targetDir = new File(this.targetDir, "generated-guides"); + if (!targetDir.isDirectory()) { + targetDir.mkdirs(); + } + + log.info("Guide dir: " + srcDir.getAbsolutePath()); + log.info("Target dir: " + targetDir.getAbsolutePath()); + + GuideBuilder g = new GuideBuilder(srcDir, targetDir, log); + g.server(); + } catch (Exception e) { + e.printStackTrace(); + throw new MojoFailureException("Failed to generated asciidoc files", e); + } + } + +} diff --git a/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/Options.java b/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/Options.java new file mode 100644 index 0000000000..34d3524530 --- /dev/null +++ b/docs/maven-plugin/src/main/java/org/keycloak/guides/maven/Options.java @@ -0,0 +1,91 @@ +package org.keycloak.guides.maven; + +import org.keycloak.quarkus.runtime.configuration.mappers.ConfigCategory; +import org.keycloak.quarkus.runtime.configuration.mappers.PropertyMappers; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +public class Options { + + private final Map options; + + public Options() { + options = PropertyMappers.getMappers().stream() + .filter(m -> !m.isHidden()) + .map(m -> new Option(m.getFrom(), m.getCategory(), m.isBuildTime(), m.getDescription(), m.getDefaultValue(), m.getExpectedValues())) + .collect(Collectors.toMap(Option::getKey, o -> o, (o1, o2) -> o1)); // Need to ignore duplicate keys?? + } + + public ConfigCategory[] getCategories() { + return ConfigCategory.values(); + } + + public Collection