Switch Jira API calls to GET-based JQL search with project scoping

- getIssue now uses GET /rest/api/2/search with JQL instead of
  GET /rest/api/2/issue/{key} for Charter compliance
- searchIssues switched from POST to GET with URL-encoded query params
- searchIssuesByKeys adds project scoping to JQL clause
- Updated UAT tests and API use-case docs to match
This commit is contained in:
root
2026-04-29 14:12:04 +00:00
parent 4f960d0866
commit 7a179f19a1
3 changed files with 30 additions and 24 deletions

View File

@@ -276,14 +276,15 @@ function jiraDelete(urlPath, options) {
* @param {string[]} [fields] - Jira field names to return
*/
async function getIssue(issueKey, fields) {
const fieldList = (fields || DEFAULT_FIELDS).join(',');
const res = await jiraGet(
`/rest/api/2/issue/${encodeURIComponent(issueKey)}?fields=${encodeURIComponent(fieldList)}`
);
if (res.status === 200) {
return { ok: true, data: JSON.parse(res.body) };
const jql = `key = "${issueKey}" AND project = ${JIRA_PROJECT_KEY}`;
const result = await searchIssues(jql, { fields: fields || DEFAULT_FIELDS, maxResults: 1, startAt: 0 });
if (result.ok && result.data.issues && result.data.issues.length > 0) {
return { ok: true, data: result.data.issues[0] };
}
return { ok: false, status: res.status, body: res.body, rateLimited: res.rateLimited };
if (result.ok && (!result.data.issues || result.data.issues.length === 0)) {
return { ok: false, status: 404, body: 'Issue not found' };
}
return result;
}
/**
@@ -303,7 +304,7 @@ async function searchIssuesByKeys(issueKeys, opts) {
// or similar, but key-based search is inherently scoped. We add updated
// clause for compliance.
const keyList = issueKeys.map(k => `"${k}"`).join(', ');
const jql = `key in (${keyList}) AND updated >= -24h`;
const jql = `key in (${keyList}) AND updated >= -24h AND project = ${JIRA_PROJECT_KEY}`;
const fields = (opts && opts.fields) || DEFAULT_FIELDS;
const maxResults = Math.min((opts && opts.maxResults) || 1000, 1000);
@@ -327,8 +328,10 @@ async function searchIssues(jql, opts) {
const maxResults = Math.min((opts && opts.maxResults) || 1000, 1000);
const fields = (opts && opts.fields) || DEFAULT_FIELDS;
const body = { jql, startAt, maxResults, fields };
const res = await jiraPost('/rest/api/2/search', body);
const fieldList = encodeURIComponent(fields.join(','));
const encodedJql = encodeURIComponent(jql);
const queryString = `?jql=${encodedJql}&fields=${fieldList}&maxResults=${maxResults}&startAt=${startAt}`;
const res = await jiraGet('/rest/api/2/search' + queryString);
if (res.status === 200) {
return { ok: true, data: JSON.parse(res.body) };
}

View File

@@ -273,7 +273,7 @@ async function testJqlSearch() {
assert(projectKey, 'JIRA_PROJECT_KEY must be set');
// Use a broad time window to ensure results even on a quiet project
const jql = `project = ${projectKey} ORDER BY updated DESC`;
const jql = `project = ${projectKey} AND updated >= -24h ORDER BY updated DESC`;
logInfo('Searching with JQL:', jql);
const result = await jiraApi.searchIssues(jql, { maxResults: 10 });
@@ -306,6 +306,8 @@ async function testBulkKeySearch() {
const result = await jiraApi.searchIssuesByKeys(keys);
assert(result.ok, 'Bulk key search should succeed. Got HTTP ' + (result.status || '') + ': ' + ((result.body || '').substring(0, 500)));
logInfo('Bulk search uses project-scoped JQL with project = ' + jiraApi.JIRA_PROJECT_KEY);
const found = (result.data.issues || []).map(i => i.key);
logInfo('Found issues:', found);
assert(found.includes(createdIssueKey), 'Should find the created issue');
@@ -350,7 +352,7 @@ async function main() {
// Run tests in order — later tests depend on the created issue
if (await runTest('1. Connection Test (GET /myself)', testConnection)) passed++; else failed++;
if (await runTest('2. Create Issue (POST /issue)', testCreateIssue)) passed++; else failed++;
if (await runTest('3. Get Single Issue (GET /issue/{key})', testGetIssue)) passed++; else failed++;
if (await runTest('3. Get Single Issue (JQL search)', testGetIssue)) passed++; else failed++;
if (await runTest('4. Update Issue (PUT /issue/{key})', testUpdateIssue)) passed++; else failed++;
if (await runTest('5. Add Comment (POST /issue/{key}/comment)', testAddComment)) passed++; else failed++;
@@ -362,7 +364,7 @@ async function main() {
await testTransitionIssue(transitions);
})) passed++; else failed++;
if (await runTest('8. JQL Search (POST /search)', testJqlSearch)) passed++; else failed++;
if (await runTest('8. JQL Search (GET /search)', testJqlSearch)) passed++; else failed++;
if (await runTest('9. Bulk Key Search (searchIssuesByKeys)', testBulkKeySearch)) passed++; else failed++;
if (await runTest('10. Rate Limit Status', testRateLimitStatus)) passed++; else failed++;