Create a Confluence Page with ScriptRunner: Server vs Cloud

ShareShare on LinkedIn.Share on Facebook.Share on Twitter.Share via email.
June 16, 2021
<p>ScriptRunner can extend the functionality of Atlassian products, such as Confluence and Jira Software. From creating pages or tickets within the script console to setting up jobs and listeners, ScriptRunner is an excellent add-on.</p> <p>In our&nbsp;previous article, <a href="/blog/the-scriptrunner-rundown" data-type="URL" data-id="/blog/the-scriptrunner-rundown" target="_blank" rel="noreferrer noopener">The ScriptRunner Showdown</a>, we explored possible limitations of ScriptRunner for Atlassian’s Cloud products when compared to the capabilities available for their Server products. Essentially, Atlassian’s Server products allow for more actions than Cloud products, largely because Cloud products are restricted to the Cloud REST API while Server products have access to both the Server REST API and Java REST API.</p> <p>Our script uses a simple example, but one that is likely to be at the heart of much more complicated scripts: create a page. To make things a bit interesting, and to diverge from the ‘copy page tree’ built-in script provided by ScriptRunner, we want the new page to appear under the home page of the target space page hierarchy. In our example script the space key is&nbsp;<code>TESTSPACE</code>.</p> <h2 class="wp-block-heading">Create a page script with ScriptRunner for Confluence Server</h2> <div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-1 wp-block-columns-is-layout-flex"> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"> <div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="181" height="184" src="https://wordpress.highwaythreesolutions.com/wp-content/uploads/2021/07/server-icon-1829170068-1623798240379.png" alt="" class="wp-image-45"></figure></div> </div> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%"> <p>It turns out there are actually two approaches that can be used to create a page using ScriptRunner for Confluence Server: via the Java REST API components or using the Server REST API. We will demonstrate both approaches.</p> </div> </div> <h3 class="wp-block-heading">Using Confluence Server Java REST API Components</h3> <div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-2 wp-block-columns-is-layout-flex"> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%"> <p>One way of creating a page in a space would be to use components from the Java REST API.</p> </div> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"> <div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="96" height="96" src="https://wordpress.highwaythreesolutions.com/wp-content/uploads/2021/07/Java.png" alt="" class="wp-image-46"></figure></div> </div> </div> <p>Confluence Server has been around for a while and there is plenty of documentation on the internet to assist with learning the broad scope of the Java REST API, whether through Atlassian community Q&amp;As or&nbsp;<a href="https://developer.atlassian.com/server/confluence/java-api-reference/">documentation</a>. This article will not provide any further background or explanation of the provided script example.</p> <p>The following code example uses the&nbsp;<code>PageManager</code>&nbsp;and&nbsp;<code>SpaceManager</code>&nbsp;components found in the API to create a page in the space with key&nbsp;<code>TESTSPACE</code>:</p> <pre class="wp-block-code language-groovy"><code><span class="token keyword">import</span> com<span class="token punctuation">.</span>atlassian<span class="token punctuation">.</span>confluence<span class="token punctuation">.</span>pages<span class="token punctuation">.</span>Page <span class="token keyword">import</span> com<span class="token punctuation">.</span>atlassian<span class="token punctuation">.</span>confluence<span class="token punctuation">.</span>pages<span class="token punctuation">.</span>PageManager <span class="token keyword">import</span> com<span class="token punctuation">.</span>atlassian<span class="token punctuation">.</span>confluence<span class="token punctuation">.</span>spaces<span class="token punctuation">.</span>Space <span class="token keyword">import</span> com<span class="token punctuation">.</span>atlassian<span class="token punctuation">.</span>confluence<span class="token punctuation">.</span>spaces<span class="token punctuation">.</span>SpaceManager <span class="token keyword">import</span> com<span class="token punctuation">.</span>atlassian<span class="token punctuation">.</span>sal<span class="token punctuation">.</span>api<span class="token punctuation">.</span>component<span class="token punctuation">.</span>ComponentLocator <span class="token keyword">def</span> spaceManager <span class="token operator">=</span> ComponentLocator<span class="token punctuation">.</span><span class="token function">getComponent</span><span class="token punctuation">(</span>SpaceManager<span class="token punctuation">)</span> <span class="token keyword">def</span> pageManager <span class="token operator">=</span> ComponentLocator<span class="token punctuation">.</span><span class="token function">getComponent</span><span class="token punctuation">(</span>PageManager<span class="token punctuation">)</span> <span class="token keyword">def</span> spaceKey <span class="token operator">=</span> <span class="token string gstring">"TESTSPACE"</span> <span class="token keyword">def</span> space <span class="token operator">=</span> spaceManager<span class="token punctuation">.</span><span class="token function">getSpace</span><span class="token punctuation">(</span><span class="token string gstring">"<span class="token expression"><span class="token punctuation">$</span><span class="token punctuation">{</span>spaceKey<span class="token punctuation">}</span></span>"</span><span class="token punctuation">)</span> <span class="token keyword">def</span> homePage <span class="token operator">=</span> space<span class="token punctuation">.</span><span class="token function">getHomePage</span><span class="token punctuation">(</span><span class="token punctuation">)</span> Page parentPage <span class="token operator">=</span> pageManager<span class="token punctuation">.</span><span class="token function">getPage</span><span class="token punctuation">(</span>homePage<span class="token punctuation">.</span>id<span class="token punctuation">)</span><span class="token punctuation">;</span> Page page <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Page</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> page<span class="token punctuation">.</span><span class="token function">setSpace</span><span class="token punctuation">(</span>space<span class="token punctuation">)</span><span class="token punctuation">;</span> page<span class="token punctuation">.</span><span class="token function">setParentPage</span><span class="token punctuation">(</span>parentPage<span class="token punctuation">)</span><span class="token punctuation">;</span> page<span class="token punctuation">.</span><span class="token function">setTitle</span><span class="token punctuation">(</span><span class="token string gstring">"Title of Page"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> page<span class="token punctuation">.</span><span class="token function">setBodyAsString</span><span class="token punctuation">(</span><span class="token string gstring">"Body of page. Bolded text."</span><span class="token punctuation">)</span><span class="token punctuation">;</span> parentPage<span class="token punctuation">.</span><span class="token function">addChild</span><span class="token punctuation">(</span>page<span class="token punctuation">)</span><span class="token punctuation">;</span> pageManager<span class="token punctuation">.</span><span class="token function">saveContentEntity</span><span class="token punctuation">(</span>page<span class="token punctuation">,</span> null<span class="token punctuation">,</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span></code></pre> <p>When creating a new page, the ‘set’ functions within the Page class (<a href="https://docs.atlassian.com/ConfluenceServer/javadoc/7.12.2/index.html?com/atlassian/confluence/pages/Page.html">API 7.12.2 documentation</a>) can be used to add values to the page being created. Recall that one requirement for this example is for the new page to be created below the homepage in the space page tree hierarchy. Therefore the new page must&nbsp;<code>setParentPage</code>&nbsp;as the homepage, and the homepage must add the new page as its child. This example does not care about the history of the new page so it can be saved with a context of null.</p> <h3 class="wp-block-heading">Using Confluence Server REST API</h3> <div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-3 wp-block-columns-is-layout-flex"> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%"> <p>The Confluence REST API Documentation may not seem as extensive as the Java REST API, but for our comparison it will get the same job done. The Confluence Server Developer even includes an example on how to create a new page.</p> </div> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"> <div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="96" height="96" src="https://wordpress.highwaythreesolutions.com/wp-content/uploads/2021/07/API-Gear.png" alt="" class="wp-image-47"></figure></div> </div> </div> <p>The following is the code to create a new page, using our requirements and the REST API:</p> <pre class="wp-block-code language-groovy"><code><span class="token keyword">import</span> com<span class="token punctuation">.</span>atlassian<span class="token punctuation">.</span>confluence<span class="token punctuation">.</span>spaces<span class="token punctuation">.</span>SpaceManager <span class="token keyword">import</span> com<span class="token punctuation">.</span>atlassian<span class="token punctuation">.</span>confluence<span class="token punctuation">.</span>spaces<span class="token punctuation">.</span>Space <span class="token keyword">import</span> groovy<span class="token punctuation">.</span>json<span class="token punctuation">.</span>StreamingJsonBuilder <span class="token keyword">import</span> com<span class="token punctuation">.</span>atlassian<span class="token punctuation">.</span>sal<span class="token punctuation">.</span>api<span class="token punctuation">.</span>component<span class="token punctuation">.</span>ComponentLocator <span class="token keyword">def</span> spaceManager <span class="token operator">=</span> ComponentLocator<span class="token punctuation">.</span><span class="token function">getComponent</span><span class="token punctuation">(</span>SpaceManager<span class="token punctuation">)</span> <span class="token keyword">def</span> server <span class="token operator">=</span> <span class="token string">'YOUR SERVER BASE URL'</span> <span class="token keyword">def</span> authString <span class="token operator">=</span> <span class="token string">'username:password'</span><span class="token punctuation">.</span><span class="token function">getBytes</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">encodeBase64</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">toString</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">def</span> spaceKey <span class="token operator">=</span> <span class="token string gstring">"TESTSPACE"</span> <span class="token keyword">def</span> space <span class="token operator">=</span> spaceManager<span class="token punctuation">.</span><span class="token function">getSpace</span><span class="token punctuation">(</span><span class="token string gstring">"<span class="token expression"><span class="token punctuation">$</span><span class="token punctuation">{</span>spaceKey<span class="token punctuation">}</span></span>"</span><span class="token punctuation">)</span> <span class="token keyword">def</span> homePage <span class="token operator">=</span> space<span class="token punctuation">.</span><span class="token function">getHomePage</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">def</span> pageParams <span class="token operator">=</span> <span class="token punctuation">[</span> title<span class="token punctuation">:</span> <span class="token string gstring">"Title of Page"</span><span class="token punctuation">,</span> type<span class="token punctuation">:</span> <span class="token string gstring">"page"</span><span class="token punctuation">,</span> space<span class="token punctuation">:</span><span class="token punctuation">[</span> key<span class="token punctuation">:</span><span class="token string gstring">"<span class="token expression"><span class="token punctuation">$</span><span class="token punctuation">{</span>spaceKey<span class="token punctuation">}</span></span>"</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> ancestors<span class="token punctuation">:</span> <span class="token punctuation">[</span> <span class="token punctuation">[</span>id<span class="token punctuation">:</span>homePage<span class="token punctuation">.</span>id<span class="token punctuation">]</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> body<span class="token punctuation">:</span> <span class="token punctuation">[</span> storage<span class="token punctuation">:</span> <span class="token punctuation">[</span> value<span class="token punctuation">:</span> <span class="token string gstring">"Body of page. Bolded text."</span><span class="token punctuation">,</span> representation<span class="token punctuation">:</span> <span class="token string gstring">"storage"</span> <span class="token punctuation">]</span> <span class="token punctuation">]</span> <span class="token punctuation">]</span> <span class="token keyword">def</span> createPageResult <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">URL</span><span class="token punctuation">(</span><span class="token string gstring">"<span class="token expression"><span class="token punctuation">$</span><span class="token punctuation">{</span>server<span class="token punctuation">}</span></span>/rest/api/content"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">openConnection</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">as</span> HttpURLConnection createPageResult<span class="token punctuation">.</span>requestMethod <span class="token operator">=</span> <span class="token string">'POST'</span> createPageResult<span class="token punctuation">.</span><span class="token function">setRequestProperty</span><span class="token punctuation">(</span><span class="token string">'Authorization'</span><span class="token punctuation">,</span> <span class="token string gstring">"Basic <span class="token expression"><span class="token punctuation">$</span><span class="token punctuation">{</span>authString<span class="token punctuation">}</span></span>"</span><span class="token punctuation">)</span> createPageResult<span class="token punctuation">.</span>doOutput <span class="token operator">=</span> <span class="token boolean">true</span> createPageResult<span class="token punctuation">.</span><span class="token function">setRequestProperty</span><span class="token punctuation">(</span><span class="token string">'Content-Type'</span><span class="token punctuation">,</span> <span class="token string">'application/json;charset=UTF-8'</span><span class="token punctuation">)</span> createPageResult<span class="token punctuation">.</span>outputStream<span class="token punctuation">.</span><span class="token function">withWriter</span><span class="token punctuation">(</span><span class="token string">'UTF-8'</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token keyword">new</span> <span class="token class-name">StreamingJsonBuilder</span><span class="token punctuation">(</span>it<span class="token punctuation">,</span> pageParams<span class="token punctuation">)</span> <span class="token punctuation">}</span> createPageResult<span class="token punctuation">.</span><span class="token function">connect</span><span class="token punctuation">(</span><span class="token punctuation">)</span> log<span class="token punctuation">.</span>warn<span class="token string gstring">"<span class="token expression"><span class="token punctuation">$</span><span class="token punctuation">{</span>createPageResult<span class="token punctuation">.</span><span class="token function">getResponseCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">}</span></span> created Page <span class="token expression"><span class="token punctuation">$</span><span class="token punctuation">{</span>pageParams<span class="token punctuation">.</span>title<span class="token punctuation">}</span></span> for Space <span class="token expression"><span class="token punctuation">$</span><span class="token punctuation">{</span>spaceKey<span class="token punctuation">}</span></span>"</span> <span class="token comment">//Close POST</span> createPageResult<span class="token punctuation">.</span><span class="token function">disconnect</span><span class="token punctuation">(</span><span class="token punctuation">)</span></code></pre> <p>Using the Server REST API involves more set up or configuration, specifically, the script must specify:</p> <ul><li>The base URL of the instance</li><li>The credentials (username and password) of a user who has permission to create a page in the target space.</li></ul> <p>In the Adaptavist ScriptRunner for Confluence’s console, this code will create a page identical to the one made by the components script shared above.</p> <h2 class="wp-block-heading">Create a page script with ScriptRunner for Confluence Cloud</h2> <div class="wp-block-columns is-layout-flex wp-container-core-columns-is-layout-4 wp-block-columns-is-layout-flex"> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:33.33%"> <div class="wp-block-image"><figure class="aligncenter size-large"><img loading="lazy" decoding="async" width="178" height="177" src="https://wordpress.highwaythreesolutions.com/wp-content/uploads/2021/07/Cloud-plain.png" alt="" class="wp-image-48" srcset="https://wordpress.highwaythreesolutions.com/wp-content/uploads/2021/07/Cloud-plain.png 178w, https://wordpress.highwaythreesolutions.com/wp-content/uploads/2021/07/Cloud-plain-150x150.png 150w" sizes="(max-width: 178px) 100vw, 178px"></figure></div> </div> <div class="wp-block-column is-layout-flow wp-block-column-is-layout-flow" style="flex-basis:66.66%"> <p>Adaptavist ScriptRunner for Confluence Cloud includes a few example code snippets which are helpful to kickstart your introduction to the Cloud REST API. The code below is similar to ScriptRunner’s&nbsp;<code>Create page in space</code>, with a few changes to fit this example. As with the previous code snippets, this creates a page in the Confluence space with key&nbsp;<code>TESTSPACE</code>.</p> </div> </div> <pre class="wp-block-code language-groovy"><code><span class="token keyword">def</span> spaceKey <span class="token operator">=</span> <span class="token string gstring">"TESTSPACE"</span> <span class="token keyword">def</span> createPageResult <span class="token operator">=</span> <span class="token function">post</span><span class="token punctuation">(</span><span class="token string">'/wiki/rest/api/content'</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">header</span><span class="token punctuation">(</span><span class="token string">'Content-Type'</span><span class="token punctuation">,</span> <span class="token string">'application/json'</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">body</span><span class="token punctuation">(</span><span class="token punctuation">[</span> space<span class="token punctuation">:</span> <span class="token punctuation">[</span> key<span class="token punctuation">:</span> spaceKey <span class="token punctuation">]</span><span class="token punctuation">,</span> status<span class="token punctuation">:</span> <span class="token string gstring">"current"</span><span class="token punctuation">,</span> title<span class="token punctuation">:</span> <span class="token string gstring">"Title of Page"</span><span class="token punctuation">,</span> type<span class="token punctuation">:</span> <span class="token string gstring">"page"</span><span class="token punctuation">,</span> body<span class="token punctuation">:</span> <span class="token punctuation">[</span> storage<span class="token punctuation">:</span> <span class="token punctuation">[</span> representation<span class="token punctuation">:</span> <span class="token string gstring">"storage"</span><span class="token punctuation">,</span> value<span class="token punctuation">:</span> <span class="token string gstring">"Body of page. Bolded text."</span> <span class="token punctuation">]</span> <span class="token punctuation">]</span> <span class="token punctuation">]</span><span class="token punctuation">)</span> <span class="token punctuation">.</span><span class="token function">asObject</span><span class="token punctuation">(</span>Map<span class="token punctuation">)</span><span class="token punctuation">.</span>body</code></pre> <p>Instead of requiring the base url and authentication be added into the console by the user, the&nbsp;<code>post</code>&nbsp;function applies the values associated with the Cloud instance. That’s great news as it means less work for the developer.</p> <p>If you have seen the ScriptRunner console’s example, you may have noticed they use a body format&nbsp;<code>view</code>&nbsp;while this example has&nbsp;<code>storage</code>. Depending on what the body content of the page is will help determine which format should be used. The&nbsp;<a href="https://developer.atlassian.com/cloud/confluence/rest/api-group-content/#api-api-content-post">Confluence REST API</a>&nbsp;states only one body format should be used, and it includes a list to choose from in its example. Definitions for the body formats can be found in the&nbsp;<a href="https://docs.atlassian.com/ConfluenceServer/javadoc/7.12.2/com/atlassian/confluence/api/model/content/ContentRepresentation.html">Server ContentRepresentation class</a>.</p> <h2 class="wp-block-heading">What did we learn?</h2> <p>We have shown three possibilities to script creating a page in a Confluence space. By demonstrating a Server script that uses the Server REST API we were able to see how that is similar to what can be expected when migrating to the Cloud. We were also able to clearly see Server scripts based upon the Java REST API components will require considerable more re-development than ones using the Server REST API.</p> <p>If you will continue to develop new scripts for Server, you should consider switching to the Server REST API as much as possible to reduce the effort required to convert the scripts when you migrate to the Cloud.</p> <p>We have summarized our findings in the following table.</p> <figure class="wp-block-table is-style-h3-table"><table><thead><tr><th>Method</th><th>Create Page</th><th>User Requirements</th></tr></thead><tbody><tr><td>Components</td><td>– Get and add Space<br>– Set values<br>– Get and add Parent Page<br>– Add Child Page to Parent Page<br>– Save configuration</td><td>– Space Key<br>– Permission to access Space and add Page</td></tr><tr><td>Server REST API</td><td>– Get Space<br>– Create parameters<br>– POST parameters</td><td>– Space Key<br>– Base URL<br>– Username<br>– Password<br>– Permission to access Space and add Page</td></tr><tr><td>Cloud REST API</td><td>– Create parameters<br>– POST parameters</td><td>– Space Key<br>– Permission to access Space and add Page</td></tr></tbody></table></figure> <p>We know our example was very simple, but it does demonstrate key differences between the three approaches, what you can do in the short term, and how you can plan for the long term as you look towards your Cloud migration.</p> <p>As part of your migration planning it is especially important to check if any components in a ScriptRunner script have an equivalent API call that can be made in Cloud. If there are none, then you may need to consider alternate designs and exploring other approaches, like Automation. Knowing what to expect and planning with that in mind will help smooth out your migration path.</p> <div class="wp-block-buttons is-content-justification-center is-layout-flex wp-block-buttons-is-layout-flex"> <div class="wp-block-button is-style-h3-cta-button"><a class="wp-block-button__link" href="/contact?subject=ScriptRunner for Cloud" target="_blank" rel="noopener">Need help configuring ScriptRunner for Cloud?</a></div> </div>