Add support to linking between guides (#9590)

Closes #9575
This commit is contained in:
Stian Thorgersen 2022-01-17 16:41:29 +01:00 committed by GitHub
parent 99e7208f36
commit f80d336276
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 170 additions and 8 deletions

View file

@ -1,6 +1,7 @@
<#import "/templates/guide.adoc" as tmpl>
<#import "/templates/kc.adoc" as kc>
<#import "/templates/options.adoc" as opts>
<#import "/templates/links.adoc" as links>
<@tmpl.guide
title="Relational database setup"
@ -10,6 +11,8 @@
First step is to decide which database vendor you are going to use. Keycloak has support for a number of different vendors.
Take a look at <@links.server id="all-config"/> for more information.
Selecting the database vendor is done at build-time rather than at runtime. To select the database vendor run:
<@kc.build parameters="--db <vendor>"/>

View file

@ -1,5 +1,10 @@
<#list ctx.guides as guide>
:links_server_${guide.id}_name: ${guide.title}
:links_server_${guide.id}_url: #${guide.id}
</#list>
= Keycloak server guide
<#list ctx.serverGuides as guide>
include::${guide}[leveloffset=+1]
<#list ctx.guides as guide>
include::${guide.template}[leveloffset=+1]
</#list>

View file

@ -0,0 +1,3 @@
<#macro server id>
link:{links_server_${id}_url}[{links_server_${id}_name}]
</#macro>

View file

@ -1,25 +1,40 @@
package org.keycloak.guides.maven;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
public class Context {
private File srcDir;
private Options options;
private String[] serverGuides;
private List<Guide> guides;
public Context(File srcDir) {
public Context(File srcDir) throws IOException {
this.srcDir = srcDir;
this.options = new Options();
this.serverGuides = new File(srcDir, "server").list((dir, f) -> f.endsWith(".adoc") && !f.equals("index.adoc"));
this.guides = new LinkedList<>();
GuideParser parser = new GuideParser();
for (File f : new File(srcDir, "server").listFiles((dir, f) -> f.endsWith(".adoc") && !f.equals("index.adoc"))) {
Guide guide = parser.parse(f);
if (guide != null) {
guides.add(guide);
}
}
Collections.sort(guides, Comparator.comparingInt(Guide::getPriority));
}
public Options getOptions() {
return options;
}
public String[] getServerGuides() {
return new File(srcDir, "server").list((dir, f) -> f.endsWith(".adoc") && !f.equals("index.adoc"));
public List<Guide> getGuides() {
return guides;
}
}

View file

@ -0,0 +1,50 @@
package org.keycloak.guides.maven;
public class Guide {
private String template;
private String id;
private String title;
private String summary;
private int priority;
public String getTemplate() {
return template;
}
public void setTemplate(String template) {
this.template = template;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
}

View file

@ -0,0 +1,86 @@
package org.keycloak.guides.maven;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class GuideParser {
private Pattern TEMPLATE_IMPORT_PATTERN = Pattern.compile("<#import \"/templates/guide.adoc\" as (?<importName>[^ ]*)>");
private Pattern GUIDE_ELEMENT_PATTERN = Pattern.compile("(?<key>priority|title|summary)=(\\\"(?<valueString>[^\\\"]*)\\\"|(?<valueInt>[\\d]*))");
/**
* Parses a FreeMarker template to retrieve Guide attributes
* @param file
* @return A Guide instance; or <code>null</code> if not a guide
* @throws IOException
*/
public Guide parse(File file) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(file))) {
String importName = getImportName(br);
String importElement = getGuideElement(br, importName);
if (importElement != null) {
Guide guide = new Guide();
guide.setTemplate(file.getName());
guide.setPriority(999);
guide.setId(file.getName().replaceAll(".adoc", ""));
setAttributes(importElement, guide);
return guide;
}
return null;
}
}
private String getImportName(BufferedReader br) throws IOException {
for (String line = br.readLine(); line != null; line = br.readLine()) {
Matcher templateImportMatcher = TEMPLATE_IMPORT_PATTERN.matcher(line);
if (templateImportMatcher.matches()) {
return templateImportMatcher.group("importName");
}
}
return null;
}
private String getGuideElement(BufferedReader br, String importName) throws IOException {
if (importName != null) {
for (String line = br.readLine(); line != null; line = br.readLine()) {
if (line.contains("<@" + importName + ".guide")) {
StringBuilder sb = new StringBuilder();
sb.append(line.trim());
while (!line.contains(">")) {
line = br.readLine();
sb.append(" " + line.trim());
}
return sb.toString();
}
}
}
return null;
}
private void setAttributes(String importElement, Guide guide) {
Matcher attributeMatcher = GUIDE_ELEMENT_PATTERN.matcher(importElement);
while (attributeMatcher.find()) {
String key = attributeMatcher.group("key");
switch (key) {
case "title":
guide.setTitle(attributeMatcher.group("valueString"));
break;
case "summary":
guide.setSummary(attributeMatcher.group("valueString"));
break;
case "priority":
guide.setPriority(Integer.parseInt(attributeMatcher.group("valueInt")));
}
}
}
}