Skip to content

Commit

Permalink
Merge pull request #52 from reportportal/feature/settings
Browse files Browse the repository at this point in the history
EPMRPP-86991 || Merge feature/settings-elastic to develop
  • Loading branch information
AmsterGet authored Oct 30, 2023
2 parents 4f132cf + ed05e6f commit b03e5b5
Show file tree
Hide file tree
Showing 28 changed files with 3,968 additions and 4,238 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
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`
5 changes: 2 additions & 3 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,7 +15,6 @@ 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()
Expand Down Expand Up @@ -148,4 +147,4 @@ task assemblePlugins(type: Copy) {
}

compileJava.dependsOn npm_run_build
build.dependsOn jacocoTestReport
build.dependsOn jacocoTestReport
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 @@ -274,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 @@ -309,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 @@ -666,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 @@ -726,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 @@ -776,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);
}
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,7 @@ public class PluginInfoProviderImpl implements PluginInfoProvider {
private static final String METADATA_KEY = "metadata";

private static final String PLUGIN_DESCRIPTION =
"Integration with Azure DevOps, can be required for projects that collect defects in a separate tracking tool. "
+ "Integration provides an exchange of information between ReportPortal and Azure DevOps, such as posting "
"The integration provides an exchange of information between ReportPortal and the Azure DevOps, such as posting "
+ "issues and linking issues, getting updates on their statuses.";
public static final Map<String, Object> PLUGIN_METADATA = new HashMap<>();
static {
Expand Down
5 changes: 3 additions & 2 deletions src/main/resources/azure-binary-data.properties
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
main=main.js
icon=plugin-icon.svg
main=remoteEntity.js
metadata=metadata.json
icon=plugin-icon.svg
Loading

0 comments on commit b03e5b5

Please sign in to comment.