sourcegraph / openctx

See contextual info about code from your dev tools, in your editor, code review, and anywhere else you read code.
https://openctx.org
Apache License 2.0
135 stars 14 forks source link

Jira context provider - Incomplete results #198

Open BobbieERay opened 3 weeks ago

BobbieERay commented 3 weeks ago

Hi, a quick observation, which might be due to my specific Jira setup, but I noticed that the results of the Jira context provider were incomplete. Many of the issues assigned to / reported by me, didn't show up, e.g. issues which are a child of an epic. The subtasks also didn't show up (but I assume this is by design as the showSubTasks flag isn't set in the provider/jira/api.ts searchIssues function).

So I created a quick prototype at my end with the /api/2/issue/picker endpoint and I noticed the same behaviour. Did anyone else notice similar issues?

To work around this problem, I have done some experiments with the /rest/api/2/search endpoint and it returned complete and consistent results. You need to included the project key in the search though. (prototype code below).

Just some food for thought really, as I don't know whether this is a general issue, or just a red herring and related to my Jira setup. Which is pretty plain vanilla however.

// Base parameters
const jiraInstance = '<subdomain>.atlassian.net';
const username = 'name@domain.com';
const apiToken = 'apitoken';

// Base64 encode the username and API token
const encodedCredentials = btoa(`${username}:${apiToken}`);

// Search parameters
const projectKey = '<projectkey>';  // The project key
const userName = '<user display name>; // The user's display name. The email address field for assignee / reporter is not accessible
const searchTerm = '';  // Optional: add a search term

// Construct the JQL query
let jql = `project = ${projectKey} AND (assignee = "${userName}" OR reporter = "${userName}")`;
if (searchTerm) jql += ` AND text ~ "${searchTerm}"`;

// Search function
async function searchAssignedIssues() {
  const maxResults = 100; 

  // Construct the URL for the request
  const apiUrl = `https://${jiraInstance}/rest/api/2/search?jql=${encodeURIComponent(jql)}&maxResults=${maxResults}`;

  // CORS proxy URL (required for in browser prototyping only!!)
  const corsProxy = 'https://cors-anywhere.herokuapp.com/';
  const proxyUrl = `${corsProxy}${apiUrl}`;

  try {
    const response = await fetch(proxyUrl, {
      method: 'GET',
      headers: {
        'Authorization': `Basic ${encodedCredentials}`,
        'Accept': 'application/json',
        'X-Requested-With': 'XMLHttpRequest'
      }
    });

    if (!response.ok) {
      const errorText = await response.text();
      throw new Error(`HTTP error! status: ${response.status}, message: ${errorText}`);
    }

    const data = await response.json();

    console.log(`Fetched ${data.issues.length} issues for assignee ${assigneeUsername} in project ${projectKey}`);

    return data.issues;

  } catch (error) {
    console.error('Error:', error);
    return [];
  }
}

// Function to simplify issue data for output
function simplifyIssueData(issues) {
  return issues.map(issue => ({
    key: issue.key,
    summary: issue.fields.summary,
    status: issue.fields.status.name,
    created: issue.fields.created,
    updated: issue.fields.updated
  }));
}

// Run the search
searchAssignedIssues().then(issues => {
  if (issues.length === 0) {
    console.log('No issues found. Check your search criteria.');
    console.log('JQL used:', jql);
    return '{}';
  } else {
    const simplifiedIssues = simplifyIssueData(issues);
    console.log('Issues fetched successfully.');
    return JSON.stringify(simplifiedIssues, null, 2);
  }
}).then(jsonResult => {
  // List the results
  console.log(jsonResult);
}).catch(error => {
  console.error('Error in main execution:', error);
});