diff --git a/manifest.yml b/manifest.yml index 6962afe..c727ac6 100644 --- a/manifest.yml +++ b/manifest.yml @@ -1,17 +1,26 @@ +# https://developer.atlassian.com/platform/forge/manifest-reference/#app +app: + id: ari:cloud:ecosystem::app/f434bcc5-834f-45e5-ba1d-62e2ee8952cd + runtime: + name: nodejs20.x # https://developer.atlassian.com/platform/forge/manifest-reference/#runtime + licensing: + enabled: false # https://developer.atlassian.com/platform/marketplace/listing-forge-apps/#enabling-licensing-for-your-app + +# https://developer.atlassian.com/platform/forge/manifest-reference/resources/#resources +resources: + - key: main + path: src/frontend/index.jsx + modules: # Trigger is used to invoke a function when a Jira issue event is fired. # https://developer.atlassian.com/platform/forge/manifest-reference/modules/trigger/ - # trigger: - # - key: issue-trigger - # function: webhook - # # https://developer.atlassian.com/platform/forge/events-reference/jira/#issue-events - # events: - # - avi:jira:created:issue - # - avi:jira:updated:issue - # filter: - # ignoreSelf: true # Prevents infinite loops by ignoring self-generated events - # # expression: event.issue.fields.issuetype.name == 'Bug' # Optional: example filter for bug issues only - # onError: IGNORE_AND_LOG # Will invoke function and log errors + trigger: + - key: installation-trigger + function: webhook + events: + # https://developer.atlassian.com/platform/forge/events-reference/life-cycle/ + - avi:forge:installed:app # Doesn't include un-installation + - avi:forge:upgraded:app # The jira module provides functionality for Jira products. jira:issuePanel: @@ -27,18 +36,10 @@ modules: function: - key: resolver handler: index.handler - # - key: webhook - # handler: webhook.handler - -resources: - - key: main - path: src/frontend/index.jsx -app: - runtime: - name: nodejs20.x # Has to be 'sandbox', 'nodejs18.x', 'nodejs20.x' - id: ari:cloud:ecosystem::app/f434bcc5-834f-45e5-ba1d-62e2ee8952cd + - key: webhook + handler: webhook.handler -# Environment variables are not supported in the manifest.yml file. +# Environment variables are not supported in "permissions" section. # https://developer.atlassian.com/platform/forge/manifest-reference/permissions/ # It takes a few hours to 1 day to update here: https://developer.atlassian.com/console/myapps/f434bcc5-834f-45e5-ba1d-62e2ee8952cd/manage/permissions permissions: diff --git a/src/webhook.js b/src/webhook.js index 57ea498..d86e126 100644 --- a/src/webhook.js +++ b/src/webhook.js @@ -1,23 +1,66 @@ -// Handle only webhook events +import forge, { route } from "@forge/api"; + export const handler = async (event, context) => { - console.log("Event: ", event); - console.log("Context: ", context); - const { changelog } = event; - console.log("Changelog: ", changelog); + // https://developer.atlassian.com/platform/forge/events-reference/life-cycle/ + console.log("Installation event payload:", event); + console.log("Context:", context); - if (event.type === "avi:jira:created:issue") { - return handleIssueCreated(event); - } + if (event.eventType === "avi:forge:installed:app") { + console.log("App was installed!"); - if (event.type === "avi:jira:updated:issue") { - return handleIssueUpdated(event); - } -}; + // Extract cloudId and contextToken + const cloudId = event.context.cloudId; + const contextToken = event.contextToken; -const handleIssueCreated = async (event) => { - console.log("Issue created"); -}; + try { + let startAt = 0; + const maxResults = 50; // Number of results per page + let allProjects = []; + let isLastPage = false; + + while (!isLastPage) { + // https://developer.atlassian.com/cloud/jira/platform/rest/v3/api-group-projects/#api-rest-api-3-project-search-get + const url = route`/rest/api/3/project/search?startAt=${startAt}&maxResults=${maxResults}`; + const response = await forge + .asApp() + .requestJira(url, { headers: { Accept: "application/json" } }); + + // Add status code checking and response logging + if (!response.ok) { + console.error("API request failed:", { + status: response.status, + statusText: response.statusText, + body: await response.text(), + }); + throw new Error(`API request failed with status ${response.status}`); + } + + const result = await response.json(); + // console.log("Raw API response:", JSON.stringify(result, null, 2)); + const { values, total, isLast } = result; + + allProjects = [...allProjects, ...values]; + isLastPage = isLast; + startAt += maxResults; + + console.log(`Fetched ${values.length} projects. Total: ${total}. Page complete: ${isLast}`); + } + + console.log( + "All projects found:", + allProjects.map((p) => ({ + id: p.id, + key: p.key, + name: p.name, + })) + ); + } catch (error) { + console.error("Error fetching projects:", error); + } + } else if (event.eventType === "avi:forge:upgraded:app") { + console.log("App was upgraded!"); + // Handle upgrade logic here + } -const handleIssueUpdated = async (event) => { - console.log("Issue updated"); + return { status: 200, body: "Installation event processed" }; };