forked from gscokart/jbehave-core
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
JBEHAVE-994: Export Stories from Confluence
- Loading branch information
Thomas Hug
authored and
Thomas Hug
committed
Feb 25, 2014
1 parent
d86d539
commit 9b36294
Showing
10 changed files
with
599 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
155 changes: 155 additions & 0 deletions
155
jbehave-rest/src/main/java/org/jbehave/core/io/rest/confluence/Confluence.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
package org.jbehave.core.io.rest.confluence; | ||
|
||
import static java.text.MessageFormat.format; | ||
|
||
import java.util.List; | ||
|
||
import org.jbehave.core.io.rest.RESTClient; | ||
|
||
import com.thoughtworks.xstream.XStream; | ||
|
||
public class Confluence { | ||
|
||
private static final String SEARCH_PAGE = "{0}/rest/prototype/1/search/name?query={2}&type=page&spaceKey={1}"; | ||
private static final String EXPAND_PAGE = "{0}?expand=children"; | ||
private static final String REGULAR_PAGE = "{0}"; | ||
|
||
private final RESTClient client; | ||
|
||
public Confluence(RESTClient client) { | ||
this.client = client; | ||
} | ||
|
||
public Page loadRootPage(String baseUrl, String spaceKey, String pageName) { | ||
String searchResult = client.get(format(SEARCH_PAGE, baseUrl, spaceKey, pageName)); | ||
XStream parse = configureXStream(); | ||
Results results = (Results) parse.fromXML(searchResult); | ||
return results.getGroup().getResult(); | ||
} | ||
|
||
public Page loadPage(String pageUrl, boolean expanded) { | ||
String pattern = expanded ? EXPAND_PAGE : REGULAR_PAGE; | ||
String content = client.get(format(pattern, pageUrl)); | ||
XStream parse = configureXStream(); | ||
Page page = (Page) parse.fromXML(content); | ||
return page; | ||
} | ||
|
||
protected XStream configureXStream() { | ||
XStream stream = new XStream(); | ||
stream.addImplicitCollection(Page.class, "link"); | ||
stream.alias("results", Results.class); | ||
stream.alias("result", Page.class); | ||
stream.alias("content", Page.class); | ||
stream.alias("link", Link.class); | ||
stream.useAttributeFor(Link.class, "rel"); | ||
stream.useAttributeFor(Link.class, "href"); | ||
stream.ignoreUnknownElements(); | ||
return stream; | ||
} | ||
|
||
public static class Results { | ||
|
||
private Group group; | ||
|
||
public Group getGroup() { | ||
return group; | ||
} | ||
|
||
public void setGroup(Group group) { | ||
this.group = group; | ||
} | ||
|
||
} | ||
|
||
public static class Group { | ||
|
||
private Page result; | ||
|
||
public Page getResult() { | ||
return result; | ||
} | ||
|
||
public void setResult(Page result) { | ||
this.result = result; | ||
} | ||
|
||
} | ||
|
||
public static class Page { | ||
|
||
private List<Link> link; | ||
private String title; | ||
private String body; | ||
private List<Page> children; | ||
|
||
public String getSelfReference() { | ||
for (Link candidate : link) { | ||
if ("self".equals(candidate.getRel())) { | ||
return candidate.getHref(); | ||
} | ||
} | ||
throw new RuntimeException("Page does not contain self-reference"); | ||
} | ||
|
||
public boolean hasChildren() { | ||
return children != null; | ||
} | ||
|
||
public List<Link> getLink() { | ||
return link; | ||
} | ||
|
||
public void setLink(List<Link> link) { | ||
this.link = link; | ||
} | ||
|
||
public String getTitle() { | ||
return title; | ||
} | ||
|
||
public void setTitle(String title) { | ||
this.title = title; | ||
} | ||
|
||
public List<Page> getChildren() { | ||
return children; | ||
} | ||
|
||
public void setChildren(List<Page> children) { | ||
this.children = children; | ||
} | ||
|
||
public String getBody() { | ||
return body; | ||
} | ||
|
||
public void setBody(String body) { | ||
this.body = body; | ||
} | ||
|
||
} | ||
|
||
public static class Link { | ||
|
||
private String rel; | ||
private String href; | ||
|
||
public String getRel() { | ||
return rel; | ||
} | ||
|
||
public void setRel(String rel) { | ||
this.rel = rel; | ||
} | ||
|
||
public String getHref() { | ||
return href; | ||
} | ||
|
||
public void setHref(String href) { | ||
this.href = href; | ||
} | ||
|
||
} | ||
} |
77 changes: 77 additions & 0 deletions
77
jbehave-rest/src/main/java/org/jbehave/core/io/rest/confluence/IndexFromConfluence.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package org.jbehave.core.io.rest.confluence; | ||
|
||
import java.util.HashMap; | ||
import java.util.Map; | ||
import java.util.regex.Pattern; | ||
|
||
import org.jbehave.core.io.rest.RESTClient; | ||
import org.jbehave.core.io.rest.RESTClient.Type; | ||
import org.jbehave.core.io.rest.Resource; | ||
import org.jbehave.core.io.rest.ResourceIndexer; | ||
import org.jbehave.core.io.rest.confluence.Confluence.Page; | ||
|
||
public class IndexFromConfluence implements ResourceIndexer { | ||
|
||
private static final String DISPLAY = "/display/"; | ||
|
||
private final Confluence confluence; | ||
|
||
public IndexFromConfluence() { | ||
this(null, null); | ||
} | ||
|
||
public IndexFromConfluence(String username, String password) { | ||
this(new RESTClient(Type.XML, username, password)); | ||
} | ||
|
||
public IndexFromConfluence(RESTClient client) { | ||
confluence = new Confluence(client); | ||
} | ||
|
||
public Map<String, Resource> indexResources(String rootURI) { | ||
return indexResources(rootURI, null); | ||
} | ||
|
||
public Map<String, Resource> indexResources(String rootURI, String rootPath, String syntax, String includes) { | ||
return indexResources(rootURI, includes); | ||
} | ||
|
||
protected Map<String, Resource> indexResources(String rootURI, String includePattern) { | ||
if (rootURI == null || !rootURI.contains(DISPLAY)) { | ||
throw new RuntimeException("Root URI is not in correct format: " + rootURI); | ||
} | ||
String[] split = rootURI.split(DISPLAY); | ||
String baseUrl = split[0]; | ||
if (split.length == 1) { | ||
throw new RuntimeException("URI does not contain space and page: " + rootURI); | ||
} | ||
String[] searchTerms = split[1].split("/"); | ||
if (split.length != 2) { | ||
throw new RuntimeException("URI does not contain space and page: " + rootURI); | ||
} | ||
return createResourceMap(baseUrl, searchTerms[0], searchTerms[1], includePattern); | ||
} | ||
|
||
private Map<String, Resource> createResourceMap(String baseUrl, String spaceKey, String pageName, String pattern) { | ||
Map<String, Resource> result = new HashMap<String, Resource>(); | ||
Page rootPage = confluence.loadRootPage(baseUrl, spaceKey, pageName); | ||
addPage(result, rootPage.getSelfReference(), pattern); | ||
return result; | ||
} | ||
|
||
private void addPage(Map<String, Resource> result, String href, String pattern) { | ||
Page page = confluence.loadPage(href, true); | ||
Resource resource = new Resource(page.getSelfReference(), page.getTitle()); | ||
resource.setContent(page.getBody()); | ||
if (pattern == null || | ||
(pattern != null && Pattern.matches(pattern, page.getTitle()))) { | ||
result.put(page.getTitle(), resource); | ||
} | ||
if (page.hasChildren()) { | ||
for (Page child : page.getChildren()) { | ||
addPage(result, child.getSelfReference(), pattern); | ||
} | ||
} | ||
} | ||
|
||
} |
120 changes: 120 additions & 0 deletions
120
jbehave-rest/src/main/java/org/jbehave/core/io/rest/confluence/LoadFromConfluence.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package org.jbehave.core.io.rest.confluence; | ||
|
||
import java.util.Arrays; | ||
import java.util.HashSet; | ||
import java.util.Set; | ||
|
||
import org.jbehave.core.io.ResourceLoader; | ||
import org.jbehave.core.io.rest.RESTClient; | ||
import org.jbehave.core.io.rest.RESTClient.Type; | ||
import org.jbehave.core.io.rest.confluence.Confluence.Page; | ||
import org.jsoup.Jsoup; | ||
import org.jsoup.nodes.Document; | ||
import org.jsoup.nodes.Element; | ||
import org.jsoup.nodes.TextNode; | ||
import org.jsoup.select.Elements; | ||
|
||
public class LoadFromConfluence implements ResourceLoader { | ||
|
||
private final Confluence confluence; | ||
private final Set<String> acceptedMacros = new HashSet<String>(Arrays.asList("panel", "info")); | ||
|
||
public LoadFromConfluence() { | ||
this(null, null); | ||
} | ||
|
||
public LoadFromConfluence(String username, String password) { | ||
this(new RESTClient(Type.XML, username, password)); | ||
} | ||
|
||
public LoadFromConfluence(RESTClient client) { | ||
confluence = new Confluence(client); | ||
} | ||
|
||
public String loadResourceAsText(String resourcePath) { | ||
Page page = confluence.loadPage(resourcePath, false); | ||
Document doc = Jsoup.parse(page.getBody()); | ||
StringBuilder builder = new StringBuilder(); | ||
addTitle(doc, builder); | ||
addPanels(doc, builder); | ||
addExamples(doc, builder); | ||
return builder.toString(); | ||
} | ||
|
||
protected void addTitle(Document doc, StringBuilder builder) { | ||
Element titleEl = doc.getElementsByTag("h1").first(); | ||
String title = titleEl.text(); | ||
builder.append(title).append("\n\n"); | ||
} | ||
|
||
protected void addPanels(Document doc, StringBuilder builder) { | ||
Elements elements = doc.getElementsByTag("ac:structured-macro"); | ||
for (Element element : elements) { | ||
String name = element.attr("ac:name"); | ||
if (acceptedMacros.contains(name)) { | ||
appendMacroTitle(builder, element); | ||
appendMacroBody(builder, element); | ||
} | ||
} | ||
} | ||
|
||
private void appendMacroTitle(StringBuilder builder, Element element) { | ||
Elements parameters = element.getElementsByTag("ac:parameter"); | ||
if (parameters.size() > 0) { | ||
for (Element param : parameters) { | ||
if ("title".equals(param.attr("ac:name"))) { | ||
String text = param.text(); | ||
if (!text.contains(":")) { | ||
text = text + ":"; | ||
} | ||
builder.append(text).append("\n"); | ||
} | ||
} | ||
} | ||
} | ||
|
||
private void appendMacroBody(StringBuilder builder, Element element) { | ||
Elements bodies = element.getElementsByTag("ac:rich-text-body"); | ||
if (!bodies.isEmpty()) { | ||
Element body = bodies.first(); | ||
cleanNodes(body, "div"); | ||
cleanNodes(body, "p"); | ||
builder.append(body.text().replaceAll("<br/>", "\n")).append("\n"); | ||
} | ||
} | ||
|
||
protected void addExamples(Document doc, StringBuilder builder) { | ||
Elements tables = doc.getElementsByTag("table"); | ||
if (!tables.isEmpty()) { | ||
builder.append("Examples:\n"); | ||
Element table = tables.first(); | ||
Elements headers = table.select("tr").first().select("th"); | ||
for (Element header : headers) { | ||
builder.append("|").append(header.text()); | ||
} | ||
builder.append("|\n"); | ||
Elements data = table.select("tr"); | ||
for (int i = 1; i < data.size(); i++) { | ||
for (Element cell : data.get(i).select("td")) { | ||
builder.append("|").append(cell.text()); | ||
} | ||
builder.append("|\n"); | ||
} | ||
} | ||
} | ||
|
||
protected void cleanNodes(Element element, String tag) { | ||
Elements elementsByTag = element.getElementsByTag(tag); | ||
for (Element el : elementsByTag) { | ||
if (el == null || el.parent() == null) { | ||
continue; | ||
} | ||
Elements children = el.children().select(tag); | ||
for (Element child : children) { | ||
cleanNodes(child, tag); | ||
} | ||
TextNode text = new TextNode(el.text() + "<br/>", ""); | ||
el.replaceWith(text); | ||
} | ||
} | ||
} |
Oops, something went wrong.