Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Release 5.11.0 #55

Merged
merged 26 commits into from
Feb 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d9a475d
EPMRPP-76278 || Include screenshots flag logic fix
Apr 13, 2022
7c4823c
EPMRPP-76278 || Code style refactoring
Apr 13, 2022
96eb952
Merge pull request #37 from reportportal/EPMRPP-76278-include-screens…
miracle8484 Apr 14, 2022
e8591cc
5.7.0 || Update release scripts
pbortnik Apr 15, 2022
ea54fd6
Merge branch 'develop' of https://github.com/reportportal/plugin-bts-…
pbortnik Jul 15, 2022
1775218
EPMRPP-78159 || Update plugin description Azure DevOps (#41)
RuslanLyubimov Jul 21, 2022
a0968ba
EPMRPP-78099 || Redesign Integrations. Update modal windows
RuslanLyubimov Aug 23, 2022
4fadc2d
Merge pull request #42 from reportportal/EPMRPP-78099-Update-modal-wi…
chivekrodis Aug 23, 2022
fde10e6
EPMRPP-79259 || Asterisks to mandatory fields (#43)
tr1ble Sep 14, 2022
382e1d9
EPMRPP-81278 || Bump Node.js version for UI build (#44)
AmsterGet Jan 17, 2023
a22c8a2
EPMRPP-78914 || Redesign integrations. Link updates
APiankouski Aug 29, 2023
101ee68
Merge pull request #47 from reportportal/EPMRPP-78914_documentation_link
APiankouski Aug 30, 2023
2ec5a49
EPMRPP-86105 || Update node
APiankouski Aug 31, 2023
4f132cf
EPMRPP-86105 || Update node
APiankouski Aug 31, 2023
cd4c20b
EPMRPP-82260 || Use new modal for Edit integration form (#49)
AmsterGet Sep 5, 2023
9413f38
Remove unused stuff
AmsterGet Sep 6, 2023
abe6c04
EPMRPP-78914 || Redesign integrations. Link updates
APiankouski Aug 29, 2023
c7e3b84
EPMRPP-86105 || Update node
APiankouski Aug 31, 2023
c95d2de
EPMRPP-86232 || Adjust to new plugin mechanism
IvanKustau Oct 10, 2023
a92c10f
Merge pull request #50 from reportportal/EPMRPP-86232-adjust-to-new-p…
IvanKustau Oct 10, 2023
438d282
EPMRPP-86229 || Adjust plugin to the new mechanism (#51)
AmsterGet Oct 30, 2023
ed05e6f
Merge branch 'develop' into feature/settings
APiankouski Oct 30, 2023
b03e5b5
Merge pull request #52 from reportportal/feature/settings
AmsterGet Oct 30, 2023
02d7af2
EPMRPP-86232 || Add React import
AmsterGet Oct 30, 2023
1ef864c
EPMRPP-86302 || Create Global integration modal. New design support (…
AmsterGet Oct 31, 2023
7831163
rc/5.11.0 || Update release version
Dec 20, 2023
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
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
Loading