Skip to content

Commit

Permalink
Merge pull request #55 from reportportal/rc/5.11.0
Browse files Browse the repository at this point in the history
Release 5.11.0
  • Loading branch information
pbortnik authored Feb 12, 2024
2 parents a10d6b3 + 7831163 commit c9ed738
Show file tree
Hide file tree
Showing 30 changed files with 3,981 additions and 4,257 deletions.
40 changes: 40 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
name: Build

on:
pull_request:
push:
branches:
- main
- develop
paths-ignore:
- '.github/**'
- README.md
- gradle.properties

jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v2

- name: Set up JDK 11
uses: actions/setup-java@v2
with:
distribution: 'adopt'
java-version: '11'

- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Setup git credentials
uses: oleksiyrudenko/gha-git-credentials@v2
with:
name: 'reportportal.io'
email: '[email protected]'
token: ${{ secrets.GITHUB_TOKEN }}

- name: Build with Gradle
id: build
run: |
./gradlew build
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ on:

env:
GH_USER_NAME: github.actor
SCRIPTS_VERSION: 5.7.0
BOM_VERSION: 5.7.0
SCRIPTS_VERSION: 5.10.0
BOM_VERSION: 5.11.2

jobs:
release:
Expand Down
22 changes: 20 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,37 @@
# Azure DevOps bug tracking system integration for ReportPortal

## UI

Preconditions:
- Install Node.js version ≥ 12.
- Install Node.js (version 14 is recommended).

Install the dependencies: `npm install`

Run in dev mode:
```bash
npm run dev # Run webpack in dev watch mode
npm run start # Serve built files
```

_Available only from RP v23.3_: use
```javascript
window.RP.overrideExtension(pluginName, url);
```
function call in browser to override the plugin UI assets in favor of your local development changes, f.e.
```javascript
window.RP.overrideExtension('Azure DevOps', 'http://localhost:9090');
```

Build the UI source code: `npm run build`

**How it works**: [UI plugin docs](https://github.com/reportportal/service-ui/blob/5.4.1/app/docs/14-plugins.md).
**How it works**: [UI plugin docs](https://github.com/reportportal/service-ui/blob/master/docs/14-plugins.md).

## Build the plugin

Preconditions:
- Install JDK version 11.
- Specify version number in gradle.properties file.

**Note:** Versions in the _develop_ branch are not release versions and must be postfixed with `NEXT_RELEASE_VERSION-SNAPSHOT-NUMBER_OF_BUILD (Example: 5.3.6-SNAPSHOT-1)`

Build the plugin: `gradlew build`
27 changes: 8 additions & 19 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
plugins {
id "io.spring.dependency-management" version "1.0.9.RELEASE"
id 'java'
id "com.moowork.node" version "1.3.1"
id "com.github.node-gradle.node" version "2.2.1"
id 'nu.studer.jooq' version '3.0.3'
}

Expand All @@ -15,29 +15,18 @@ def scriptsUrl = 'https://raw.githubusercontent.com/reportportal/gradle-scripts/

apply from: scriptsUrl + '/release-fat.gradle'
apply from: scriptsUrl + '/signing.gradle'
apply from: scriptsUrl + '/build-quality.gradle'

repositories {
mavenCentral()
mavenLocal()
if (releaseMode) {
dependencyRepos.forEach { path ->
maven {
setUrl("https://maven.pkg.github.com/reportportal/${path}")
credentials {
username = findProperty("githubUserName")
password = findProperty("githubToken")
}
}
}
} else {
mavenCentral { url "https://repo1.maven.org/maven2" }

if (!releaseMode) {
maven { url 'https://jitpack.io' }
}
}

dependencyManagement {
imports {
mavenBom(releaseMode ? 'com.epam.reportportal:commons-bom:' + getProperty('bom.version') : 'com.github.reportportal:commons-bom:4b7ed8a')
mavenBom(releaseMode ? 'com.epam.reportportal:commons-bom:' + getProperty('bom.version') : 'com.epam.reportportal:commons-bom:5.11.2')
}
}

Expand All @@ -46,8 +35,8 @@ dependencies {
implementation 'com.epam.reportportal:plugin-api'
annotationProcessor 'com.epam.reportportal:plugin-api'
} else {
implementation 'com.github.reportportal:plugin-api:886ac55'
annotationProcessor 'com.github.reportportal:plugin-api:886ac55'
implementation 'com.epam.reportportal:plugin-api'
annotationProcessor 'com.epam.reportportal:plugin-api'
}

compile 'com.squareup.okhttp:okhttp:2.7.5'
Expand Down Expand Up @@ -148,4 +137,4 @@ task assemblePlugins(type: Copy) {
}

compileJava.dependsOn npm_run_build
build.dependsOn jacocoTestReport
build.dependsOn jacocoTestReport
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
version=5.7.2
version=5.11.0
description=EPAM Report Portal. Azure DevOps plugin
pluginId = Azure DevOps
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.epam.reportportal.extension.IntegrationGroupEnum;
import com.epam.reportportal.extension.PluginCommand;
import com.epam.reportportal.extension.ReportPortalExtensionPoint;
import com.epam.reportportal.extension.azure.command.binary.GetFileCommand;
import com.epam.reportportal.extension.azure.command.connection.TestConnectionCommand;
import com.epam.reportportal.extension.azure.entity.model.IntegrationParameters;
import com.epam.reportportal.extension.azure.event.launch.AzureStartLaunchEventListener;
Expand Down Expand Up @@ -42,6 +41,7 @@
import com.google.common.base.Suppliers;
import com.google.common.io.ByteStreams;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.tika.config.TikaConfig;
import org.apache.tika.mime.MimeType;
Expand Down Expand Up @@ -85,6 +85,8 @@ public class AzureExtension implements ReportPortalExtensionPoint, DisposableBea

public static final Logger LOGGER = LoggerFactory.getLogger(AzureExtension.class);

private static final String DOCUMENTATION_LINK_FIELD = "documentationLink";
private static final String DOCUMENTATION_LINK = "https://reportportal.io/docs/plugins/AzureDevOpsBTS";
public static final String BINARY_DATA_PROPERTIES_FILE_ID = "azure-binary-data.properties";

public static final String SCHEMA_SCRIPTS_DIR = "schema";
Expand Down Expand Up @@ -215,6 +217,7 @@ public WorkItemsApi getWorkItemsApi() {
@Override
public Map<String, ?> getPluginParams() {
Map<String, Object> params = new HashMap<>();
params.put(DOCUMENTATION_LINK_FIELD, DOCUMENTATION_LINK);
params.put(ALLOWED_COMMANDS, new ArrayList<>(pluginCommandMapping.get().keySet()));
return params;
}
Expand Down Expand Up @@ -271,7 +274,6 @@ private void removeListeners() {

private Map<String, PluginCommand<?>> getCommands() {
Map<String, PluginCommand<?>> pluginCommandMapping = new HashMap<>();
pluginCommandMapping.put("getFile", new GetFileCommand(resourcesDir, BINARY_DATA_PROPERTIES_FILE_ID));
pluginCommandMapping.put("testConnection", new TestConnectionCommand(basicTextEncryptor));
return pluginCommandMapping;
}
Expand Down Expand Up @@ -306,11 +308,10 @@ public Optional<Ticket> getTicket(String id, Integration integration) {
@Override
public Ticket submitTicket(PostTicketRQ ticketRQ, Integration integration) {
initFields(integration);
List<AttachmentInfo> attachmentsURL = new ArrayList<>();

List<JsonPatchOperation> patchOperationList = new ArrayList<>();

ticketRQ.getBackLinks().keySet().forEach(backLinkId -> uploadAttachmentToAzure(ticketRQ, attachmentsURL, backLinkId));
List<AttachmentInfo> attachmentsURL = uploadAttachmentToAzure(ticketRQ);

String issueType = null;
List<PostFormField> fields = ticketRQ.getFields();
Expand Down Expand Up @@ -663,21 +664,20 @@ private void updateDescriptionBuilder(StringBuilder descriptionBuilder, PostTick

private void addLogsInfoToDescription(StringBuilder descriptionBuilder, Long backLinkId, PostTicketRQ ticketRQ,
List<AttachmentInfo> attachmentsURL) {
itemRepository.findById(backLinkId).ifPresent(item -> ofNullable(item.getLaunchId()).ifPresent(launchId -> {
List<Log> logs = logRepository.findAllUnderTestItemByLaunchIdAndTestItemIdsWithLimit(launchId,
Collections.singletonList(item.getItemId()),
ticketRQ.getNumberOfLogs()
);
if (CollectionUtils.isNotEmpty(logs) && (ticketRQ.getIsIncludeLogs() || ticketRQ.getIsIncludeScreenshots())) {
descriptionBuilder.append(LOGS_HEADER);
logs.forEach(log -> updateWithLog(descriptionBuilder,
log,
ticketRQ.getIsIncludeLogs(),
ticketRQ.getIsIncludeScreenshots(),
attachmentsURL
));
}
}));
if (ticketRQ.getIsIncludeLogs() || ticketRQ.getIsIncludeScreenshots()) {
itemRepository.findById(backLinkId)
.map(item -> findLogsUnderItem(item, ticketRQ.getNumberOfLogs()))
.filter(CollectionUtils::isNotEmpty)
.ifPresent(logs -> {
descriptionBuilder.append(LOGS_HEADER);
logs.forEach(log -> updateWithLog(descriptionBuilder,
log,
ticketRQ.getIsIncludeLogs(),
ticketRQ.getIsIncludeScreenshots(),
attachmentsURL
));
});
}
}

private void updateWithLog(StringBuilder descriptionBuilder, Log log, boolean includeLog, boolean includeScreenshot,
Expand Down Expand Up @@ -723,45 +723,26 @@ private void addAttachmentToDescription(StringBuilder descriptionBuilder, Attach
}
}

private void uploadAttachmentToAzure(PostTicketRQ ticketRQ, List<AttachmentInfo> attachmentsURL, Long backLinkId) {
List<Attachment> attachments = new ArrayList<>();
itemRepository.findById(backLinkId).ifPresent(item -> ofNullable(item.getLaunchId()).ifPresent(launchId -> {
List<Log> logs = logRepository.findAllUnderTestItemByLaunchIdAndTestItemIdsWithLimit(launchId,
Collections.singletonList(item.getItemId()),
ticketRQ.getNumberOfLogs()
);
logs.forEach(log -> ofNullable(log.getAttachment()).ifPresent(attachment -> attachments.add(attachment)));
}));

for (Attachment attachment : attachments) {
Optional<InputStream> fileOptional = attachmentDataStoreService.load(attachment.getFileId());
if (fileOptional.isPresent()) {
try (InputStream file = fileOptional.get()) {
MimeType mimeType = mimeRepository.forName(attachment.getContentType());
byte[] bytes = ByteStreams.toByteArray(file);
AttachmentsApi attachmentsApi = new AttachmentsApi(defaultClient);
String fileName = attachment.getFileId() + mimeType.getExtension();
AttachmentReference attachmentReference = attachmentsApi.attachmentsCreate(organizationName,
bytes,
params.getProjectName(),
API_VERSION,
fileName,
null,
null
);
attachmentsURL.add(new AttachmentInfo(fileName,
attachment.getFileId(),
attachmentReference.getUrl(),
attachment.getContentType()
));
} catch (IOException | ApiException | MimeTypeException e) {
LOGGER.error("Unable to post ticket : " + e.getMessage(), e);
throw new ReportPortalException(UNABLE_INTERACT_WITH_INTEGRATION, "Unable to post ticket: " + e.getMessage(), e);
}
} else {
throw new ReportPortalException(UNABLE_TO_LOAD_BINARY_DATA);
}
private List<AttachmentInfo> uploadAttachmentToAzure(PostTicketRQ ticketRQ) {
if (!ticketRQ.getIsIncludeScreenshots()) {
return Collections.emptyList();
}

if (MapUtils.isEmpty(ticketRQ.getBackLinks())) {
return Collections.emptyList();
}

return ticketRQ.getBackLinks()
.keySet()
.stream()
.map(itemRepository::findById)
.map(item -> item.map(it -> findLogsUnderItem(it, ticketRQ.getNumberOfLogs())).orElseGet(Collections::emptyList))
.flatMap(List::stream)
.map(Log::getAttachment)
.filter(Objects::nonNull)
.map(this::uploadAttachment)
.collect(Collectors.toList());

}

private String getFormattedMessage(Log log) {
Expand All @@ -773,4 +754,33 @@ private String getFormattedMessage(Log log) {
messageBuilder.append("<br>").append("Log: ").append(log.getLogMessage());
return messageBuilder.toString();
}

private List<Log> findLogsUnderItem(TestItem item, int logCount) {
return ofNullable(item.getLaunchId()).map(launchId -> logRepository.findAllUnderTestItemByLaunchIdAndTestItemIdsWithLimit(launchId,
Collections.singletonList(item.getItemId()),
logCount
)).orElseGet(Collections::emptyList);
}

private AttachmentInfo uploadAttachment(Attachment attachment) {
try (InputStream file = attachmentDataStoreService.load(attachment.getFileId())
.orElseThrow(() -> new ReportPortalException(UNABLE_TO_LOAD_BINARY_DATA))) {
MimeType mimeType = mimeRepository.forName(attachment.getContentType());
byte[] bytes = ByteStreams.toByteArray(file);
AttachmentsApi attachmentsApi = new AttachmentsApi(defaultClient);
String fileName = attachment.getFileId() + mimeType.getExtension();
AttachmentReference attachmentReference = attachmentsApi.attachmentsCreate(organizationName,
bytes,
params.getProjectName(),
API_VERSION,
fileName,
null,
null
);
return new AttachmentInfo(fileName, attachment.getFileId(), attachmentReference.getUrl(), attachment.getContentType());
} catch (IOException | ApiException | MimeTypeException e) {
LOGGER.error("Unable to post ticket : " + e.getMessage(), e);
throw new ReportPortalException(UNABLE_INTERACT_WITH_INTEGRATION, "Unable to post ticket: " + e.getMessage(), e);
}
}
}
Loading

0 comments on commit c9ed738

Please sign in to comment.