SlideShare a Scribd company logo
Connect Add-ons for
Cloud & Server
PATRICK STREULE • ARCHITECT • ATLASSIAN • @PSTREULE
AtlasCamp 2015: Connect everywhere - Cloud and Server
Connect
AtlasCamp 2015: Connect everywhere - Cloud and Server
Overview
Cloud
Browser
Product – Cloud Add-On Service
Connect Plugin
Connect JS
Add-On UI
Add-On JS
AssetsAPI
Browser
Product – Cloud Add-On Service
Connect Plugin
Connect JS
Add-On UI
Add-On JS
AssetsAPI
Cloud Create iFrame,
XDM bridge
Browser
Product – Cloud Add-On Service
Connect Plugin
Connect JS
Add-On UI
Add-On JS
AssetsAPI
Cloud
Expose
Host API
Browser
Product – Cloud Add-On Service
Connect Plugin
Connect JS
Add-On UI
Add-On JS
AssetsAPI
Cloud
Lifecycle,
Webhooks
Browser
Product – Cloud Add-On Service
Connect Plugin
Connect JS
Add-On UI
Add-On JS
AssetsAPI
Cloud
REST API
Browser
Product – Cloud Add-On Service
Connect Plugin
Connect JS
Add-On UI
Add-On JS
AssetsAPI
REST API
Bridge
Cloud
Now in Server
Server
Product – Server v1
Product – Server v2
Browser
Add-On Service
?
Add-On UI
Add-On JS
AssetsAPI
Product – Server v3
?
Server
Product – Server v1
Product – Server v2
Browser
Add-On Service
?
Add-On UI
Add-On JS
AssetsAPI
Product – Server v3
?
iFrame setup?
XDM?
API Version?
Server
Product – Server v1
Product – Server v2
Browser
Add-On Service
?
Add-On UI
Add-On JS
AssetsAPI
Product – Server v3
?
Auth?

Privacy?
Connectivity?
Server
Product – Server v1
Product – Server v2
Browser
Add-On Service
?
Add-On UI
Add-On JS
AssetsAPI
Product – Server v3
?
Connectivity?
API Version?
Auth?
Example
Sequence Diagram Add-On
AtlasCamp 2015: Connect everywhere - Cloud and Server
• Purely client-side
• No server-side rendering
• Javascript & SVG
• No data stored by the add-on
• Uses macro body for storage
• Uses REST API to fetch the macro
body
• However: The Connect version
stores the installation payload
Browser
Product – Cloud Add-On Service
Connect Plugin
Connect JS
Add-On UI
Add-On JS
Assets
Design Choices
ConnectP2
Browser
Product – Server
P2 Plugin
Add-On UI
Add-On JS
Assets
Connect JS Shim
Show me code
Plugin XML
<web-resource key="sequence-diagram-resources" name="sequence-diagram Web Resources">
<resource type="download" name="blueprint.css" location="/css/blueprint.css"/>
<resource type="download" name="confluence.css" location="/css/confluence.css"/>
<resource type="download" name="napkin.css" location="/css/napkin.css"/>
<resource type="download" name="plain.css" location="/css/plain.css"/>
<resource type="download" name="sequence-diagrams.js" location="/js/sequence-diagrams.743cf0e5.js"/>
<resource type="download" name="connect-api.js" location="/js/connect-api.js"/>
<resource type="download" name="addon.js" location="/js/addon.js"/>
</web-resource>
<servlet name="Diagram IFrame Servlet" key="sequence-diagram-servlet" class="com.pstreule.SequenceDiagramIFrame">
<url-pattern>/sequence-diagram</url-pattern>
</servlet>
<xhtml-macro name="sequence-diagram" class="com.pstreule.SequenceDiagramMacro" key="sequence-diagram-macro">
<parameters>
<parameter name="theme" type="enum" required="true">
<value name="Confluence"/>
<value name="Blueprint"/>
<value name="Napkin"/>
<value name="Plain"/>
</parameter>
<parameter name="width" type="string"/>
</parameters>
</xhtml-macro>
Render the iFrame
public SequenceDiagramMacro(TemplateRenderer renderer, ApplicationProperties applicationProperties)
{
this.renderer = renderer;
this.applicationProperties = applicationProperties;
}
@Override
public String execute(Map<String, String> parameters, String storageFormatBody, ConversionContext conversionContext)
throws MacroExecutionException
{
ContentEntityObject entity = conversionContext.getEntity();
String baseUrl = applicationProperties.getBaseUrl();
URI uri = UriBuilder.fromPath(baseUrl + "/plugins/servlet/sequence-diagram")
.queryParam("pid", entity.getIdAsString())
.queryParam("pv", entity.getVersion())
.queryParam("mh", DigestUtils.md5Hex(storageFormatBody))
.queryParam("theme", parameters.get("theme"))
.build();
Map<String, Object> context = new HashMap<>();
context.put("url", uri.toString());
context.put("name", "preview".equals(conversionContext.getOutputType()) ? storageFormatBody : "");
return render("/templates/iframe.vm", context);
}
<iframe src="$url" name="$name" frameborder="0"
rel="nofollow" scrolling="no"></iframe>
Render the iFrame Content
public class SequenceDiagramIFrame extends HttpServlet
{
private final TemplateRenderer renderer;
private final PageBuilderService pageBuilderService;
public SequenceDiagramIFrame(TemplateRenderer renderer, PageBuilderService pageBuilderService)
{
this.renderer = renderer;
this.pageBuilderService = pageBuilderService;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
{
resp.setStatus(HttpServletResponse.SC_OK);
resp.setContentType("text/html");
StringWriter writer = new StringWriter();
WebResourceAssembler assembler = pageBuilderService.assembler();
assembler.assembled().drainIncludedResources();
assembler.resources().requireWebResource("com.pstreule.sequence-diagram:sequence-diagram-resources");
WebResourceSet set = assembler.assembled().drainIncludedResources();
set.writeHtmlTags(writer, UrlMode.RELATIVE);
renderer.render("/templates/sequence-diagram.vm",
Collections.singletonMap("resourcesWithHtml", writer.toString()), resp.getWriter());
}
}
<html>
<head>
$resourcesWithHtml
</head>
<body>
<div id="sequence-diagram"></div>
<script type="application/javascript">
renderSequenceDiagram("sequence-diagram", window.name)
</script>
</body>
</html>
Add-On JavaScript Code
function renderSequenceDiagram(id, previewMacroBody) {
function render(diagramCode) {
function onRenderSuccess(dimensions) {
AP.resize(dimensions.w + 10, dimensions.h + 10);
}
var themeName = Loader.getUrlParameter("theme");
var theme = SequenceDiagramThemes.byName(themeName);
var controller = new SequenceDiagramController(id);
controller.renderSequenceDiagram(diagramCode, theme, null, onRenderSuccess, onRenderError);
}
function loadMacroAndRender() {
function onLoadSuccess(responseBody) {
var diagramCode = JSON.parse(responseBody).body;
render(diagramCode);
}
AP.require(['request'], function (request) {
var pageId = Loader.getUrlParameter("pid");
var pageVersion = Loader.getUrlParameter("pv");
var macroHash = Loader.getUrlParameter("mh");
request({
url: "/rest/api/content/" + pageId + "/history/" + pageVersion + "/macro/hash/" + macroHash,
success: onLoadSuccess
});
});
}
loadMacroAndRender();
}
Connect JS Shim
var AP = {};
(function (AP) {
var AJS = window.parent.AJS;
var $ = AJS.$;
_modules = {
'request': function (url, args) {
…
$.ajax({
url: url,
…
}).then(done, fail);
}
};
AP.require = function (modules, callback) {
var requiredModules = Array.isArray(modules) ? modules : [modules];
var args = requiredModules.map(function (module) {
return _modules[module];
});
callback.apply(window, args);
};
AP.resize = function (width, height) {
AJS.$(window.frameElement).css({width: width, height: height});
};
})(AP);
Best Practices
Make it 

Self-Contained
Run in Process
Browser
Product – Server
P2 Plugin
Add-On UI
Add-On JS
AssetsAPI
Connect JS Shim
Adapter
Run in Process
Browser
Product – Server
P2 Plugin
Add-On UI
Add-On JS
AssetsAPI
Connect JS Shim
Adapter
No cross-domain
bridge needed
Run in Process
Browser
Product – Server
P2 Plugin
Add-On UI
Add-On JS
AssetsAPI
Connect JS Shim
AdapterEvent Handling,
Storage
Run in Process
Browser
Product – Server
P2 Plugin
Add-On UI
Add-On JS
AssetsAPI
Connect JS Shim
Adapter
100% Reusable
100% Reusable
Run in Process
Browser
Product – Server
P2 Plugin
Add-On UI
Add-On JS
AssetsAPI
Connect JS Shim
Adapter
100% reusable
(maybe)
Use Client-Side
Rendering as much
as Possible
Shrink the server-side
Browser
Product – Server
P2 Plugin
Add-On UI
Add-On JS
AssetsAPI
Connect JS Shim
Use In-Product 

Data Storage
Use Entity Properties for Data Storage
POST /rest/api/content/{content_ID}/property
Content-Type: application/json
{
"key" : "attachment",
"value" : {
"size": 234374,
"description": "My description"
...
}
}
GET /rest/api/content/{content_ID}/propertyGET /rest/api/2/issue/{key}/properties/
PUT /rest/api/2/issue/{key}/properties/
attachment
Content-Type: application/json
{
"size": 234374,
"description": "My description"
...
}
Use Entity Properties for Data Storage
{
"jiraEntityProperties": [
{
"keyConfigurations": [
{
"propertyKey": "attachment",
"extractions": [
{
"objectName": "attachment.size",
"type": "number",
"alias": "attachmentSize"
}
]
}
],
"entityType": "issue",
"name": {
"value": "Attachment Index Document"
}
}
]
}
{
"confluenceContentProperties": [
{
"keyConfigurations": [
{
"propertyKey": "attachment",
"extractions": [
{
"objectName": "attachment.size",
"type": "number"
}
]
}
],
"name": {
"value": "Attachment Index Document"
}
}
]
}
Use client-side REST Calls
// Get the content properties of an issue
AP.require(['request'], function(request){
request({
url: '/rest/api/2/issue/' + issueKey + '/properties/',
success: function(responseText){
var properties = JSON.parse(responseText);
…
}
});
});
Expose REST API
for Server-Side
Logic
JAX-RS/Jersey
@Path("my-resource")
public class MyResource
{
@GET
@Produces("application/json")
public Response getSomething(@Context HttpServletRequest request)
{
// ...
return Response.ok();
}
}
Tools*
*currently in planning
Thank you!
PATRICK STREULE • ARCHITECT • ATLASSIAN • @PSTREULE
Connect everywhere - Cloud and Server
Submit your feedback:
go.atlassian.com/acconnectcloud

More Related Content

PDF
AtlasCamp 2015: Web technologies you should be using now
PDF
AtlasCamp 2015: Using add-ons to build add-ons
PDF
AtlasCamp 2015: JIRA Service Desk: Scale your team with build-it-yourself aut...
PDF
Building Universal Web Apps with React ForwardJS 2017
PDF
jQuery and Rails: Best Friends Forever
PPT
jQuery and AJAX with Rails
PDF
Supercharge Your Pages - New Ways to Extend the Confluence Editor
PDF
AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen
AtlasCamp 2015: Web technologies you should be using now
AtlasCamp 2015: Using add-ons to build add-ons
AtlasCamp 2015: JIRA Service Desk: Scale your team with build-it-yourself aut...
Building Universal Web Apps with React ForwardJS 2017
jQuery and Rails: Best Friends Forever
jQuery and AJAX with Rails
Supercharge Your Pages - New Ways to Extend the Confluence Editor
AtlasCamp 2010: Understanding the Atlassian Platform - Tim Pettersen

What's hot (20)

PPTX
Orchard Harvest 2014 - The Future of Widgets?
PDF
AtlasCamp 2013: Modernizing your Plugin UI
PDF
ILUG 2010 - Deploying plug-ins to the enterprise
PDF
Unlock the next era of UI design with Polymer
PDF
Vaadin Components @ Angular U
PDF
Ajax Rails
 
PDF
Web Components & Polymer 1.0 (Webinale Berlin)
PDF
All you need to know about JavaScript loading and execution in the browser - ...
PDF
Vaadin Components
PDF
Gae Meets Django
PPTX
SPSSTL - PowerShell - Through the SharePoint Looking Glass
PDF
Angular vs React for Web Application Development
KEY
Introduction Django
PPTX
Apex & jQuery Mobile
PDF
Laravel 8 export data as excel file with example
PDF
In The Brain of Cagatay Civici: Exploring JavaServer Faces 2.0 and PrimeFaces
PDF
Course CodeSchool - Shaping up with Angular.js
PDF
The Complementarity of React and Web Components
PDF
Integrate CI/CD Pipelines with Jira Software Cloud
PPTX
AngularJS training - Day 1 - Basics: Why, What and basic features of AngularJS
Orchard Harvest 2014 - The Future of Widgets?
AtlasCamp 2013: Modernizing your Plugin UI
ILUG 2010 - Deploying plug-ins to the enterprise
Unlock the next era of UI design with Polymer
Vaadin Components @ Angular U
Ajax Rails
 
Web Components & Polymer 1.0 (Webinale Berlin)
All you need to know about JavaScript loading and execution in the browser - ...
Vaadin Components
Gae Meets Django
SPSSTL - PowerShell - Through the SharePoint Looking Glass
Angular vs React for Web Application Development
Introduction Django
Apex & jQuery Mobile
Laravel 8 export data as excel file with example
In The Brain of Cagatay Civici: Exploring JavaServer Faces 2.0 and PrimeFaces
Course CodeSchool - Shaping up with Angular.js
The Complementarity of React and Web Components
Integrate CI/CD Pipelines with Jira Software Cloud
AngularJS training - Day 1 - Basics: Why, What and basic features of AngularJS
Ad

Similar to AtlasCamp 2015: Connect everywhere - Cloud and Server (20)

PDF
Attractive HTML5~開発者の視点から~
PDF
vidhi talk.pdf
KEY
Modular Web Applications With Netzke
PDF
An Introduction to Sencha Touch
KEY
Introduction to Palm's Mojo SDK
PPTX
Building single page applications
PDF
AtlasCamp 2014: Static Connect Add-ons
KEY
End of native?
PPTX
Cloud Endpoints _Polymer_ Material design by Martin Görner
PPTX
Building assets on the fly with Node.js
PDF
Automating the API Product Lifecycle
PDF
Hybrid Apps (Native + Web) via QtWebKit
PDF
Heroku pop-behind-the-sense
PDF
S60 3rd FP2 Widgets
KEY
Paris js extensions
PDF
Hybrid Apps (Native + Web) using WebKit
PDF
Hybrid Apps (Native + Web) using WebKit
PDF
Social Connections VI — IBM Connections Extensions and Themes Demystified
PDF
SPUnite17 Building Great Client Side Web Parts with SPFx
PDF
Jacob Waller: Webifying Titanium Development
Attractive HTML5~開発者の視点から~
vidhi talk.pdf
Modular Web Applications With Netzke
An Introduction to Sencha Touch
Introduction to Palm's Mojo SDK
Building single page applications
AtlasCamp 2014: Static Connect Add-ons
End of native?
Cloud Endpoints _Polymer_ Material design by Martin Görner
Building assets on the fly with Node.js
Automating the API Product Lifecycle
Hybrid Apps (Native + Web) via QtWebKit
Heroku pop-behind-the-sense
S60 3rd FP2 Widgets
Paris js extensions
Hybrid Apps (Native + Web) using WebKit
Hybrid Apps (Native + Web) using WebKit
Social Connections VI — IBM Connections Extensions and Themes Demystified
SPUnite17 Building Great Client Side Web Parts with SPFx
Jacob Waller: Webifying Titanium Development
Ad

More from Atlassian (20)

PPTX
International Women's Day 2020
PDF
10 emerging trends that will unbreak your workplace in 2020
PDF
Forge App Showcase
PDF
Let's Build an Editor Macro with Forge UI
PDF
Meet the Forge Runtime
PDF
Forge UI: A New Way to Customize the Atlassian User Experience
PDF
Take Action with Forge Triggers
PDF
Observability and Troubleshooting in Forge
PDF
Trusted by Default: The Forge Security & Privacy Model
PDF
Designing Forge UI: A Story of Designing an App UI System
PDF
Forge: Under the Hood
PDF
Access to User Activities - Activity Platform APIs
PDF
Design Your Next App with the Atlassian Vendor Sketch Plugin
PDF
Tear Up Your Roadmap and Get Out of the Building
PDF
Nailing Measurement: a Framework for Measuring Metrics that Matter
PDF
Building Apps With Color Blind Users in Mind
PDF
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...
PDF
Beyond Diversity: A Guide to Building Balanced Teams
PDF
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed Team
PDF
Building Apps With Enterprise in Mind
International Women's Day 2020
10 emerging trends that will unbreak your workplace in 2020
Forge App Showcase
Let's Build an Editor Macro with Forge UI
Meet the Forge Runtime
Forge UI: A New Way to Customize the Atlassian User Experience
Take Action with Forge Triggers
Observability and Troubleshooting in Forge
Trusted by Default: The Forge Security & Privacy Model
Designing Forge UI: A Story of Designing an App UI System
Forge: Under the Hood
Access to User Activities - Activity Platform APIs
Design Your Next App with the Atlassian Vendor Sketch Plugin
Tear Up Your Roadmap and Get Out of the Building
Nailing Measurement: a Framework for Measuring Metrics that Matter
Building Apps With Color Blind Users in Mind
Creating Inclusive Experiences: Balancing Personality and Accessibility in UX...
Beyond Diversity: A Guide to Building Balanced Teams
The Road(map) to Las Vegas - The Story of an Emerging Self-Managed Team
Building Apps With Enterprise in Mind

Recently uploaded (20)

PDF
Review of recent advances in non-invasive hemoglobin estimation
PDF
Agricultural_Statistics_at_a_Glance_2022_0.pdf
PPTX
MYSQL Presentation for SQL database connectivity
PPTX
Understanding_Digital_Forensics_Presentation.pptx
PPTX
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
KodekX | Application Modernization Development
PDF
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PPTX
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
PDF
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
PDF
Diabetes mellitus diagnosis method based random forest with bat algorithm
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PPT
Teaching material agriculture food technology
PPTX
Cloud computing and distributed systems.
PDF
Network Security Unit 5.pdf for BCA BBA.
PDF
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
DOCX
The AUB Centre for AI in Media Proposal.docx
PDF
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
PPTX
A Presentation on Artificial Intelligence
PDF
Dropbox Q2 2025 Financial Results & Investor Presentation
Review of recent advances in non-invasive hemoglobin estimation
Agricultural_Statistics_at_a_Glance_2022_0.pdf
MYSQL Presentation for SQL database connectivity
Understanding_Digital_Forensics_Presentation.pptx
KOM of Painting work and Equipment Insulation REV00 update 25-dec.pptx
Mobile App Security Testing_ A Comprehensive Guide.pdf
KodekX | Application Modernization Development
TokAI - TikTok AI Agent : The First AI Application That Analyzes 10,000+ Vira...
PA Analog/Digital System: The Backbone of Modern Surveillance and Communication
Blue Purple Modern Animated Computer Science Presentation.pdf.pdf
Diabetes mellitus diagnosis method based random forest with bat algorithm
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
Teaching material agriculture food technology
Cloud computing and distributed systems.
Network Security Unit 5.pdf for BCA BBA.
7 ChatGPT Prompts to Help You Define Your Ideal Customer Profile.pdf
The AUB Centre for AI in Media Proposal.docx
Peak of Data & AI Encore- AI for Metadata and Smarter Workflows
A Presentation on Artificial Intelligence
Dropbox Q2 2025 Financial Results & Investor Presentation

AtlasCamp 2015: Connect everywhere - Cloud and Server

  • 1. Connect Add-ons for Cloud & Server PATRICK STREULE • ARCHITECT • ATLASSIAN • @PSTREULE
  • 6. Cloud Browser Product – Cloud Add-On Service Connect Plugin Connect JS Add-On UI Add-On JS AssetsAPI
  • 7. Browser Product – Cloud Add-On Service Connect Plugin Connect JS Add-On UI Add-On JS AssetsAPI Cloud Create iFrame, XDM bridge
  • 8. Browser Product – Cloud Add-On Service Connect Plugin Connect JS Add-On UI Add-On JS AssetsAPI Cloud Expose Host API
  • 9. Browser Product – Cloud Add-On Service Connect Plugin Connect JS Add-On UI Add-On JS AssetsAPI Cloud Lifecycle, Webhooks
  • 10. Browser Product – Cloud Add-On Service Connect Plugin Connect JS Add-On UI Add-On JS AssetsAPI Cloud REST API
  • 11. Browser Product – Cloud Add-On Service Connect Plugin Connect JS Add-On UI Add-On JS AssetsAPI REST API Bridge Cloud
  • 13. Server Product – Server v1 Product – Server v2 Browser Add-On Service ? Add-On UI Add-On JS AssetsAPI Product – Server v3 ?
  • 14. Server Product – Server v1 Product – Server v2 Browser Add-On Service ? Add-On UI Add-On JS AssetsAPI Product – Server v3 ? iFrame setup? XDM? API Version?
  • 15. Server Product – Server v1 Product – Server v2 Browser Add-On Service ? Add-On UI Add-On JS AssetsAPI Product – Server v3 ? Auth?
 Privacy? Connectivity?
  • 16. Server Product – Server v1 Product – Server v2 Browser Add-On Service ? Add-On UI Add-On JS AssetsAPI Product – Server v3 ? Connectivity? API Version? Auth?
  • 19. • Purely client-side • No server-side rendering • Javascript & SVG • No data stored by the add-on • Uses macro body for storage • Uses REST API to fetch the macro body • However: The Connect version stores the installation payload Browser Product – Cloud Add-On Service Connect Plugin Connect JS Add-On UI Add-On JS Assets Design Choices ConnectP2 Browser Product – Server P2 Plugin Add-On UI Add-On JS Assets Connect JS Shim
  • 21. Plugin XML <web-resource key="sequence-diagram-resources" name="sequence-diagram Web Resources"> <resource type="download" name="blueprint.css" location="/css/blueprint.css"/> <resource type="download" name="confluence.css" location="/css/confluence.css"/> <resource type="download" name="napkin.css" location="/css/napkin.css"/> <resource type="download" name="plain.css" location="/css/plain.css"/> <resource type="download" name="sequence-diagrams.js" location="/js/sequence-diagrams.743cf0e5.js"/> <resource type="download" name="connect-api.js" location="/js/connect-api.js"/> <resource type="download" name="addon.js" location="/js/addon.js"/> </web-resource> <servlet name="Diagram IFrame Servlet" key="sequence-diagram-servlet" class="com.pstreule.SequenceDiagramIFrame"> <url-pattern>/sequence-diagram</url-pattern> </servlet> <xhtml-macro name="sequence-diagram" class="com.pstreule.SequenceDiagramMacro" key="sequence-diagram-macro"> <parameters> <parameter name="theme" type="enum" required="true"> <value name="Confluence"/> <value name="Blueprint"/> <value name="Napkin"/> <value name="Plain"/> </parameter> <parameter name="width" type="string"/> </parameters> </xhtml-macro>
  • 22. Render the iFrame public SequenceDiagramMacro(TemplateRenderer renderer, ApplicationProperties applicationProperties) { this.renderer = renderer; this.applicationProperties = applicationProperties; } @Override public String execute(Map<String, String> parameters, String storageFormatBody, ConversionContext conversionContext) throws MacroExecutionException { ContentEntityObject entity = conversionContext.getEntity(); String baseUrl = applicationProperties.getBaseUrl(); URI uri = UriBuilder.fromPath(baseUrl + "/plugins/servlet/sequence-diagram") .queryParam("pid", entity.getIdAsString()) .queryParam("pv", entity.getVersion()) .queryParam("mh", DigestUtils.md5Hex(storageFormatBody)) .queryParam("theme", parameters.get("theme")) .build(); Map<String, Object> context = new HashMap<>(); context.put("url", uri.toString()); context.put("name", "preview".equals(conversionContext.getOutputType()) ? storageFormatBody : ""); return render("/templates/iframe.vm", context); } <iframe src="$url" name="$name" frameborder="0" rel="nofollow" scrolling="no"></iframe>
  • 23. Render the iFrame Content public class SequenceDiagramIFrame extends HttpServlet { private final TemplateRenderer renderer; private final PageBuilderService pageBuilderService; public SequenceDiagramIFrame(TemplateRenderer renderer, PageBuilderService pageBuilderService) { this.renderer = renderer; this.pageBuilderService = pageBuilderService; } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setStatus(HttpServletResponse.SC_OK); resp.setContentType("text/html"); StringWriter writer = new StringWriter(); WebResourceAssembler assembler = pageBuilderService.assembler(); assembler.assembled().drainIncludedResources(); assembler.resources().requireWebResource("com.pstreule.sequence-diagram:sequence-diagram-resources"); WebResourceSet set = assembler.assembled().drainIncludedResources(); set.writeHtmlTags(writer, UrlMode.RELATIVE); renderer.render("/templates/sequence-diagram.vm", Collections.singletonMap("resourcesWithHtml", writer.toString()), resp.getWriter()); } } <html> <head> $resourcesWithHtml </head> <body> <div id="sequence-diagram"></div> <script type="application/javascript"> renderSequenceDiagram("sequence-diagram", window.name) </script> </body> </html>
  • 24. Add-On JavaScript Code function renderSequenceDiagram(id, previewMacroBody) { function render(diagramCode) { function onRenderSuccess(dimensions) { AP.resize(dimensions.w + 10, dimensions.h + 10); } var themeName = Loader.getUrlParameter("theme"); var theme = SequenceDiagramThemes.byName(themeName); var controller = new SequenceDiagramController(id); controller.renderSequenceDiagram(diagramCode, theme, null, onRenderSuccess, onRenderError); } function loadMacroAndRender() { function onLoadSuccess(responseBody) { var diagramCode = JSON.parse(responseBody).body; render(diagramCode); } AP.require(['request'], function (request) { var pageId = Loader.getUrlParameter("pid"); var pageVersion = Loader.getUrlParameter("pv"); var macroHash = Loader.getUrlParameter("mh"); request({ url: "/rest/api/content/" + pageId + "/history/" + pageVersion + "/macro/hash/" + macroHash, success: onLoadSuccess }); }); } loadMacroAndRender(); }
  • 25. Connect JS Shim var AP = {}; (function (AP) { var AJS = window.parent.AJS; var $ = AJS.$; _modules = { 'request': function (url, args) { … $.ajax({ url: url, … }).then(done, fail); } }; AP.require = function (modules, callback) { var requiredModules = Array.isArray(modules) ? modules : [modules]; var args = requiredModules.map(function (module) { return _modules[module]; }); callback.apply(window, args); }; AP.resize = function (width, height) { AJS.$(window.frameElement).css({width: width, height: height}); }; })(AP);
  • 28. Run in Process Browser Product – Server P2 Plugin Add-On UI Add-On JS AssetsAPI Connect JS Shim Adapter
  • 29. Run in Process Browser Product – Server P2 Plugin Add-On UI Add-On JS AssetsAPI Connect JS Shim Adapter No cross-domain bridge needed
  • 30. Run in Process Browser Product – Server P2 Plugin Add-On UI Add-On JS AssetsAPI Connect JS Shim AdapterEvent Handling, Storage
  • 31. Run in Process Browser Product – Server P2 Plugin Add-On UI Add-On JS AssetsAPI Connect JS Shim Adapter 100% Reusable 100% Reusable
  • 32. Run in Process Browser Product – Server P2 Plugin Add-On UI Add-On JS AssetsAPI Connect JS Shim Adapter 100% reusable (maybe)
  • 33. Use Client-Side Rendering as much as Possible
  • 34. Shrink the server-side Browser Product – Server P2 Plugin Add-On UI Add-On JS AssetsAPI Connect JS Shim
  • 36. Use Entity Properties for Data Storage POST /rest/api/content/{content_ID}/property Content-Type: application/json { "key" : "attachment", "value" : { "size": 234374, "description": "My description" ... } } GET /rest/api/content/{content_ID}/propertyGET /rest/api/2/issue/{key}/properties/ PUT /rest/api/2/issue/{key}/properties/ attachment Content-Type: application/json { "size": 234374, "description": "My description" ... }
  • 37. Use Entity Properties for Data Storage { "jiraEntityProperties": [ { "keyConfigurations": [ { "propertyKey": "attachment", "extractions": [ { "objectName": "attachment.size", "type": "number", "alias": "attachmentSize" } ] } ], "entityType": "issue", "name": { "value": "Attachment Index Document" } } ] } { "confluenceContentProperties": [ { "keyConfigurations": [ { "propertyKey": "attachment", "extractions": [ { "objectName": "attachment.size", "type": "number" } ] } ], "name": { "value": "Attachment Index Document" } } ] }
  • 38. Use client-side REST Calls // Get the content properties of an issue AP.require(['request'], function(request){ request({ url: '/rest/api/2/issue/' + issueKey + '/properties/', success: function(responseText){ var properties = JSON.parse(responseText); … } }); });
  • 39. Expose REST API for Server-Side Logic
  • 40. JAX-RS/Jersey @Path("my-resource") public class MyResource { @GET @Produces("application/json") public Response getSomething(@Context HttpServletRequest request) { // ... return Response.ok(); } }
  • 42. Thank you! PATRICK STREULE • ARCHITECT • ATLASSIAN • @PSTREULE
  • 43. Connect everywhere - Cloud and Server Submit your feedback: go.atlassian.com/acconnectcloud