atlascamp 2014: static connect add-ons

Post on 17-May-2015

540 Views

Category:

Technology

0 Downloads

Preview:

Click to see full reader

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

June 3-5, 2014 | Berlin, Germany

@kannonboy

Timothy Pettersen, Developer Advocate, Atlassian

Static Connect Add-ons

BRACE YOURSELVESBRACE YOURSELVES

ATLASSIAN CONNECT IS COMINGATLASSIAN CONNECT IS COMING

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

Fantastic Profits

Outrageous Profits

Fantastic Profits

Demo Time

Traditional Web Apps

HTML CSS JS

Browser App Server

GET /some/resource

POST /some/form

Static Web Apps

HTML CSS JS

Browser Web Server

GET /some/resource

Static Web Apps

HTML CSS JS

Browser CDN

GET /some/resource

Cheap & Fast!

Easy to cache

Static Add-Ons

HTML CSS JS

Browser

GET /resource

Full REST API

CDN

X-Domain JavaScript API

4 reasons why static add-ons are awesome

Performance

Scalability

Security

Dev Loop

Static Connect Add-ons

LICENSING

STORING STUFF

ANATOMY OF A STATIC ADD-

WHAT IF I NEED A BACKEND?

Static Connect Add-ons

LICENSING

STORING STUFF

ANATOMY OF A STATIC ADD-

WHAT IF I NEED A BACKEND?

ANATOMY OF A STATIC ADD-

Static Add-Ons

Browser HTML CSS JS

GET /resourceX-Domain JavaScript API

CDN

GET /atlassian-connect.json

Static Add-Ons

Browser CDNHTML CSS JS

GET /resourceX-Domain JavaScript API

GET /atlassian-connect.json

Authentication

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

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

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

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

Pages & Web Fragments

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

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

Tab Panels

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

Macros

“dynamicContentMacros”: [..]!

“staticContentMacros”: [..]!

Static Add-Ons

HTML CSS JS

Browser

GET /resource

CDN

GET /atlassian-connect.json

X-Domain JavaScript API

Static Add-Ons

HTML CSS JS

Browser

GET /resource

CDN

GET /atlassian-connect.json

X-Domain JavaScript API

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

no-cache

Static Caching

Browser CDN

HTML

JS, CSS

Cache-Control:

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

public

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

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=…”>

Static Add-Ons

HTML CSS JS

Browser

GET /resource

CDN

GET /atlassian-connect.json

X-Domain JavaScript API

Static Add-Ons

HTML CSS JS

Browser

GET /resource

CDN

GET /atlassian-connect.json

X-Domain JavaScript API

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

});!

}

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

}

Static Connect Add-ons

STORING STUFF

ANATOMY OF A STATIC ADD-

WHAT IF I NEED A BACKEND?

LICENSING

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

We’ve got your back

Static Connect Add-ons

LICENSING

ANATOMY OF A STATIC ADD-

WHAT IF I NEED A BACKEND?

STORING STUFF

Third-party cookies

<iframe>

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

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

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

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

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

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

!?

Static Connect Add-ons

STORING STUFF

LICENSING

ANATOMY OF A STATIC ADD-

WHAT IF I NEED A BACKEND?

Are you sure?

Are you sure?

Analytics Mapping Realtime Charting Rendering

Imaging Video AudioGraphs Code

Static limitations

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

Static Add-Ons

Browser HTML CSS JS

CDN

GET /resourceX-Domain

JavaScript API

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

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)

Send Email

Send Email

Custom backend

Browser CDNGET /resourcespace-graph.io

POST /send-mail

Same origin policy

WebSockets

Browser CDNGET /resourcespace-graph.io

WebSockets

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

Browserspace-graph.io

CORS (Cross Origin Resource Sharing)

OPTIONS /send-mail

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

POST /send-mail

200 OK

Static Connect Add-ons

LICENSING

STORING STUFF

ANATOMY OF A STATIC ADD-

WHAT IF I NEED A BACKEND?

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

Example Static Add-Ons

Performance == happy users

Scalability == happy accountants

Security == happy Atlassian

Dev Loop == happy developers (you!)

Four awesome reasons

tpettersen.bitbucket.org/static

Questions?

@kannonboy

top related