Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 5 additions & 15 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
id 'jacoco'
id "com.github.node-gradle.node" version "2.2.1"
id "org.openapi.generator" version "7.12.0"
id "org.springframework.boot" version "3.4.2" apply false
id "org.springframework.boot" version "${springBootVersion}" apply false
}

import org.openapitools.generator.gradle.plugin.tasks.GenerateTask
Expand All @@ -27,28 +27,18 @@ repositories {
}
}

dependencyManagement {
imports {
mavenBom(releaseMode ? 'com.epam.reportportal:commons-bom:' + getProperty('bom.version') : 'com.epam.reportportal:commons-bom:5.14.2')
}
}
ext['spring-boot.version'] = "${springBootVersion}"

dependencies {
// ALL new dependencies should be included in shadowJar plugin!
// transitive dependencies may be required as well. could be checked in runtime only!
if (releaseMode) {
implementation 'com.epam.reportportal:commons-dao'
implementation 'com.epam.reportportal:plugin-api'
annotationProcessor 'com.epam.reportportal:plugin-api'
implementation 'com.epam.reportportal:service-api'
annotationProcessor 'com.epam.reportportal:service-api'
} else {
implementation 'com.github.reportportal:commons-dao:develop-SNAPSHOT'
implementation 'com.github.reportportal:plugin-api:develop-SNAPSHOT'
annotationProcessor 'com.github.reportportal:plugin-api:develop-SNAPSHOT'
implementation 'com.github.reportportal:service-api:3e34a90'
annotationProcessor 'com.github.reportportal:service-api:3e34a90'
Comment on lines +36 to +40
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify service-api dependency migration completeness and version resolution.

The migration from commons-dao and plugin-api to service-api requires verification:

  1. Release mode (line 36-37): No version is specified for service-api. Per the summary, the dependencyManagement BOM was removed. Confirm that version resolution for service-api is still correct without an explicit version or BOM.
  2. Transitive dependencies: Verify that service-api transitively provides all functionality previously supplied by commons-dao and plugin-api.
  3. Legacy dependency removal: The summary mentions removal of explicit httpclient5 and hibernate-validator dependencies. Confirm these are still available transitively from service-api or that their removal is safe.

🏁 Script executed:

#!/bin/bash
# Description: Verify dependency tree for service-api and check if httpclient5 and hibernate-validator are transitively available.

# Check if service-api dependency includes httpclient5 and hibernate-validator transitively
./gradlew dependencies --configuration=compileClasspath | grep -A 5 -B 5 'service-api\|httpclient5\|hibernate-validator' || true

Length of output: 5400


I need to verify the dependency migration in alternative ways since Gradle commands won't work in this sandbox. Let me inspect the build.gradle file directly and search for dependency documentation.





🏁 Script executed:

#!/bin/bash
# Inspect build.gradle to understand full context around lines 36-40 and identify the module structure
cat build.gradle | head -50

Length of output: 1849


🏁 Script executed:

#!/bin/bash
# Search for any pom.xml or dependency metadata files
fd -e pom.xml -e '.gradle' -e 'gradle.properties' -e 'settings.gradle'

Length of output: 64


🏁 Script executed:

#!/bin/bash
# Search for references to commons-dao, plugin-api, and service-api throughout the codebase
rg -i 'commons-dao|plugin-api|service-api' --type gradle -n

Length of output: 409


🏁 Script executed:

#!/bin/bash
# Check if there's any dependencyManagement block or BOM reference in build files
rg -n 'dependencyManagement|BOM|import' build.gradle

Length of output: 69


🏁 Script executed:

#!/bin/bash
# Look for httpclient5 and hibernate-validator references to understand their previous usage
rg -n 'httpclient5|hibernate-validator' --type gradle

Length of output: 0


🏁 Script executed:

#!/bin/bash
# Search for any documentation or comments about the migration
rg -n 'migrate|commons-dao|plugin-api' build.gradle -i -A 2 -B 2

Length of output: 0


🌐 Web query:

com.epam.reportportal service-api transitive dependencies httpclient5 hibernate-validator

💡 Result:

Short answer: Yes — com.epam.reportportal:service-api exposes org.apache.httpcomponents.client5:httpclient5 and org.hibernate.validator:hibernate-validator as transitive (runtime) dependencies. See the service‑api Maven page listing httpclient5 and hibernate‑validator in its runtime dependencies. [1]

Sources

  • service-api (com.epam.reportportal) — dependency list showing httpclient5 and hibernate‑validator. [1]

Let me check the other gradle files and look for any BOM definitions or legacy dependency references:





🏁 Script executed:

#!/bin/bash
# Inspect project-properties.gradle to see the full dependency configuration
cat project-properties.gradle

Length of output: 349


🏁 Script executed:

#!/bin/bash
# Inspect ui.gradle to check for any dependency management
cat ui.gradle

Length of output: 329


🏁 Script executed:

#!/bin/bash
# Search for any BOM imports or spring-boot managed dependencies
rg -n 'bom|BOM|dependencyManagement|spring.boot' build.gradle -C 3

Length of output: 165


🏁 Script executed:

#!/bin/bash
# Check for commons-dao or plugin-api in Java source files to see if they're still used
rg -r 'commons-dao|plugin-api' --type java -l

Length of output: 396


🏁 Script executed:

#!/bin/bash
# Look at settings.gradle for any additional configuration
cat settings.gradle

Length of output: 43


🏁 Script executed:

#!/bin/bash
# Check if there's a gradle.properties or gradle wrapper properties that might affect dependency resolution
fd -name 'gradle.properties' -o -name 'gradle-wrapper.properties' -exec cat {} \;

Length of output: 230


🌐 Web query:

service-api com.epam.reportportal maven central version latest 2025

💡 Result:

Latest version on Maven Central is 5.14.0 (published Jun 03, 2025).
Dependency: com.epam.reportportal:service-api:5.14.0. [1][2]

Sources:
[1] Maven Central index (repo.maven.apache.org) — com/epam/reportportal/service-api (shows 5.14.0, 2025-06-03).
[2] mvnrepository.com — com.epam.reportportal:service-api:5.14.0.



---

🏁 Script executed:

```shell
#!/bin/bash
# Correct fd syntax to search for gradle wrapper properties
find . -name 'gradle-wrapper.properties' -exec cat {} \;

Length of output: 252


🏁 Script executed:

#!/bin/bash
# Search gradle files for any version property or BOM declaration related to service-api
rg -n 'serviceApi|service.api|service-api' build.gradle project-properties.gradle -i

Length of output: 329


Add explicit version or BOM management for release mode service-api dependency to ensure reproducible builds.

The release mode dependency implementation 'com.epam.reportportal:service-api' (line 36) lacks any version specification. This means Maven will resolve to the latest available version, which currently is 5.14.0. Different builds at different times will pull different versions, breaking reproducibility.

Transitive dependency verification:
Confirmed—com.epam.reportportal:service-api transitively provides httpclient5 and hibernate-validator as runtime dependencies, so their removal from explicit declarations is safe.

Legacy migration verification:
Commons-dao and plugin-api are no longer referenced in source code, confirming the migration to service-api is complete.

Action required: Pin service-api to an explicit version (e.g., 5.14.0) or declare a BOM in dependencyManagement to match the non-release mode's explicit versioning approach. Alternatively, ensure Spring Boot's dependency-management plugin imports a ReportPortal BOM that pins this version.

🤖 Prompt for AI Agents
In build.gradle around lines 36 to 40, the release-mode dependency declaration
for com.epam.reportportal:service-api has no version which breaks reproducible
builds; update the file to either pin the service-api to an explicit version
(e.g., set both implementation and annotationProcessor entries to
com.epam.reportportal:service-api:5.14.0) or add/import a ReportPortal BOM in
dependencyManagement (or via Spring Boot dependency-management plugin) that pins
the service-api version used in release mode, ensuring the annotationProcessor
entry uses the same managed/pinned version.

}

implementation("org.apache.httpcomponents.client5:httpclient5:5.4.2") // supports gzip encoding
implementation "org.hibernate.validator:hibernate-validator:8.0.2.Final"
implementation 'jakarta.el:jakarta.el-api:6.0.1'
implementation 'org.glassfish:jakarta.el:4.0.2'
implementation "org.springdoc:springdoc-openapi-common:1.8.0"
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ version=5.13.5
description=EPAM Report Portal. Cloud Jira plugin
pluginId = JIRA Cloud

springBootVersion=3.4.2
lombokVersion=1.18.36
springBootVersion=3.4.11
lombokVersion=1.18.42
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@
import com.epam.reportportal.extension.jira.info.impl.PluginInfoProviderImpl;
import com.epam.reportportal.extension.jira.utils.MemoizingSupplier;
import com.epam.reportportal.extension.util.RequestEntityConverter;
import com.epam.ta.reportportal.binary.DataStoreService;
import com.epam.ta.reportportal.dao.IntegrationRepository;
import com.epam.ta.reportportal.dao.IntegrationTypeRepository;
import com.epam.ta.reportportal.dao.LaunchRepository;
import com.epam.ta.reportportal.dao.LogRepository;
import com.epam.ta.reportportal.dao.ProjectRepository;
import com.epam.ta.reportportal.dao.TestItemRepository;
import com.epam.ta.reportportal.dao.TicketRepository;
import com.epam.ta.reportportal.dao.organization.OrganizationRepositoryCustom;
import com.epam.reportportal.infrastructure.persistence.binary.DataStoreService;
import com.epam.reportportal.infrastructure.persistence.dao.IntegrationRepository;
import com.epam.reportportal.infrastructure.persistence.dao.IntegrationTypeRepository;
import com.epam.reportportal.infrastructure.persistence.dao.LaunchRepository;
import com.epam.reportportal.infrastructure.persistence.dao.LogRepository;
import com.epam.reportportal.infrastructure.persistence.dao.ProjectRepository;
import com.epam.reportportal.infrastructure.persistence.dao.TestItemRepository;
import com.epam.reportportal.infrastructure.persistence.dao.TicketRepository;
import com.epam.reportportal.infrastructure.persistence.dao.organization.OrganizationRepositoryCustom;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
Expand Down Expand Up @@ -233,10 +233,13 @@ private Map<String, CommonPluginCommand<?>> getCommonCommands() {

private Map<String, PluginCommand<?>> getCommands() {
List<PluginCommand<?>> commands = new ArrayList<>();
commands.add(new UserSearchCommand(projectRepository, cloudJiraClientProviderSupplier.get(), organizationRepository));
commands.add(
new UserSearchCommand(projectRepository, cloudJiraClientProviderSupplier.get(), organizationRepository));
commands.add(new TestConnectionCommand(cloudJiraClientProviderSupplier.get()));
commands.add(new GetIssueFieldsCommand(projectRepository, organizationRepository, cloudJiraClientProviderSupplier.get()));
commands.add(new GetIssueTypesCommand(projectRepository, cloudJiraClientProviderSupplier.get(), organizationRepository));
commands.add(
new GetIssueFieldsCommand(projectRepository, organizationRepository, cloudJiraClientProviderSupplier.get()));
commands.add(
new GetIssueTypesCommand(projectRepository, cloudJiraClientProviderSupplier.get(), organizationRepository));
commands.add(new PostTicketCommand(projectRepository,
requestEntityConverter,
cloudJiraClientProviderSupplier.get(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@
* @author <a href="mailto:ivan_budayeu@epam.com">Ivan Budayeu</a>
*/
public class CloudJiraPlugin extends Plugin {
/**
* Constructor to be used by plugin manager for plugin instantiation.
* Your plugins have to provide constructor with this exact signature to
* be successfully loaded by manager.
*
* @param wrapper
*/
public CloudJiraPlugin(PluginWrapper wrapper) {
super(wrapper);
}

/**
* Constructor to be used by plugin manager for plugin instantiation. Your plugins have to provide constructor with
* this exact signature to be successfully loaded by manager.
*
* @param wrapper
*/
public CloudJiraPlugin(PluginWrapper wrapper) {
super(wrapper);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,13 @@
import com.epam.reportportal.extension.jira.command.utils.CloudJiraClientProvider;
import com.epam.reportportal.extension.jira.command.utils.CloudJiraProperties;
import com.epam.reportportal.extension.jira.command.utils.JIRATicketUtils;
import com.epam.reportportal.model.externalsystem.Ticket;
import com.epam.reportportal.rules.exception.ErrorType;
import com.epam.reportportal.rules.exception.ReportPortalException;
import com.epam.ta.reportportal.dao.IntegrationRepository;
import com.epam.ta.reportportal.dao.TicketRepository;
import com.epam.ta.reportportal.entity.integration.Integration;
import com.epam.ta.reportportal.entity.integration.IntegrationParams;
import com.epam.reportportal.infrastructure.model.externalsystem.Ticket;
import com.epam.reportportal.infrastructure.persistence.dao.IntegrationRepository;
import com.epam.reportportal.infrastructure.persistence.dao.TicketRepository;
import com.epam.reportportal.infrastructure.persistence.entity.integration.Integration;
import com.epam.reportportal.infrastructure.persistence.entity.integration.IntegrationParams;
import com.epam.reportportal.infrastructure.rules.exception.ErrorType;
import com.epam.reportportal.infrastructure.rules.exception.ReportPortalException;
import java.util.Map;
import java.util.Optional;
import org.apache.commons.collections4.CollectionUtils;
Expand Down Expand Up @@ -57,36 +57,42 @@ public Ticket executeCommand(Map<String, Object> params) {
var ticketId = Optional.ofNullable(params.get(TICKET_ID))
.map(String::valueOf)
.orElseThrow(() -> new ReportPortalException(ErrorType.BAD_REQUEST_ERROR, TICKET_ID + " must be provided"));
final com.epam.ta.reportportal.entity.bts.Ticket ticket = ticketRepository.findByTicketId(ticketId)
.orElseThrow(() -> new ReportPortalException(ErrorType.BAD_REQUEST_ERROR, "Ticket not found with id " + TICKET_ID));
var ticket = ticketRepository.findByTicketId(ticketId)
.orElseThrow(
() -> new ReportPortalException(ErrorType.BAD_REQUEST_ERROR, "Ticket not found with id " + TICKET_ID));
final Long projectId = (Long) Optional.ofNullable(params.get(PROJECT_ID))
.orElseThrow(() -> new ReportPortalException(ErrorType.BAD_REQUEST_ERROR, PROJECT_ID + " must be provided"));

final String btsUrl = CloudJiraProperties.URL.getParam(params)
.orElseThrow(() -> new ReportPortalException(ErrorType.UNABLE_INTERACT_WITH_INTEGRATION, "Url is not specified."));
.orElseThrow(
() -> new ReportPortalException(ErrorType.UNABLE_INTERACT_WITH_INTEGRATION, "Url is not specified."));
final String btsProject = CloudJiraProperties.PROJECT.getParam(params)
.orElseThrow(() -> new ReportPortalException(ErrorType.UNABLE_INTERACT_WITH_INTEGRATION, "Project is not specified."));
.orElseThrow(
() -> new ReportPortalException(ErrorType.UNABLE_INTERACT_WITH_INTEGRATION, "Project is not specified."));

final Integration integration =
integrationRepository.findProjectBtsByUrlAndLinkedProject(btsUrl, btsProject, projectId)
.orElseGet(() -> integrationRepository.findGlobalBtsByUrlAndLinkedProject(btsUrl, btsProject)
.orElseThrow(() -> new ReportPortalException(ErrorType.BAD_REQUEST_ERROR, "Integration with provided url and project isn't found")));
.orElseThrow(() -> new ReportPortalException(ErrorType.BAD_REQUEST_ERROR,
"Integration with provided url and project isn't found")));
return getTicket(ticketId, integration.getParams());
}

private Ticket getTicket(String ticketId, IntegrationParams details) {
var client = cloudJiraClientProvider.getApiClient(details);
SearchResults issues;
try {
var jql = String.format("project=%s and key=%s", CloudJiraProperties.PROJECT.getParam(details.getParams()).get(), ticketId);
var jql = String.format("project=%s and key=%s", CloudJiraProperties.PROJECT.getParam(details.getParams()).get(),
ticketId);
issues = client.issueSearchApi().searchForIssuesUsingJql(jql, null, 50, "", null, null, null, null, null);

} catch (Exception e) {
throw new ReportPortalException(ErrorType.BAD_REQUEST_ERROR);
}
if (CollectionUtils.isNotEmpty(issues.getIssues())) {
return JIRATicketUtils.toTicket(issues.getIssues().getFirst(), CloudJiraProperties.URL.getParam(details)
.orElseThrow(() -> new ReportPortalException(ErrorType.UNABLE_INTERACT_WITH_INTEGRATION, "Url is not specified.")));
.orElseThrow(
() -> new ReportPortalException(ErrorType.UNABLE_INTERACT_WITH_INTEGRATION, "Url is not specified.")));
} else {
throw new ReportPortalException(ErrorType.BAD_REQUEST_ERROR, "Ticket with id {} is not found", ticketId);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import static com.epam.reportportal.extension.jira.command.utils.IssueField.FIX_VERSIONS_FIELD;
import static com.epam.reportportal.extension.jira.command.utils.IssueField.ISSUE_TYPE_FIELD;
import static com.epam.reportportal.extension.jira.command.utils.IssueField.PRIORITY_FIELD;
import static com.epam.reportportal.rules.exception.ErrorType.UNABLE_INTERACT_WITH_INTEGRATION;
import static com.epam.reportportal.infrastructure.rules.exception.ErrorType.UNABLE_INTERACT_WITH_INTEGRATION;

import com.epam.reportportal.extension.ProjectManagerCommand;
import com.epam.reportportal.extension.jira.api.model.IssueCreateMetadata;
Expand All @@ -34,13 +34,13 @@
import com.epam.reportportal.extension.jira.command.utils.CloudJiraClientProvider;
import com.epam.reportportal.extension.jira.command.utils.CloudJiraProperties;
import com.epam.reportportal.extension.jira.command.utils.JIRATicketUtils;
import com.epam.reportportal.model.externalsystem.AllowedValue;
import com.epam.reportportal.model.externalsystem.PostFormField;
import com.epam.reportportal.rules.exception.ErrorType;
import com.epam.reportportal.rules.exception.ReportPortalException;
import com.epam.ta.reportportal.dao.ProjectRepository;
import com.epam.ta.reportportal.dao.organization.OrganizationRepositoryCustom;
import com.epam.ta.reportportal.entity.integration.Integration;
import com.epam.reportportal.infrastructure.model.externalsystem.AllowedValue;
import com.epam.reportportal.infrastructure.model.externalsystem.PostFormField;
import com.epam.reportportal.infrastructure.persistence.dao.ProjectRepository;
import com.epam.reportportal.infrastructure.persistence.dao.organization.OrganizationRepositoryCustom;
import com.epam.reportportal.infrastructure.persistence.entity.integration.Integration;
import com.epam.reportportal.infrastructure.rules.exception.ErrorType;
import com.epam.reportportal.infrastructure.rules.exception.ReportPortalException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.ArrayList;
Expand Down Expand Up @@ -92,7 +92,8 @@ protected List<PostFormField> invokeCommand(Integration integration, Map<String,
IssueTypeDetails issueType = jiraProject.getIssueTypes().stream()
.filter(input -> issueTypeParam.equalsIgnoreCase(input.getName()))
.findFirst()
.orElseThrow(() -> new ReportPortalException(UNABLE_INTERACT_WITH_INTEGRATION, "Issue type '" + issueTypeParam + "' not found"));
.orElseThrow(() -> new ReportPortalException(UNABLE_INTERACT_WITH_INTEGRATION,
"Issue type '" + issueTypeParam + "' not found"));

IssueCreateMetadata issueCreateMetadata = client.issuesApi().getCreateIssueMeta(
Collections.singletonList(jiraProject.getId()),
Expand Down Expand Up @@ -125,10 +126,10 @@ protected List<PostFormField> invokeCommand(Integration integration, Map<String,
// Provide values for custom fields with predefined options
if (issueField.getValue().getAllowedValues() != null) {
allowed = issueField.getValue().getAllowedValues().stream()
.map(value -> (JsonNode) new ObjectMapper().valueToTree(value))
.filter(JIRATicketUtils::isCustomField)
.map(jn -> new AllowedValue(jn.get("id").asText(), jn.get("value").asText()))
.collect(Collectors.toList());
.map(value -> (JsonNode) new ObjectMapper().valueToTree(value))
.filter(JIRATicketUtils::isCustomField)
.map(jn -> new AllowedValue(jn.get("id").asText(), jn.get("value").asText()))
.collect(Collectors.toList());
}

if (fieldID.equalsIgnoreCase(COMPONENTS_FIELD.getValue())) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@
import com.epam.reportportal.extension.jira.client.JiraRestClient;
import com.epam.reportportal.extension.jira.command.utils.CloudJiraClientProvider;
import com.epam.reportportal.extension.jira.command.utils.CloudJiraProperties;
import com.epam.reportportal.rules.exception.ErrorType;
import com.epam.reportportal.rules.exception.ReportPortalException;
import com.epam.ta.reportportal.dao.ProjectRepository;
import com.epam.ta.reportportal.dao.organization.OrganizationRepositoryCustom;
import com.epam.ta.reportportal.entity.integration.Integration;
import com.epam.reportportal.infrastructure.persistence.dao.ProjectRepository;
import com.epam.reportportal.infrastructure.persistence.dao.organization.OrganizationRepositoryCustom;
import com.epam.reportportal.infrastructure.persistence.entity.integration.Integration;
import com.epam.reportportal.infrastructure.rules.exception.ErrorType;
import com.epam.reportportal.infrastructure.rules.exception.ReportPortalException;
import java.util.List;
import java.util.Map;
import org.springframework.web.client.RestClientException;
Expand Down
Loading