Jira Issue Picker

/* eslint-disable */

enableDebugMode();

/**
 * Questmate Custom Item - Jira Issue Picker
 *
 * Permissions:
 * @UseApp {ATLASSIAN}
 *
 * Changelog:
 * v0.1 Initial release
 */

return defineCustomItem((Questmate) => {
  Questmate.registerView("ITEM_RUN_VIEW", ({ useRunData, useConfigData }) => {
    const [instanceUrl] = useConfigData("instanceUrl");
    const [projectId] = useConfigData("projectId");
    const [issueId, setIssueId] = useRunData("issueId", null);

    if (instanceUrl === null || projectId === null) {
      return {
        components: [],
      };
    }

    return {
      components: [
        {
          id: "issue",
          type: "dropdown",
          title: "Jira Issue",
          icon: "issue",
          value: issueId,
          onSelect: setIssueId,
          getOptions: async () => {
            const issues = await jiraDataSource.retrieveIssues(instanceUrl, projectId);
            if (!Array.isArray(issues)) {
              console.log(
                "ERROR: Invalid configuration, invalid issues.",
                JSON.stringify({
                  instanceUrl,
                  projectId,
                  issues,
                })
              );
              return [];
            }

            return issues
              .map((issue) => ({
                label: issue.key + ": " + issue.fields.summary,
                value: issue.id,
              }))
              .sort((a, b) =>
                a.label.localeCompare(b.label, undefined, {
                  numeric: true,
                  sensitivity: "base",
                })
              );
          },
          optionNoun: "Issue",
          optionPluralNoun: "Issues",
        },
      ],
    };
  });

  Questmate.registerView("ITEM_CONFIG_VIEW", async ({ useConfigData }) => {
    const [selectedInstanceUrl, setSelectedInstanceUrl] = useConfigData(
      "instanceUrl",
      null
    );
    const [selectedProjectId, setSelectedProjectId] = useConfigData(
      "projectId",
      null
    );

    const viewComponents = [];
    viewComponents.push({
      id: "instance",
      title: "Jira Instance",
      type: "dropdown",
      value: selectedInstanceUrl,
      getOptions: async () => {
        return (await jiraDataSource.retrieveInstances()).map((instance) => ({
          label: instance.name,
          value: instance.id,
        }));
      },
      onSelect: setSelectedInstanceUrl,
      optionNoun: "Instance",
      optionPluralNoun: "Instances",
    });

    if (selectedInstanceUrl !== null) {
      viewComponents.push({
        id: "project",
        title: "Jira Project",
        type: "dropdown",
        value: selectedProjectId,
        getOptions: async () => {
          return (await jiraDataSource.retrieveProjects(selectedInstanceUrl)).map((project) => ({
            label: project.name,
            value: project.id,
          }));
        },
        onSelect: setSelectedProjectId,
        optionNoun: "Project",
        optionPluralNoun: "Projects",
      });
    }

    return {
      components: viewComponents,
    };
  });

  const jiraDataSource = {
    retrieveInstances: async () => {
      const instancesData = await fetch(
        "https://api.atlassian.com/oauth/token/accessible-resources"
      );
      const instances = await instancesData.json();
      return instances.map(instance => ({
        id: instance.id,
        name: instance.name,
        url: instance.url,
        avatarUrl: instance.avatarUrl,
        scopes: instance.scopes
      }));
    },
    retrieveProjects: async (instanceId) => {
      const projectsData = await fetch(
        `https://api.atlassian.com/ex/jira/${instanceId}/rest/api/2/project`
      );
      const projects = await projectsData.json();
      return projects;
    },
    retrieveIssues: async (instanceId, projectId) => {
      const issuesData = await fetch(
        `https://api.atlassian.com/ex/jira/${instanceId}/rest/api/2/search?jql=project=${projectId}`
      );
      const { issues } = await issuesData.json();
      return issues;
    },
  };
});