So I’ve been trying to rework the Atlassian Connect Express tutorial to reuse the same Ngrok tunnel and just restart the server without having to reinstall the app, but I’ve run into a bit of a baffling issue.
Failed to register with host https://XXXXXX.atlassian.net/wiki (400)
{"subCode":"upm.pluginInstall.error.invalid.uri.syntax"}
Add-on not registered; no compatible hosts detected
It’s important to note that I am setting up the NGrok tunnel myself and passing in the local base URL using the AC_LOCAL_BASE_URL environment variable. It’s pretty insane that you have to reinstall the entire app anytime you want a change to be made to the server code, since the tunnel gets reset on every change (which breaks the app).
It’s trying to tell me that my base URL has invalid syntax, but can’t find out why. I’m able to access the atlassian-connect.json from my ngrok URL and it appears to be identical to a stock tutorial project (that works). Strangely, I don’t get a single GET request to my local server unless I navigate to the atlassian-connect.json file manually in a browser. So it seems that there is a weird networking thing going on that I am obviously missing here.
Here’s a bunch of info about my setup:
The NGrok start:
const url = await ngrok.connect({
proto: 'http',
addr: '3000'
});
Running Nodemon that will start the app with the right base URL:
nodemon({
script: "src/app.js",
exec: `SET AC_LOCAL_BASE_URL=${url} && node -r esm`
}).on('start', function () {
console.log('App has started');
})
A copy of the atlassian-connect.json:
{
"key": "my-app",
"name": "My app",
"description": "My very first app",
"baseUrl": "https://d6e6a363ec3e.ngrok.io",
"enableLicensing": true,
"authentication": {
"type": "jwt"
},
"lifecycle": {
"installed": "/installed"
},
"scopes": [
"READ"
],
"modules": {
"generalPages": [
{
"key": "hello-world-page-jira",
"location": "system.top.navigation.bar",
"name": {
"value": "Hello World"
},
"url": "/hello-world",
"conditions": [
{
"condition": "user_is_logged_in"
}
]
},
{
"key": "hello-world-page-confluence",
"location": "system.header/left",
"name": {
"value": "Hello World"
},
"url": "/hello-world",
"conditions": [
{
"condition": "user_is_logged_in"
}
]
}
]
}
}
The app.js file itself is largely unchanged from the tutorial, but here is the whole file anyways:
// Entry point for the app
// Express is the underlying that atlassian-connect-express uses:
// https://expressjs.com
import express from 'express';
// https://expressjs.com/en/guide/using-middleware.html
import bodyParser from 'body-parser';
import compression from 'compression';
import cookieParser from 'cookie-parser';
import errorHandler from 'errorhandler';
import morgan from 'morgan';
// atlassian-connect-express also provides a middleware
import ace from 'atlassian-connect-express';
// Use Handlebars as view engine:
// https://npmjs.org/package/express-hbs
// http://handlebarsjs.com
import hbs from 'express-hbs';
// We also need a few stock Node modules
import http from 'http';
import path from 'path';
import os from 'os';
import helmet from 'helmet';
import nocache from 'nocache';
// Routes live here; this is the C in MVC
import routes from './routes';
import { addServerSideRendering } from './server-side-rendering';
//process.env.FIREBASE_CONFIG =
const admin = require('firebase-admin');
const functions = require('firebase-functions');
const { factory } = require('atlassian-connect-firestore')
admin.initializeApp(functions.config().firebase)
const db = admin.firestore()
ace.store.register('firestore', factory(db))
// Bootstrap Express and atlassian-connect-express
const app = express();
const addon = ace(app);
// See config.json
const port = addon.config.port();
app.set('port', port);
// Log requests, using an appropriate formatter by env
const devEnv = app.get('env') === 'development';
app.use(morgan(devEnv ? 'dev' : 'combined'));
// Configure Handlebars
const viewsDir = path.join(__dirname, 'views');
const handlebarsEngine = hbs.express4();
app.engine('hbs', handlebarsEngine);
app.set('view engine', 'hbs');
//app.set('views', viewsDir);
// Configure jsx (jsx files should go in views/ and export the root component as the default export)
addServerSideRendering(app, handlebarsEngine);
// Atlassian security policy requirements
// http://go.atlassian.com/security-requirements-for-cloud-apps
// HSTS must be enabled with a minimum age of at least one year
app.use(helmet.hsts({
maxAge: 31536000,
includeSubDomains: false
}));
app.use(helmet.referrerPolicy({
policy: ['origin']
}));
// Include request parsers
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(cookieParser());
// Gzip responses when appropriate
app.use(compression());
// Include atlassian-connect-express middleware
app.use(addon.middleware());
// Mount the static files directory
const staticDir = path.join(__dirname, 'public');
app.use(express.static(staticDir));
// Atlassian security policy requirements
// http://go.atlassian.com/security-requirements-for-cloud-apps
app.use(nocache());
// Show nicer errors in dev mode
if (devEnv) app.use(errorHandler());
// Wire up routes
routes(app, addon);
// Boot the HTTP server
http.createServer(app).listen(port, () => {
console.log('App server running at http://' + os.hostname() + ':' + port);
console.log('ENV: AC_LOCAL_BASE_URL = ' + process.env.AC_LOCAL_BASE_URL);
console.log(addon.config.localBaseUrl())
console.log(addon.config.hosts())
// Enables auto registration/de-registration of app into a host in dev mode
if (devEnv) addon.register();
});