Skip to content

Commit 6129711

Browse files
committed
Merge branch 'main' of github.com:atlassian/atlassian-connect-sample-app-node
2 parents fee08bb + 6c685d3 commit 6129711

File tree

7 files changed

+69
-28
lines changed

7 files changed

+69
-28
lines changed

postinstall.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@ const envExampleFileName = `${envFileName}.example`;
66
const envFilePath = path.resolve(__dirname, envFileName);
77
const envExampleFilePath = path.resolve(__dirname, envExampleFileName);
88

9-
// Creates the .env file
9+
/**
10+
* Creates the env file
11+
*/
1012
const createEnvFile = async () => {
1113
if (!fs.existsSync(envFilePath)) {
1214
await fs.copyFile(envExampleFilePath, envFilePath, (err) => {
@@ -19,6 +21,10 @@ const createEnvFile = async () => {
1921
}
2022
};
2123

24+
/**
25+
* This script creates an env file if there isn't one,
26+
* The env file is based off from .env.example
27+
*/
2228
(async function main() {
2329
try {
2430
await createEnvFile();

prestart.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ import * as path from 'path';
44
const envFileName = '.env';
55
const envFilePath = path.resolve(__dirname, envFileName);
66

7+
/**
8+
* Fetching the list of running tunnels
9+
*
10+
* https://ngrok.com/docs/ngrok-agent/api/#request
11+
*/
712
const callTunnel = async () => {
813
const results = await Promise.all([
914
fetch('http://tunnel:4040/api/tunnels').catch(() => undefined),
@@ -38,6 +43,10 @@ const waitForTunnel = async () => {
3843
}
3944
};
4045

46+
47+
/**
48+
* This method update the APP_URL in the env file with the ngrok tunneled URL
49+
*/
4150
(async function main() {
4251
try {
4352
await waitForTunnel();

src/db.ts

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import path from "path";
22
import { chain, ExpChain, remove } from "lodash";
33
import { JSONFile, Low } from "@commonify/lowdb";
44

5+
/**
6+
* Type Definitions for the database
7+
*/
58
export interface JiraTenant {
69
id: string;
710
url: string;
@@ -21,15 +24,22 @@ interface ConnectAppData {
2124
logs: Log[];
2225
}
2326

27+
// Configure lowdb to write to JSONFile
28+
const dbFile = path.join(process.cwd(), "db.json");
29+
const adapter = new JSONFile<ConnectAppData>(dbFile);
30+
31+
// Default data to be added to JSON db
32+
const defaults = {
33+
jiraTenants: [],
34+
logs: []
35+
};
36+
2437
// Extend Low class with a new `chain` field
2538
class LowWithLodash<T> extends Low<T> {
2639
chain: ExpChain<this["data"]> = chain(this).get("data");
2740
}
2841

29-
// Configure lowdb to write to JSONFile
30-
const dbFile = path.join(process.cwd(), "db.json");
31-
const adapter = new JSONFile<ConnectAppData>(dbFile);
32-
42+
// Initializes the db
3343
const initialized = () => {
3444
return (_target: unknown, _propertyKey: string, descriptor: TypedPropertyDescriptor<(...params: any[]) => Promise<any>>) => {
3545
const fn = descriptor.value;
@@ -41,13 +51,9 @@ const initialized = () => {
4151
};
4252
};
4353

44-
// Default data to be added to JSON db
45-
const defaults = {
46-
jiraTenants: [],
47-
logs: []
48-
};
49-
50-
// This is a stand in for any kind of DB/ORM library you'd like to use to store data
54+
/**
55+
* This is a stand-in for any kind of DB/ORM library you'd like to use to store data0
56+
*/
5157
class ConnectAppDatabase extends LowWithLodash<ConnectAppData> {
5258
constructor() {
5359
super(adapter);

src/middlewares/connect-iframe-jwt-middleware.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,11 @@ import { fromExpressRequest } from "atlassian-jwt/dist/lib/jwt";
77
* This middleware decodes the JWT token from Jira, verifies it
88
* And sets the `clientKey` in `res.locals`
99
* The tenant for each instance of the app is recognized based on this `clientKey`
10-
*
11-
* source: https://developer.atlassian.com/cloud/jira/platform/understanding-jwt-for-connect-apps/#decoding-and-verifying-a-jwt-token
12-
*
10+
* https://developer.atlassian.com/cloud/jira/platform/understanding-jwt-for-connect-apps/#decoding-and-verifying-a-jwt-token
1311
*/
1412
export const connectIframeJWTMiddleware = async (req: Request, res: Response, next: NextFunction): Promise<void> => {
1513

16-
// const request = fromExpressRequest(req);
17-
// This is the Jira instance url injected into the iframe url
14+
// This query parameter is injected into the iframe url
1815
const jwt = req.query.jwt as string;
1916

2017
// if JWT is missing, return a 401

src/routes/router.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,23 +9,39 @@ import getMarkdownAndConvertToHtml from "../utils/markup";
99

1010
export const RootRouter = Router();
1111

12-
// Healthcheck route to make sure the server works
13-
RootRouter.get("/healthcheck", (_req, res) => res.status(200).send("Works!"));
14-
15-
// This is the Connect JSON app descriptor
12+
/************************************************************************************************************************
13+
* Healthcheck
14+
************************************************************************************************************************/
15+
RootRouter.get("/healthcheck", (_req, res) => res.status(200).send("Healthy!"));
16+
17+
/************************************************************************************************************************
18+
* Connect app descriptor
19+
************************************************************************************************************************/
1620
RootRouter.get("/atlassian-connect.json", connectDescriptorGet);
1721

18-
// Public files like images and stylesheets
22+
/************************************************************************************************************************
23+
* Public files(images, stylesheets)
24+
************************************************************************************************************************/
1925
RootRouter.use("/public", Static(path.join(process.cwd(), "static")));
2026

21-
// The Connect lifecycle events as specified in the Connect JSON above
27+
/************************************************************************************************************************
28+
* Connect lifecycle Events
29+
************************************************************************************************************************/
2230
RootRouter.use("/events", eventsRouter);
2331

24-
// Jira webhooks we listen to as specified in the Connect JSON above
32+
/************************************************************************************************************************
33+
* Webhooks
34+
************************************************************************************************************************/
2535
RootRouter.use("/webhooks", webhooksRouter);
2636

37+
/************************************************************************************************************************
38+
* Middlewares
39+
************************************************************************************************************************/
2740
RootRouter.use(connectIframeJWTMiddleware);
2841

42+
/************************************************************************************************************************
43+
* Views
44+
************************************************************************************************************************/
2945
RootRouter.get("/", (_req: Request, res: Response): void => {
3046
res.render("introduction", {
3147
pageContent: getMarkdownAndConvertToHtml("introduction.md")

src/routes/webhooks.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ webhooksRouter.post("/jira/issue-created", async (req, res) => {
1919
});
2020

2121
webhooksRouter.post("/jira/issue-updated", async (req, res) => {
22-
// Mapping the host set during the installation with the url coming in from the webhooks
2322
const host = new URL(req.body.user.self).origin;
2423
const tenant = await database.findJiraTenant({ url: host });
2524
await database.addLogs({
@@ -33,7 +32,6 @@ webhooksRouter.post("/jira/issue-updated", async (req, res) => {
3332
});
3433

3534
webhooksRouter.post("/jira/issue-deleted", async (req, res) => {
36-
// Mapping the host set during the installation with the url coming in from the webhooks
3735
const host = new URL(req.body.user.self).origin;
3836
const tenant = await database.findJiraTenant({ url: host });
3937
await database.addLogs({

src/utils/markup.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ import { marked } from "marked";
55

66
const renderer = new marked.Renderer();
77

8+
/**
9+
* Converting the two types of links in the markdown files
10+
* 1. External URLs containing `http` or `https`
11+
* 2. Internal URLs containing the Connect module keys
12+
* (Connect page navigation is handled in `static/js/index.js`)
13+
*/
814
renderer.link = (href: string, _, text: string): string => {
915
if (href?.includes("https" || href?.includes("http"))) {
1016
return `<a target="_blank" href="${href}">${text}</a>`;
@@ -13,11 +19,15 @@ renderer.link = (href: string, _, text: string): string => {
1319
return `<span class="link-span" id="${page}" data-connect-module-key="${page}">${text}</span>`;
1420
}
1521
};
22+
23+
/**
24+
* Converts the content of the markdown files to corresponding HTML
25+
*/
1626
const getMarkdownAndConvertToHtml = (fileName: string): string => {
1727
const filePath = path.join(__dirname, "..", "content", fileName);
1828
const contents = fs.readFileSync(filePath);
1929
// TODO - see if there's a way to modify the way we are using marked.js so we can pass data directly to HTML elements
20-
const markdownToHtml = marked.parse(contents.toString(), { renderer: renderer });
30+
const markdownToHtml = marked.parse(contents.toString(), { renderer });
2131

2232
return sanitizeHtml(markdownToHtml, {
2333
allowedAttributes: {
@@ -27,5 +37,4 @@ const getMarkdownAndConvertToHtml = (fileName: string): string => {
2737
});
2838
};
2939

30-
3140
export default getMarkdownAndConvertToHtml;

0 commit comments

Comments
 (0)