Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/pluginImprovements' into pluginI…
Browse files Browse the repository at this point in the history
…mprovements
  • Loading branch information
SaachiNayyer committed Apr 29, 2024
2 parents ae45c04 + cbbcd21 commit fca6f1a
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 73 deletions.
6 changes: 5 additions & 1 deletion .changeset/sharp-sheep-smell.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,8 @@
'@axis-backstage/plugin-jira-dashboard-backend': major
---

Implemented a new GET endpoint in the router.ts file to provide compatibility with the existing POST endpoint in the api.ts file. This endpoint allows users to retrieve Jira issues using the provided JQL query and query parameters, enabling flexibility in choosing the HTTP method that best suits their needs. Additionally, this implementation allows users to utilize the response for other plugins interacting with Jira.
Introduced TypeScript type definitions SearchJiraResponse and JiraQueryResults to represent Jira search responses and pagination details.
Updated the searchJira function to return search results as a SearchJiraResponse, incorporating the new types.
Enhanced error handling in the searchJira function by handling HTTP response errors and logging them appropriately.
The JiraQueryResults type outlines the structure of a paginated Jira search response, facilitating better data handling.
These changes streamline the Jira Dashboard plugin's codebase, improving error resilience and clarity in handling search operations.
68 changes: 50 additions & 18 deletions plugins/jira-dashboard-backend/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@ import fetch from 'node-fetch';
import {
Filter,
Issue,
JiraQueryResults,
Project,
SearchJiraResponse,
} from '@axis-backstage/plugin-jira-dashboard-common';
import { resolveJiraBaseUrl, resolveJiraToken } from './config';
import { Logger } from 'winston';

export const getProjectInfo = async (
projectKey: string,
Expand Down Expand Up @@ -88,33 +91,62 @@ export type SearchOptions = {
/**
* Search for Jira issues using JQL.
*
* For more information about the available options see the API
* documentation at:
* https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-search/#api-rest-api-2-search-post
* This function sends a POST request to the Jira API's search endpoint
* to find issues based on the provided JQL query and query options.
*
* @param config - A Backstage config
* @param jqlQuery - A string containing the jql query.
* @param options - Query options that will be passed on to the POST request.
* For more information about the available options, refer to the API
* documentation at: {@link https://developer.atlassian.com/cloud/jira/platform/rest/v2/api-group-issue-search/#api-rest-api-2-search-post}
*
* @param config - A Backstage config containing Jira base URL and authentication token.
* @param jqlQuery - A string containing the JQL (Jira Query Language) query.
* @param options - Additional search options to customize the query. See {@link SearchOptions}.
* @param logger - An optional logger for error handling and debugging purposes.
* @returns A promise that resolves with the search results and status code.
* @throws If an error occurs during the search process.
* @public
*/
export const searchJira = async (
config: Config,
jqlQuery: string,
options: SearchOptions,
): Promise<Issue[]> => {
const response = await fetch(`${resolveJiraBaseUrl(config)}search`, {
method: 'POST',
body: JSON.stringify({ jql: jqlQuery, ...options }),
headers: {
Authorization: resolveJiraToken(config),
Accept: 'application/json',
'Content-Type': 'application/json',
},
}).then(resp => resp.json());
return response.issues;
config: Config,
logger: Logger,
): Promise<SearchJiraResponse> => {
try {
const response = await fetch(`${resolveJiraBaseUrl(config)}search`, {
method: 'POST',
body: JSON.stringify({ jql: jqlQuery, ...options }),
headers: {
Authorization: resolveJiraToken(config),
Accept: 'application/json',
'Content-Type': 'application/json',
},
});
const jsonResponse = await response.json();

return {
results: toPaginatedResponse(jsonResponse),
statusCode: response.status,
} as SearchJiraResponse;
} catch (error) {
logger.error(error);
throw error;
}
};

function toPaginatedResponse(response: any): JiraQueryResults {
return {
expand: response.expand,
names: response.names,
schema: response.schema,
issues: response.issues,
total: response.total,
startAt: response.startAt,
maxResults: response.maxResults,
warningMessages: response.warningMessages,
errorMessages: response.errorMessages,
};
}

export const getIssuesByComponent = async (
projectKey: string,
componentKey: string,
Expand Down
55 changes: 1 addition & 54 deletions plugins/jira-dashboard-backend/src/service/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
type Project,
} from '@axis-backstage/plugin-jira-dashboard-common';
import stream from 'stream';
import { SearchOptions, getProjectAvatar, searchJira } from '../api';
import { getProjectAvatar } from '../api';
import {
getProjectResponse,
getFiltersFromAnnotations,
Expand Down Expand Up @@ -239,59 +239,6 @@ export async function createRouter(
},
);

/**
* Retrieves Jira issues based on the provided JQL query and query parameters.
*
* @param {string} jqlQuery - The JQL query string.
* @param {string} fields - The fields to return in the search results.
* @param {string} startAt - The index of the first issue to return (0-based).
* @param {string} maxResults - The maximum number of issues to return per response.
* @param {Config} config - The configuration object containing Jira settings.
* @param {Logger} logger - The logger object to log any errors.
* @returns {Promise<any>} A Promise that resolves with the Jira response data.
*/

router.get('/jira/search', async (request, response) => {
const jqlQuery = request.query.jql as string;
const searchOptions: SearchOptions = {
fields: request.query.fields
? (request.query.fields as string).split(',')
: [],
startAt: parseInt(request.query.startAt as string, 10) || 0,
maxResults: parseInt(request.query.maxResults as string, 10) || 50,
};

try {
if (!jqlQuery || jqlQuery.trim() === '') {
return response.status(400).json({
results: {
errorMessages: [
'Bad Request: The jql query parameter is missing in the request. ' +
'Please include the jql parameter to perform the search.',
],
},
});
}

const issues = await searchJira(config, jqlQuery, searchOptions);
return response.status(200).json({
results: issues,
});
} catch (error) {
const errorMessage =
error instanceof Error ? error.message : 'An unexpected error occurred';
logger.error(
`Encountered error: ${errorMessage} for jql query: ${jqlQuery}`,
);

return response.status(500).json({
results: {
errorMessages: [`${errorMessage}`],
},
});
}
});

router.use(errorHandler());
return router;
}
25 changes: 25 additions & 0 deletions plugins/jira-dashboard-common/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,3 +70,28 @@ export type JiraResponse = {
project: Project;
data: JiraDataResponse[];
};

/**
* Type definition for a Jira search result & the recieved HTTP status code
* @public
*/
export type SearchJiraResponse = {
results: JiraQueryResults;
statusCode: number;
};

/**
* Type definition for a paginated Jira search response
* @public
*/
export type JiraQueryResults = {
expand: string;
names: object;
schema: object;
issues: Issue[];
total: number;
startAt: number;
maxResults: number;
warningMessages?: string[];
errorMessages?: string[];
};

0 comments on commit fca6f1a

Please sign in to comment.