AtlasCamp 2014: Static Connect Add-ons

997 views
819 views

Published on

Building Atlassian Connect add-ons with client-server frameworks like Express.JS and Play produces powerful add-ons, but requires hosting and database consideration. In contrast, "static" Connect add-ons are easier to write and far simpler to deploy. A static Connect add-on is simply a set of files (including an atlassian-connect.json descriptor) that are web accessible, either through a web server or via deployment to a CDN. Static add-ons have several advantages. First, infinite scalability. Why pay for CPU when you can let the user agent do the work? Second, simple persistence. Why pay for disk when you have local storage and JIRA & Confluence's persistence REST APIs? Third, extreme performance. Let a worldwide CDN serve your files from the nearest edge - it's literally impossible to compete with a static add-on when it comes to performance! Finally, easy caching. Why bother serving requests when your unchanged static files can be cached indefinitely with simple HTTP caching? In this talk I'll highlight the benefits and restrictions of static add-on architecture, and discuss the nuts and bolts of implementing a static add-on. I'll also show off the static add-on skeleton that will get your static add-on development off to a flying start.

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
997
On SlideShare
0
From Embeds
0
Number of Embeds
7
Actions
Shares
0
Downloads
8
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

AtlasCamp 2014: Static Connect Add-ons

  1. 1. June 3-5, 2014 | Berlin, Germany
  2. 2. @kannonboy Timothy Pettersen, Developer Advocate, Atlassian Static Connect Add-ons
  3. 3. BRACEYOURSELVESBRACEYOURSELVES ATLASSIANCONNECTISCOMINGATLASSIANCONNECTISCOMING
  4. 4. 1/7THOFACENTPERUSERPERMONTH?1/7THOFACENTPERUSERPERMONTH? MYFANTASTICMARKETPLACEPROFITSSHOULD COVERTHAT MYFANTASTICMARKETPLACEPROFITSSHOULD COVERTHAT
  5. 5. Fantastic Profits
  6. 6. Outrageous Profits Fantastic Profits
  7. 7. Demo Time
  8. 8. Traditional Web Apps HTML CSS JS Browser App Server GET /some/resource POST /some/form
  9. 9. Static Web Apps HTML CSS JS Browser Web Server GET /some/resource
  10. 10. Static Web Apps HTML CSS JS Browser CDN GET /some/resource Cheap & Fast! Easy to cache
  11. 11. Static Add-Ons HTML CSS JS Browser GET /resource Full REST API CDN X-Domain JavaScript API
  12. 12. 4 reasons why static add-ons are awesome
  13. 13. Performance
  14. 14. Scalability
  15. 15. Security
  16. 16. Dev Loop
  17. 17. Static Connect Add-ons LICENSING STORING STUFF ANATOMY OF A STATIC ADD- WHAT IF I NEED A BACKEND?
  18. 18. Static Connect Add-ons LICENSING STORING STUFF ANATOMY OF A STATIC ADD- WHAT IF I NEED A BACKEND? ANATOMY OF A STATIC ADD-
  19. 19. Static Add-Ons Browser HTML CSS JS GET /resourceX-Domain JavaScript API CDN GET /atlassian-connect.json
  20. 20. Static Add-Ons Browser CDNHTML CSS JS GET /resourceX-Domain JavaScript API GET /atlassian-connect.json
  21. 21. Authentication authentication: {! "type": “none”! }! scopes: [! “read”, “write”,! ! “delete”, “admin”! ]!
  22. 22. Callbacks “lifecycle”: {! "installed": "/installed",! "uninstalled": "/uninstalled",! "enabled": "/enabled",! "disabled": "/disabled"! } "webhooks": [{! “event": "jira:issue_created",! "url": "/issue-created"! }]
  23. 23. Pages & Web Fragments “adminPages”: [..],! “generalPages”: [..],! “profilePages”: [..],! “configurePage”: {} “webSections”: [..],! “webPanels”: [..],! “webItems”: [..]!
  24. 24. Tab Panels “jiraProjectAdminTabPanels”: [..],! “jiraIssueTabPanels”: [..],! “jiraComponentTabPanels”: [..],! “jiraProfileTabPanels”: [..],! “jiraProjectTabPanels”: [..],! “jiraVersionTabPanels”: [..],! “spaceToolsTabs”: [..]!
  25. 25. Macros “dynamicContentMacros”: [..]! “staticContentMacros”: [..]!
  26. 26. Static Add-Ons HTML CSS JS Browser GET /resource CDN GET /atlassian-connect.json X-Domain JavaScript API
  27. 27. Static Add-Ons HTML CSS JS Browser GET /resource CDN GET /atlassian-connect.json X-Domain JavaScript API
  28. 28. Static Connect Boilerplate <head>! <link rel="stylesheet" href=“//aui-cdn.atlassian.com/../aui.css”> ! <script src=“//cdnjs.cloudflare.com/../jquery.js”></script>! <script src=“//aui-cdn.atlassian.com/../aui.js”></script>! <!--[if IE]>! <script src=“//aui-cdn.atlassian.com/../aui-ie.js”></script>! <![endif]—>! <script src=“//aui-cdn.atlassian.com/../include-all.js”></script>! ! <script src=“//somecdn.com/../space-graph.js”></script>! <link rel="stylesheet" href=“//somecdn.com/../space-graph.css”>! </head>! ..
  29. 29. no-cache Static Caching Browser CDN HTML JS, CSS Cache-Control: Expires: Tue, 3 Aug 2032 12:00:00 GMT public
  30. 30. Versioned URLs <head>! <script src=“//mycdn/../v1/space-graph.js”></script>! <link rel="stylesheet" href=“//mycdn/../v1/space-graph.css”>! </head>! .. <head>! <script src=“//mycdn/../v2/space-graph.js”></script>! <link rel="stylesheet" href=“//mycdn/../v2/space-graph.css”>! </head>! ..
  31. 31. Context parameters generalPages: [{! "key": "space-graph",! "url": "/space-graph.html?spaceKey={space.key}",! "location": "system.content.action",! "name": {! "value": "Space Graph"! }! }] <iframe src=“https://somecdn.com/space-graph.html?spaceKey=JRA”> <iframe src=“https://somecdn.com/space-graph.html?spaceKey=CONF”>! <iframe src=“https://somecdn.com/space-graph.html?spaceKey=…”>
  32. 32. Static Add-Ons HTML CSS JS Browser GET /resource CDN GET /atlassian-connect.json X-Domain JavaScript API
  33. 33. Static Add-Ons HTML CSS JS Browser GET /resource CDN GET /atlassian-connect.json X-Domain JavaScript API
  34. 34. include-all.js function includeAll(callback) {! // parse query parameters from web-panel iframe URL! var host = getQueryParam(“xdm_e”);! var contextPath = getQueryParam(“cp”);! ! // construct URL targeting host application! var allUrl = host + contextPath + “/atlassian-connect/all.js";! ! // retrieve script asynchronously! jQuery.getScript(allUrl, function () {! // window.AP is now available ! // can now launch pop-ups, use REST APIs and interact with host UI! callback(window.AP);! });! }
  35. 35. AP API includeAll(function(AP) {! ! // use the REST API! ! AP.request({! ! ! url: “/rest/prototype/1/space/" + spaceKey + ".json?expand=rootpages”,! ! ! type: “GET”,! ! ! success: function(spaceJson) {! ! ! ! // parse root pages from space object! ! ! }! ! });! ! ! ! // display messages! ! AP.messages.info(“Space Graph”, “Initializing Space Graph for ” + spaceKey);! ! ! // plus AP.events, AP.dialog, AP.cookie, etc.! }
  36. 36. Static Connect Add-ons STORING STUFF ANATOMY OF A STATIC ADD- WHAT IF I NEED A BACKEND? LICENSING
  37. 37. //obfuscated! var _0x81b0=["x6C x69x63","x61x63x74x69x76x65","x65x78x70x69x 72x65x64"];var d=a(_0x81b0[0]);switch(d){case _0x81b0[1]:break;case _0x81b0[2]:b();break;case _0x81b0[1]:c();break;} // minified! var d=a("lic");switch(d) {case"active":break;case"expired":b();break;case"active" :c();break} lic parameter switch (getQueryParameter("lic")) {! case "active":! // license is valid! break;! case "expired":! doExpired();! break;! case "none":! doUnlicensed();! break;! }
  38. 38. We’ve got your back
  39. 39. Static Connect Add-ons LICENSING ANATOMY OF A STATIC ADD- WHAT IF I NEED A BACKEND? STORING STUFF
  40. 40. Third-party cookies <iframe>
  41. 41. AP.cookie // AP.cookie! // .save(name, value, expires);! // .read(name, callback);! // .erase(name); // example: display a welcome message once per user! AP.cookie.read(“seen.welcome.message”, function(val) {!! ! ! if (val !== “true”) {! ! ! displayFirstTimeWelcomeMessage();! ! ! AP.cookie.save(“seen.welcome.message”, “true”, 365);! ! }! ! ! });!
  42. 42. @Tim Pettersen: this page looks old. Please check if we can delete it.
  43. 43. Confluence Content Properties // example: store a JSON object against the selected page! var propertyKey = “note”;! var propertyValue = {created: todaysDate, body: bodyText}; // POST -> CREATE, GET -> RETRIEVE, PUT -> UPDATE, DELETE -> DELETE AP.request({! ! url: “/rest/api/1/experimental/content/“ + pageId + ”/property”,! ! type: “POST”,! contentType: “application/json”,! ! data: JSON.stringify({key: propertyKey, value: propertyValue}) ! });
  44. 44. JIRA Entity Properties // example: store a JSON object against an issue! var propertyKey = “note”;! var propertyValue = {created: todaysDate, body: bodyText};! ! AP.request({! ! url: “/rest/api/2/issue/“ + issueKey + “/properties/” + propertyKey,! ! type: “POST”,! contentType: “application/json”,! ! data: JSON.stringify(propertyValue)! }); // POST -> CREATE, GET -> RETRIEVE, PUT -> UPDATE, DELETE -> DELETE
  45. 45. Indexing Entity Properties "jiraEntityProperties": [{! "name": {! "value" : ”Issue Notes"! },! "entityType": "issue",! "keyConfigurations": [{! "propertyKey" : "note",! "extractions" : [{! "objectName" : "created",! "type" : “date"! }, {! ! ! ! ! "objectName" : "created",! "type" : “text" // or number, string! ! ! }]! }]! }]! ALSO AVAILABLE IN P2 JQL: ! issue.property[note].bodyText ~ “foo” or! issue.property[note].created > startOfMonth()
  46. 46. Storage Options AP.cookie Very fast (local)! Scoped to the browser! Size limited! Temporary storage! Use for user preference, recent history, etc. Entity storage Slower (ajax roundtrip)! Scoped to the entity! Size limited! Permanent storage ! Use for annotating issues or pages with data Backend ! ?
  47. 47. Static Connect Add-ons STORING STUFF LICENSING ANATOMY OF A STATIC ADD- WHAT IF I NEED A BACKEND?
  48. 48. Are you sure?
  49. 49. Are you sure? Analytics Mapping Realtime Charting Rendering Imaging Video AudioGraphs Code
  50. 50. Static limitations • Web hooks! • Long running tasks! • Indexing! • Aggregation! • Secret credentials! • Notifications
  51. 51. Static Add-Ons Browser HTML CSS JS CDN GET /resource X-Domain JavaScript API
  52. 52. Hybrid Add-Ons Browser HTML CSS JS CDN GET /resource X-Domain JavaScript API Service A Service B ? ? • Persistence • Authentication • Email / IM • 3rd party web services • Other server-side stuff
  53. 53. Hybrid Add-Ons Browser • Real-time storage & sync • Authentication • Generous free dev plan (50 connections) • Now with hosting / CDN • Real-time storage & sync • Authentication • Self-hosted • FOSS • Real-time storage & sync • Integration with 3rd party services: Mailgun, Mandrill, SendGrid, Stripe, Twilio • Generous free dev plan (30 req / sec)
  54. 54. Send Email Send Email
  55. 55. Custom backend Browser CDNGET /resourcespace- graph.io POST /send-mail Same origin policy
  56. 56. WebSockets Browser CDNGET /resourcespace- graph.io
  57. 57. WebSockets Browser space- graph.io // setup websocket & email data! var ws = new WebSocket(“myaddon.io”);! var email = {to: “foo@bar.com”, ..};! ! // wait for connection then send data! ws.onopen(function() {! ws.send(JSON.stringify(email));! });! ! // wait for confirmation! ws.onmessage(function(event) {! if (event.data === “ok”) {! AP.messages.info(“Email sent!”);! }! });
  58. 58. Browser space- graph.io CORS (Cross Origin Resource Sharing) OPTIONS /send-mail Access-Control-Allow-Origin: https://somecdn.com POST /send-mail 200 OK
  59. 59. Static Connect Add-ons LICENSING STORING STUFF ANATOMY OF A STATIC ADD- WHAT IF I NEED A BACKEND?
  60. 60. • Space Graph! • Google Maps Macro! • Who's Looking v2! • Web Sequence Diagrammer v2 Example Static Add-Ons
  61. 61. Performance == happy users Scalability == happy accountants Security == happy Atlassian Dev Loop == happy developers (you!) Four awesome reasons
  62. 62. tpettersen.bitbucket.org/static Questions? @kannonboy

×