Skip to content

Commit

Permalink
unit tests for publishToConfluence
Browse files Browse the repository at this point in the history
  • Loading branch information
PacoVK committed Jul 2, 2023
1 parent 4873b1d commit 0a49348
Show file tree
Hide file tree
Showing 7 changed files with 314 additions and 83 deletions.
143 changes: 102 additions & 41 deletions scripts/asciidoc2confluence.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,10 @@ import groovyx.net.http.HttpResponseException
import groovyx.net.http.HTTPBuilder
import groovyx.net.http.EncoderRegistry
import groovyx.net.http.ContentType

import groovy.transform.Field

import java.nio.file.Path
import java.security.MessageDigest
import static groovy.io.FileType.FILES
//to upload attachments:
Expand All @@ -50,9 +54,13 @@ import org.apache.http.entity.mime.content.InputStreamBody
import org.apache.http.entity.mime.HttpMultipartMode
import groovyx.net.http.Method

@Field
def CDATA_PLACEHOLDER_START = '<cdata-placeholder>'

@Field
def CDATA_PLACEHOLDER_END = '</cdata-placeholder>'

@Field
def baseUrl
def allPages
// #938-mksiva: global variable to hold input spaceKey passed in the Config.groovy
Expand Down Expand Up @@ -214,7 +222,7 @@ def realTitle = { pageTitle ->
confluencePagePrefix + pageTitle + confluencePageSuffix
}

def rewriteMarks = { body ->
def rewriteMarks (body) {
// Confluence strips out mark elements. Replace them with default formatting.
body.select('mark').wrap('<span style="background:#ff0;color:#000"></style>').unwrap()
}
Expand Down Expand Up @@ -368,7 +376,7 @@ boolean hasRequestedParent(Map existingPage, String requestedParentId) {
}
}

def rewriteDescriptionLists = { body ->
def rewriteDescriptionLists(body) {
def TAGS = [ dt: 'th', dd: 'td' ]
body.select('dl').each { dl ->
// WHATWG allows wrapping dt/dd in divs, simply unwrap them
Expand Down Expand Up @@ -419,7 +427,7 @@ def rewriteDescriptionLists = { body ->
}
}

def rewriteInternalLinks = { body, anchors, pageAnchors ->
def rewriteInternalLinks (body, anchors, pageAnchors) {
// find internal cross-references and replace them with link macros
body.select('a[href]').each { a ->
def href = a.attr('href')
Expand Down Expand Up @@ -525,7 +533,7 @@ def rewriteCodeblocks(Elements body, String cdataStart, String cdataEnd) {
}
}

def rewriteOpenAPI = { org.jsoup.nodes.Element body ->
def rewriteOpenAPI (org.jsoup.nodes.Element body) {
if (config.confluence.useOpenapiMacro == true || config.confluence.useOpenapiMacro == 'confluence-open-api') {
body.select('div.openapi pre > code').each { code ->
def parent=code.parent()
Expand Down Expand Up @@ -581,7 +589,7 @@ def rewriteOpenAPI = { org.jsoup.nodes.Element body ->
}
}

def unescapeCDATASections = { html ->
def unescapeCDATASections(html){
def start = html.indexOf(CDATA_PLACEHOLDER_START)
while (start > -1) {
def end = html.indexOf(CDATA_PLACEHOLDER_END, start)
Expand All @@ -595,14 +603,62 @@ def unescapeCDATASections = { html ->
}
start = html.indexOf(CDATA_PLACEHOLDER_START, start + 1)
}
html
return html
}

def getEmbeddedImageData(src){
def imageData = src.split("[;:,]")
def fileExtension = imageData[1].split("/")[1]
return Map.of(
"fileExtension", fileExtension,
"encoding", imageData[2],
"encodedContent", imageData[3]
)
}

def handleEmbeddedImage(basePath, fileName, fileExtension, encodedContent) {
def imageDir = "images/"
if(config.imageDirs.size() > 0){
def dir = config.imageDirs.find { it ->
def configureImagesDir = it.replace('./', '/')
Path.of(basePath, configureImagesDir, fileName).toFile().exists()
}
if(dir != null){
imageDir = dir.replace('./', '/')
}
}

if(!Path.of(basePath, imageDir, fileName).toFile().exists()){
println "Could not find embedded image at a known location"
def embeddedImagesLocation = "/confluence/images/"
new File(basePath + embeddedImagesLocation).mkdirs()
def imageHash = MD5(encodedContent)
println "Embedded Image Hash " + imageHash
def image = new File(basePath + embeddedImagesLocation + imageHash + ".${fileExtension}")
if(!image.exists()){
println "Creating image at " + basePath + embeddedImagesLocation
image.withOutputStream {output ->
output.write(encodedContent.decodeBase64())}
}
fileName = imageHash + ".${fileExtension}"
return Map.of(
"filePath", image.canonicalPath,
"fileName", fileName
)
} else {
return Map.of(
"filePath", basePath + imageDir + fileName,
"fileName", fileName
)
}
}

//modify local page in order to match the internal confluence storage representation a bit better
//definition lists are not displayed by confluence, so turn them into tables
//body can be of type Element or Elements
def deferredUpload = []
def parseBody = { body, anchors, pageAnchors ->

def parseBody(body, anchors, pageAnchors) {
def uploads = []
rewriteOpenAPI body

body.select('div.paragraph').unwrap()
Expand Down Expand Up @@ -633,38 +689,34 @@ def parseBody = { body, anchors, pageAnchors ->
// <ac:image ac:align="center" ac:width="500">
// <ri:attachment ri:filename="deployment-context.png"/>
// </ac:image>
def imageDir = "images/"

body.select('img').each { img ->
def src = img.attr('src')
def imgWidth = img.attr('width')?:500
def imgAlign = img.attr('align')?:"center"

//it is not an online image, so upload it to confluence and use the ri:attachment tag
if(!src.startsWith("http")) {
def newUrl = baseUrl.toString().replaceAll('\\\\','/').replaceAll('/[^/]*$','/')
def sanitizedBaseUrl = baseUrl.toString().replaceAll('\\\\','/').replaceAll('/[^/]*$','/')
def newUrl
def fileName
//it is an embedded image
if(src.startsWith("data:image")){
def imageData = src.split(";");
def fileExtension = imageData[0].split("/")[1]
def imageData = getEmbeddedImageData(src)
def fileExtension = imageData.get("fileExtension")
def encodedContent = imageData.get("encodedContent")
fileName = img.attr('alt').replaceAll(/\s+/,"_").concat(".${fileExtension}")
if(config.imageDirs.size > 0){
def dir = config.imageDirs.find { it ->
new File( newUrl + it + fileName).exists()
}
if(dir != null){
imageDir = dir
}
}
newUrl += imageDir + fileName
def embeddedImage = handleEmbeddedImage(sanitizedBaseUrl, fileName, fileExtension, encodedContent)
newUrl = embeddedImage.get("filePath")
fileName = embeddedImage.get("fileName")
}else {
newUrl += src
newUrl = sanitizedBaseUrl + src
fileName = java.net.URLDecoder.decode((src.tokenize('/')[-1]),"UTF-8")
}
newUrl = java.net.URLDecoder.decode(newUrl,"UTF-8")
println " image: "+newUrl
trythis {
deferredUpload << [0,newUrl,fileName,"automatically uploaded"]
uploads << [0,newUrl,fileName,"automatically uploaded"]
}
img.after("<ac:image ac:align=\"${imgAlign}\" ac:width=\"${imgWidth}\"><ri:attachment ri:filename=\"${fileName}\"/></ac:image>")
}
Expand All @@ -690,7 +742,7 @@ def parseBody = { body, anchors, pageAnchors ->
newUrl = java.net.URLDecoder.decode(newUrl,"UTF-8")

trythis {
deferredUpload << [0,newUrl,fileName,"automatically uploaded non-image attachment by docToolchain"]
uploads << [0,newUrl,fileName,"automatically uploaded non-image attachment by docToolchain"]
}
def uriArray=fileName.split("/")
def pureFilename = uriArray[uriArray.length-1]
Expand Down Expand Up @@ -722,7 +774,27 @@ def parseBody = { body, anchors, pageAnchors ->
.replaceAll(CDATA_PLACEHOLDER_START,'<![CDATA[')
.replaceAll(CDATA_PLACEHOLDER_END,']]>')

return pageString
return Map.of(
"page", pageString,
"uploads", uploads
)
}

def generateAndAttachToC(localPage) {
def content
if(config.confluence.disableToC){
def prefix = (config.confluence.extraPageContent?:'')
content = prefix+localPage
}else{
def default_toc = '<p><ac:structured-macro ac:name="toc"/></p>'
def prefix = (config.confluence.tableOfContents?:default_toc)+(config.confluence.extraPageContent?:'')
content = prefix+localPage
def default_children = '<p><ac:structured-macro ac:name="children"><ac:parameter ac:name="sort">creation</ac:parameter></ac:structured-macro></p>'
content += (config.confluence.tableOfChildren?:default_children)
}
def localHash = MD5(localPage)
content += '<p style="display:none">hash: #'+localHash+'#</p>'
return content
}

// the create-or-update functionality for confluence pages
Expand All @@ -731,6 +803,7 @@ def pushToConfluence = { pageTitle, pageBody, parentId, anchors, pageAnchors, ke
parentId = parentId?.toString()
def api = new RESTClient(config.confluence.api)
def headers = getHeaders()
def deferredUpload = []
String realTitleLC = realTitle(pageTitle).toLowerCase()

//this fixes the encoding
Expand All @@ -739,23 +812,11 @@ def pushToConfluence = { pageTitle, pageBody, parentId, anchors, pageAnchors, ke
api.setProxy(config.confluence.proxy.host, config.confluence.proxy.port, config.confluence.proxy.schema ?: 'http')
}
//try to get an existing page
localPage = parseBody(pageBody, anchors, pageAnchors)
def parsedBody = parseBody(pageBody, anchors, pageAnchors)
localPage = parsedBody.get("page")
deferredUpload.addAll(parsedBody.get("uploads"))
def localHash = MD5(localPage)
if(config.confluence.disableToC){
def prefix = (config.confluence.extraPageContent?:'')
localPage = prefix+localPage
localHash = MD5(localPage)
localPage += '<p style="display:none">hash: #'+localHash+'#</p>'
}else{
def default_toc = '<p><ac:structured-macro ac:name="toc"/></p>'
def prefix = (config.confluence.tableOfContents?:default_toc)+(config.confluence.extraPageContent?:'')
localPage = prefix+localPage
def default_children = '<p><ac:structured-macro ac:name="children"><ac:parameter ac:name="sort">creation</ac:parameter></ac:structured-macro></p>'
localPage += (config.confluence.tableOfChildren?:default_children)
localHash = MD5(localPage)
localPage += '<p style="display:none">hash: #'+localHash+'#</p>'
}

localPage = generateAndAttachToC(localPage)

def request = [
type : 'page',
Expand Down
42 changes: 0 additions & 42 deletions src/test/groovy/docToolchain/RewriteConfluenceCodeblock.groovy

This file was deleted.

0 comments on commit 0a49348

Please sign in to comment.