atlascamp 2014: static connect add-ons

62
June 3-5, 2014 | Berlin, Germany

Upload: atlassian

Post on 17-May-2015

540 views

Category:

Technology


0 download

DESCRIPTION

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.

TRANSCRIPT

Page 1: AtlasCamp 2014: Static Connect Add-ons

June 3-5, 2014 | Berlin, Germany

Page 2: AtlasCamp 2014: Static Connect Add-ons

@kannonboy

Timothy Pettersen, Developer Advocate, Atlassian

Static Connect Add-ons

Page 3: AtlasCamp 2014: Static Connect Add-ons

BRACE YOURSELVESBRACE YOURSELVES

ATLASSIAN CONNECT IS COMINGATLASSIAN CONNECT IS COMING

Page 4: AtlasCamp 2014: Static Connect Add-ons

1/7TH OF A CENT PER USER PER MONTH?1/7TH OF A CENT PER USER PER MONTH?

MY FANTASTIC MARKETPLACE PROFITS SHOULD COVER THAT

MY FANTASTIC MARKETPLACE PROFITS SHOULD COVER THAT

Page 5: AtlasCamp 2014: Static Connect Add-ons

Fantastic Profits

Page 6: AtlasCamp 2014: Static Connect Add-ons

Outrageous Profits

Fantastic Profits

Page 7: AtlasCamp 2014: Static Connect Add-ons

Demo Time

Page 8: AtlasCamp 2014: Static Connect Add-ons

Traditional Web Apps

HTML CSS JS

Browser App Server

GET /some/resource

POST /some/form

Page 9: AtlasCamp 2014: Static Connect Add-ons

Static Web Apps

HTML CSS JS

Browser Web Server

GET /some/resource

Page 10: AtlasCamp 2014: Static Connect Add-ons

Static Web Apps

HTML CSS JS

Browser CDN

GET /some/resource

Cheap & Fast!

Easy to cache

Page 11: AtlasCamp 2014: Static Connect Add-ons

Static Add-Ons

HTML CSS JS

Browser

GET /resource

Full REST API

CDN

X-Domain JavaScript API

Page 12: AtlasCamp 2014: Static Connect Add-ons

4 reasons why static add-ons are awesome

Page 13: AtlasCamp 2014: Static Connect Add-ons

Performance

Page 14: AtlasCamp 2014: Static Connect Add-ons

Scalability

Page 15: AtlasCamp 2014: Static Connect Add-ons

Security

Page 16: AtlasCamp 2014: Static Connect Add-ons

Dev Loop

Page 17: AtlasCamp 2014: Static Connect Add-ons

Static Connect Add-ons

LICENSING

STORING STUFF

ANATOMY OF A STATIC ADD-

WHAT IF I NEED A BACKEND?

Page 18: AtlasCamp 2014: Static Connect Add-ons

Static Connect Add-ons

LICENSING

STORING STUFF

ANATOMY OF A STATIC ADD-

WHAT IF I NEED A BACKEND?

ANATOMY OF A STATIC ADD-

Page 19: AtlasCamp 2014: Static Connect Add-ons

Static Add-Ons

Browser HTML CSS JS

GET /resourceX-Domain JavaScript API

CDN

GET /atlassian-connect.json

Page 20: AtlasCamp 2014: Static Connect Add-ons

Static Add-Ons

Browser CDNHTML CSS JS

GET /resourceX-Domain JavaScript API

GET /atlassian-connect.json

Page 21: AtlasCamp 2014: Static Connect Add-ons

Authentication

authentication: {! "type": “none”!}!

scopes: [! “read”, “write”,!! “delete”, “admin”!]!

Page 22: AtlasCamp 2014: Static Connect Add-ons

Callbacks“lifecycle”: {! "installed": "/installed",! "uninstalled": "/uninstalled",! "enabled": "/enabled",! "disabled": "/disabled"!}

"webhooks": [{! “event": "jira:issue_created",! "url": "/issue-created"!}]

Page 23: AtlasCamp 2014: Static Connect Add-ons

Pages & Web Fragments

“adminPages”: [..],!“generalPages”: [..],!“profilePages”: [..],!“configurePage”: {}

“webSections”: [..],!“webPanels”: [..],!“webItems”: [..]!

Page 24: AtlasCamp 2014: Static Connect Add-ons

Tab Panels

“jiraProjectAdminTabPanels”: [..],!“jiraIssueTabPanels”: [..],!“jiraComponentTabPanels”: [..],!“jiraProfileTabPanels”: [..],!“jiraProjectTabPanels”: [..],!“jiraVersionTabPanels”: [..],!“spaceToolsTabs”: [..]!

Page 25: AtlasCamp 2014: Static Connect Add-ons

Macros

“dynamicContentMacros”: [..]!

“staticContentMacros”: [..]!

Page 26: AtlasCamp 2014: Static Connect Add-ons

Static Add-Ons

HTML CSS JS

Browser

GET /resource

CDN

GET /atlassian-connect.json

X-Domain JavaScript API

Page 27: AtlasCamp 2014: Static Connect Add-ons

Static Add-Ons

HTML CSS JS

Browser

GET /resource

CDN

GET /atlassian-connect.json

X-Domain JavaScript API

Page 28: AtlasCamp 2014: Static Connect Add-ons

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>!..

Page 29: AtlasCamp 2014: Static Connect Add-ons

no-cache

Static Caching

Browser CDN

HTML

JS, CSS

Cache-Control:

Expires: Tue, 3 Aug 2032 12:00:00 GMT

public

Page 30: AtlasCamp 2014: Static Connect Add-ons

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>!..

Page 31: AtlasCamp 2014: Static Connect Add-ons

Context parametersgeneralPages: [{! "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=…”>

Page 32: AtlasCamp 2014: Static Connect Add-ons

Static Add-Ons

HTML CSS JS

Browser

GET /resource

CDN

GET /atlassian-connect.json

X-Domain JavaScript API

Page 33: AtlasCamp 2014: Static Connect Add-ons

Static Add-Ons

HTML CSS JS

Browser

GET /resource

CDN

GET /atlassian-connect.json

X-Domain JavaScript API

Page 34: AtlasCamp 2014: Static Connect Add-ons

include-all.jsfunction 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);!

});!

}

Page 35: AtlasCamp 2014: Static Connect Add-ons

AP APIincludeAll(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.!

}

Page 36: AtlasCamp 2014: Static Connect Add-ons

Static Connect Add-ons

STORING STUFF

ANATOMY OF A STATIC ADD-

WHAT IF I NEED A BACKEND?

LICENSING

Page 37: AtlasCamp 2014: Static Connect Add-ons

//obfuscated!var _0x81b0=["\x6C\x69\x63","\x61\x63\x74\x69\x76\x65","\x65\x78\x70\x69\x72\x65\x64"];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 parameterswitch (getQueryParameter("lic")) {! case "active":! // license is valid! break;! case "expired":! doExpired();! break;! case "none":! doUnlicensed();! break;!}

Page 38: AtlasCamp 2014: Static Connect Add-ons

We’ve got your back

Page 39: AtlasCamp 2014: Static Connect Add-ons

Static Connect Add-ons

LICENSING

ANATOMY OF A STATIC ADD-

WHAT IF I NEED A BACKEND?

STORING STUFF

Page 40: AtlasCamp 2014: Static Connect Add-ons

Third-party cookies

<iframe>

Page 41: AtlasCamp 2014: Static Connect Add-ons

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);!! }!! !});!

Page 42: AtlasCamp 2014: Static Connect Add-ons

@Tim Pettersen: this page looks old. Please check if we can delete it.

Page 43: AtlasCamp 2014: Static Connect Add-ons

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}) !});

Page 44: AtlasCamp 2014: Static Connect Add-ons

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

Page 45: AtlasCamp 2014: Static Connect Add-ons

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()

Page 46: AtlasCamp 2014: Static Connect Add-ons

Storage Options

AP.cookieVery fast (local)!

Scoped to the browser!

Size limited!

Temporary storage!

Use for user preference, recent

history, etc.

Entity storageSlower (ajax roundtrip)!

Scoped to the entity!

Size limited!

Permanent storage !

Use for annotating issues or pages with

data

Backend

!?

Page 47: AtlasCamp 2014: Static Connect Add-ons

Static Connect Add-ons

STORING STUFF

LICENSING

ANATOMY OF A STATIC ADD-

WHAT IF I NEED A BACKEND?

Page 48: AtlasCamp 2014: Static Connect Add-ons

Are you sure?

Page 49: AtlasCamp 2014: Static Connect Add-ons

Are you sure?

Analytics Mapping Realtime Charting Rendering

Imaging Video AudioGraphs Code

Page 50: AtlasCamp 2014: Static Connect Add-ons

Static limitations

• Web hooks!• Long running tasks!• Indexing!• Aggregation!• Secret credentials!• Notifications

Page 51: AtlasCamp 2014: Static Connect Add-ons

Static Add-Ons

Browser HTML CSS JS

CDN

GET /resourceX-Domain

JavaScript API

Page 52: AtlasCamp 2014: Static Connect Add-ons

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

Page 53: AtlasCamp 2014: Static Connect Add-ons

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)

Page 54: AtlasCamp 2014: Static Connect Add-ons

Send Email

Send Email

Page 55: AtlasCamp 2014: Static Connect Add-ons

Custom backend

Browser CDNGET /resourcespace-graph.io

POST /send-mail

Same origin policy

Page 56: AtlasCamp 2014: Static Connect Add-ons

WebSockets

Browser CDNGET /resourcespace-graph.io

Page 57: AtlasCamp 2014: Static Connect Add-ons

WebSockets

Browserspace-graph.io

// setup websocket & email data!var ws = new WebSocket(“myaddon.io”);!var email = {to: “[email protected]”, ..};!!// 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!”);! }!});

Page 58: AtlasCamp 2014: Static Connect Add-ons

Browserspace-graph.io

CORS (Cross Origin Resource Sharing)

OPTIONS /send-mail

Access-Control-Allow-Origin: https://somecdn.com

POST /send-mail

200 OK

Page 59: AtlasCamp 2014: Static Connect Add-ons

Static Connect Add-ons

LICENSING

STORING STUFF

ANATOMY OF A STATIC ADD-

WHAT IF I NEED A BACKEND?

Page 60: AtlasCamp 2014: Static Connect Add-ons

• Space Graph!• Google Maps Macro!• Who's Looking v2!• Web Sequence Diagrammer v2

Example Static Add-Ons

Page 61: AtlasCamp 2014: Static Connect Add-ons

Performance == happy users

Scalability == happy accountants

Security == happy Atlassian

Dev Loop == happy developers (you!)

Four awesome reasons

Page 62: AtlasCamp 2014: Static Connect Add-ons

tpettersen.bitbucket.org/static

Questions?

@kannonboy