AtlasCamp 2015: Using add-ons to build add-ons

83
Building add-ons using add-ons DANIEL WESTER CO-FOUNDER WITTIFIED @DWESTER42A

Transcript of AtlasCamp 2015: Using add-ons to build add-ons

Building add-ons using add-ons

DANIEL WESTER • CO-FOUNDER • WITTIFIED • @DWESTER42A

Building add-ons using add-ons

DANIEL WESTER • CO-FOUNDER • WITTIFIED • @DWESTER42A

About me

20%

Web Fragment Finder

My button

A C T I V E O B J E C T S

p2 addon

A D D I N G A W E B I T E M

W E B R E S O U R C E S

E V E N T S

G E T T I N G S TA R T E D

A C T I V E O B J E C T S

G E T T I N G S TA R T E D

p2 addon

A D D I N G A W E B I T E M

W E B R E S O U R C E S

E V E N T S

B A S I C D E S C R I P TO R

<atlassian-plugin key="${project.groupId}.${project.artifactId}" name="${project.name}" plugins-version="2"> <plugin-info> <description>${project.description}</description> <version>${project.version}</version> <vendor name="${project.organization.name}" url="${project.organization.url}" /> <param name="plugin-icon">images/pluginIcon.png</param> <param name="plugin-logo">images/pluginLogo.png</param> </plugin-info>

<component-import key="applicationProperties" interface="com.atlassian.sal.api.ApplicationProperties" /> <component-import key="eventPublisher" interface="com.atlassian.event.api.EventPublisher"/>

<component-import key="ao" name="Active Objects service" interface="com.atlassian.activeobjects.external.ActiveObjects"> <description>Active Objects functionality from the plugin</description> </component-import> <ao key="ao-module"> <description>Active Objects entries</description> <entity>com.wittified.pres.DashView</entity> </ao>

<component key="dash-tracker-service" class="com.wittified.pres.DashTrackerServiceImpl"> <interface>com.wittified.pres.DashTrackerService</interface> </component>

<rest key="rest-end-points" path="/wittified/dashboards" version="1.0"> <description>REST interface to integrate with things</description> </rest>

<resource type="i18n" name="i18n" location="dashboard-tracker"/> <web-resource key="atlascamp-dashboard-tracker-resources" name="atlascamp-dashboard-tracker Web Resources"> <dependency>com.atlassian.auiplugin:ajs</dependency> <dependency>com.atlassian.auiplugin:dialog2</dependency> <resource type="download" name="dashboard-tracker.css" location="/css/dashboard-tracker.css"/> <resource type="download" name="dashboard-tracker.js" location="/js/dashboard-tracker.js"/> <resource type="download" name="images/" location="/images"/>

<transformation extension="soy"> <transformer key="soyTransformer"/> </transformation>

<resource type="download" name="dashboard-counter.soy.js" location="/soy/TabularData.soy"/>

<context>atl.general</context> </web-resource>

</atlassian-plugin>

A C T I V E O B J E C T S E N T I T Y

package com.wittified.pres;

import net.java.ao.Entity;import net.java.ao.schema.PrimaryKey;

public interface DashView extends Entity{ public Long getDashboard(); public void setDashboard(Long dashboard);

public Long getViews(); public void setViews( Long count);}

A C T I V E O B J E C T S S E RV I C E

@Override @Transactional public void trackThis(long dashboard) { DashView[] dashViews = this.activeObjects.find(DashView.class, Query.select().where(" DASHBOARDS = ? ", dashboard) ); DashView dashView = null; if(dashViews.length==0) { dashView = this.activeObjects.create( DashView.class); dashView.setDashboard( dashboard); dashView.setViews(0L); } else { dashView = dashViews[0]; } dashView.setViews(dashView.getViews() + 1); dashView.save(); }

R E S T

@GET @Produces({MediaType.APPLICATION_JSON}) @Path("/all") public Response getAll() { List<DashBoardEntity> dashBoardEntityList = new ArrayList<DashBoardEntity>(); for(DashView dashView: this.dashTrackerService.getAll()) { PortalPage page = this.portalPageManager.getPortalPageById( dashView.getDashboard()); DashBoardEntity dashBoardEntity = new DashBoardEntity(); dashBoardEntity.setName( page.getName()); dashBoardEntity.setCount( dashView.getViews()); dashBoardEntity.setId( dashView.getDashboard()); if(page.getOwner()==null) { dashBoardEntity.setOwner("None"); } else { dashBoardEntity.setOwner(page.getOwner().getDisplayName()); }

dashBoardEntityList.add( dashBoardEntity); } return Response.ok( dashBoardEntityList).build(); }

E V E N T L I S T E N E R

public class DashBoardEventListener implements InitializingBean, DisposableBean{ private final EventPublisher eventPublisher; private final DashTrackerService dashTrackerService;

public DashBoardEventListener( final EventPublisher eventPublisher, final DashTrackerService dashTrackerService) { this.eventPublisher = eventPublisher; this.dashTrackerService = dashTrackerService; }

@Override public void destroy() throws Exception { this.eventPublisher.unregister( this); }

@Override public void afterPropertiesSet() throws Exception {

this.eventPublisher.register( this); }}

A C T I V E O B J E C T S

E V E N T S

p2 addon

A D D I N G A W E B I T E M

W E B R E S O U R C E S

G E T T I N G S TA R T E D

E V E N T L I S T E N E R

@EventListener public void onDashboardViewEvent( DashboardViewEvent dashboardViewEvent) { this.dashTrackerService.trackThis( dashboardViewEvent.getId() ); }

E V E N T S

A C T I V E O B J E C T S

p2 addon

A D D I N G A W E B I T E M

W E B R E S O U R C E S

G E T T I N G S TA R T E D

A C T I V E O B J E C T S E RV I C E E R R O R

A C T I V E O B J E C T S E RV I C E E R R O R

DashView[] dashViews = this.activeObjects.find(DashView.class, Query.select().where(" DASHBOARDS = ? ", dashboard) );

A C T I V E O B J E C T S E RV I C E F I X E D

DashView[] dashViews = this.activeObjects.find(DashView.class, Query.select().where(" DASHBOARD = ? ", dashboard) );

A C T I V E O B J E C T S

A D D I N G A W E B I T E M

p2 addon

E V E N T S

W E B R E S O U R C E S

G E T T I N G S TA R T E D

T E M P L AT E I Z E D W E B - I T E M

<web-item key="web-item-example" name="My Web Item" section="gadgets.dashboard.menu" weight=“200">

<description key="my-web-item-desc">General description</description><label key="my-web-item-label" /><link linkId=“my-web-item-link”>/my-web-item</link>

</web-item>

F L U S H E D O U T W E B - I T E M

<web-item key="web-item-dashboard-usage" name="DashBoard Usage" section="gadgets.dashboard.menu" weight="200"> <description key="my-web-item-desc">General description</description> <label key="Dashboard usage" /> <link linkId=“dashboard-usagelink">/</link></web-item>

A C T I V E O B J E C T S

W E B R E S O U R C E S

p2 addon

E V E N T S

A D D I N G A W E B I T E M

G E T T I N G S TA R T E D

W E B R E S O U R C E E N T Y: G E N E R A L

<web-resource key="atlascamp-dashboard-tracker-resources" name="atlascamp-dashboard-tracker Web Resources"> <dependency>com.atlassian.auiplugin:ajs</dependency> <dependency>com.atlassian.auiplugin:dialog2</dependency> <resource type="download" name="dashboard-tracker.css" location="/css/dashboard-tracker.css"/> <resource type="download" name="dashboard-tracker.js" location="/js/dashboard-tracker.js"/> <resource type="download" name="images/" location="/images"/>

<transformation extension="soy"> <transformer key="soyTransformer"/> </transformation> <resource type="download" name="dashboard-counter.soy.js" location="/soy/TabularData.soy"/> <context>atl.general</context> </web-resource>

W E B R E S O U R C E E N T Y: S P E C I F I C

<web-resource key="atlascamp-dashboard-tracker-resources" name="atlascamp-dashboard-tracker Web Resources"> <dependency>com.atlassian.auiplugin:ajs</dependency> <dependency>com.atlassian.auiplugin:dialog2</dependency> <resource type="download" name="dashboard-tracker.css" location="/css/dashboard-tracker.css"/> <resource type="download" name="dashboard-tracker.js" location="/js/dashboard-tracker.js"/> <resource type="download" name="images/" location="/images"/>

<transformation extension="soy"> <transformer key="soyTransformer"/> </transformation> <resource type="download" name="dashboard-counter.soy.js" location="/soy/TabularData.soy"/> <context>atl.dashboard</context> </web-resource>

A C T I V E O B J E C T S

p2 addon

E V E N T S

A D D I N G A W E B I T E M

G E T T I N G S TA R T E D

W E B R E S O U R C E S

Web Fragment Finder functionality

• Identify Web Items/Panels/Sections/Resource locations• Click to copy xml• Active Objects Inspector• Event inspector

But what about Atlassian Connect?S O M E B O D Y F R O M AT L A S S I A N

”“

B A S I C W E B I T E M S

Atlassian Connect

W O R K I N G W I T H P 2 A D D O N S

M A N A G I N G A C A D D O N S

H T T P

A D D I N G W E B PA N E L S

I S S U E P R O P E R T I E S

A D VA N C E D W E B I T E M S

I N S TA L L AT L A S S I A N C O N N E C T

B A S I C W E B I T E M S

I N S TA L L AT L A S S I A N C O N N E C T

Atlassian Connect

W O R K I N G W I T H P 2 A D D O N S

M A N A G I N G A C A D D O N S

H T T P

A D D I N G W E B PA N E L S

I S S U E P R O P E R T I E S

A D VA N C E D W E B I T E M S

N O R M A L S TA R T I N G O F A C E N A B L E D J I R A

atlas-run-standalone --product jira --version 6.5-OD-03-002 --bundled-plugins com.atlassian.bundles:json-schema-validator-atlassian-bundle:1.0.4,com.atlassian.webhooks:atlassian-webhooks-plugin:2.0.0,com.atlassian.jwt:jwt-plugin:1.2.2,com.atlassian.upm:atlassian-universal-plugin-manager-plugin:2.19-D20150321T001323,com.atlassian.plugins:atlassian-connect-plugin:1.1.27 --jvmargs -Datlassian.upm.on.demand=true

(as of May 5th 2015)

S TA R T I N G O F A C E N A B L E D J I R A W I T H W E B F R A G S

atlas-run-standalone --product jira

I N S TA L L AT L A S S I A N C O N N E C T

B A S I C W E B I T E M S

Atlassian Connect

W O R K I N G W I T H P 2 A D D O N S

M A N A G I N G A C A D D O N S

H T T P

A D D I N G W E B PA N E L S

I S S U E P R O P E R T I E S

A D VA N C E D W E B I T E M S

B A S I C W E B - I T E M F R A G M E N T

{ "url": "/my-web-item", "location": "system.top.navigation.bar", "context": "addon", "weight": 200, "target": { "type": "page" }, "styleClasses": [ "webitem", "system-present-webitem" ], "tooltip": { "value": "Example tooltip" }, "icon": { "width": 16, "height": 16, "url": "/maps/icon.png" }, "key": "web-item-example", "name": { "value": "My Web Item" }}

B A S I C W E B I T E M S

W O R K I N G W I T H P 2 A D D O N S

Atlassian Connect

M A N A G I N G A C A D D O N S

H T T P

A D D I N G W E B PA N E L S

I S S U E P R O P E R T I E S

A D VA N C E D W E B I T E M S

I N S TA L L AT L A S S I A N C O N N E C T

B A S I C W E B I T E M S

M A N A G I N G A C A D D O N S

Atlassian Connect

W O R K I N G W I T H P 2 A D D O N S

H T T P

A D D I N G W E B PA N E L S

I S S U E P R O P E R T I E S

A D VA N C E D W E B I T E M S

I N S TA L L AT L A S S I A N C O N N E C T

B A S I C W E B I T E M S

H T T P

Atlassian Connect

W O R K I N G W I T H P 2 A D D O N S

M A N A G I N G A C A D D O N S

A D D I N G W E B PA N E L S

I S S U E P R O P E R T I E S

A D VA N C E D W E B I T E M S

I N S TA L L AT L A S S I A N C O N N E C T

B A S I C W E B I T E M S

A D D I N G W E B PA N E L S

Atlassian Connect

W O R K I N G W I T H P 2 A D D O N S

M A N A G I N G A C A D D O N S

H T T P

I S S U E P R O P E R T I E S

A D VA N C E D W E B I T E M S

I N S TA L L AT L A S S I A N C O N N E C T

B A S I C W E B I T E M S

I S S U E P R O P E R T I E S

Atlassian Connect

W O R K I N G W I T H P 2 A D D O N S

M A N A G I N G A C A D D O N S

H T T P

A D D I N G W E B PA N E L S

A D VA N C E D W E B I T E M S

I N S TA L L AT L A S S I A N C O N N E C T

B A S I C W E B I T E M S

A D VA N C E D W E B I T E M S

Atlassian Connect

W O R K I N G W I T H P 2 A D D O N S

M A N A G I N G A C A D D O N S

H T T P

A D D I N G W E B PA N E L S

I S S U E P R O P E R T I E S

I N S TA L L AT L A S S I A N C O N N E C T

Web Fragment Finder functionality

• Identify Web Items/Panels/Sections/Resource locations• Click to copy xml• Active Objects Inspector• Event inspector

Web Fragment Finder functionality

• Identify Web Items/Panels/Sections/Resource locations

• Click to copy xml• Active Objects Inspector• Event inspector

• Identify Web Items/Panels/Sections/Resource locations

• Click to copy json• Manage Descriptors• Start node applications• HTTP traffic analyser• Inspect properties

p2 AC

F I N D I N G T H E L O C AT I O N S

for (WebItemModuleDescriptor w : this.pluginAccessor.getEnabledModuleDescriptorsByClass( WebItemModuleDescriptor.class)){ …}for (WebSectionModuleDescriptor w :

this.pluginAccessor.getEnabledModuleDescriptorsByClass( WebSectionModuleDescriptor.class)){ …}for (WebPanelModuleDescriptor w : this.pluginAccessor.getEnabledModuleDescriptorsByClass( WebPanelModuleDescriptor.class)){ …}

G E N E R AT I N G T H E P L U G I N

File file = File.createTempFile("plugin-generator",".jar");

FileOutputStream fos = new FileOutputStream(file); ZipOutputStream zipOut = new ZipOutputStream( fos );

Map<String, String> resourceJS = this.generateWebResources();

for (String item: resourceJS.keySet()) { zipOut.putNextEntry( new ZipEntry( String.format( "web-resources/%s.js", item)) ); zipOut.write( resourceJS.get( item).getBytes()); }

zipOut.putNextEntry(new ZipEntry("atlassian-plugin.xml")); zipOut.write(this.generatePluginXml().getBytes());

zipOut.putNextEntry(new ZipEntry("com/wittified/fragfinder/conditions/WebItemCondition.class")); IOUtils.copy(this.getClass().getClassLoader().getResourceAsStream( "com/wittified/fragfinder/conditions/WebItemCondition.class"), zipOut);

PluginArtifact pluginArtifact = this.pluginArtifactFactory.create(file.toURI()); this.pluginController.installPlugins(pluginArtifact);

F I N D I N G E V E N T S

@EventListenerpublic void onAllEvents( Object eventObject){ String className = eventObject.getClass().getName(); ….}

P R O X Y S E RV E R B U I LT I N

public class PluginEnablingListener implements LifecycleAware, DisposableBean{

public void onStart(){

this.httpProxyService.start();}

public void destroy(){

this.httpProxyService.stop();}

}

AT L A S S I A N C O N N E C T I N S TA L L AT I O N

{"validFrom":"2015-05-05T04:02:59.097Z","validTo":"OPEN","environment":{"dev":{"connectVersion":"1.1.29","jiraCommand":"atlas-run-standalone --product jira --version 6.5-OD-03-003 --bundled-plugins com.atlassian.bundles:json-schema-validator-atlassian-bundle:1.0.4,com.atlassian.webhooks:atlassian-webhooks-plugin:2.0.0,com.atlassian.jwt:jwt-plugin:1.2.2,com.atlassian.upm:atlassian-universal-plugin-manager-plugin:2.19-D20150504T170409,com.atlassian.plugins:atlassian-connect-plugin:1.1.29 --jvmargs -Datlassian.upm.on.demand=true","confluenceCommand":"atlas-run-standalone --product confluence --version 5.8-OD-51-005 --bundled-plugins com.atlassian.bundles:json-schema-validator-atlassian-bundle:1.0.4,com.atlassian.webhooks:atlassian-webhooks-plugin:1.0.6,com.atlassian.jwt:jwt-plugin:1.2.2,com.atlassian.upm:atlassian-universal-plugin-manager-plugin:2.19-D20150504T170409,com.atlassian.plugins:atlassian-connect-plugin:1.1.29 --jvmargs -Datlassian.upm.on.demand=true"},"prd":{"connectVersion":"1.1.27","jiraCommand":"atlas-run-standalone --product jira --version 6.5-OD-03-002 --bundled-plugins com.atlassian.bundles:json-schema-validator-atlassian-bundle:1.0.4,com.atlassian.webhooks:atlassian-webhooks-plugin:2.0.0,com.atlassian.jwt:jwt-plugin:1.2.2,com.atlassian.upm:atlassian-universal-plugin-manager-plugin:2.19-D20150321T001323,com.atlassian.plugins:atlassian-connect-plugin:1.1.27 --jvmargs -Datlassian.upm.on.demand=true","confluenceCommand":"atlas-run-standalone --product confluence --version 5.8-OD-50-011 --bundled-plugins com.atlassian.bundles:json-schema-validator-atlassian-bundle:1.0.4,com.atlassian.webhooks:atlassian-webhooks-plugin:1.0.6,com.atlassian.jwt:jwt-plugin:1.2.2,com.atlassian.upm:atlassian-universal-plugin-manager-plugin:2.19-D20150422T024538,com.atlassian.plugins:atlassian-connect-plugin:1.1.27 --jvmargs -Datlassian.upm.on.demand=true"}}}

source: https://developer.atlassian.com/static/connect/commands/connect-versions.json

* Sorry Crowd

Available for almost all apps*

Dig into the resources

Use Web Fragment Finder

Thank you!

DANIEL WESTER • CO-FOUNDER • WITTIFIED • @DWESTER42A