Merge branch 'dev' into release_candidate
Change-Id: I289c687d2e3c270690bd1cc9b39b76c6638b907b
diff --git a/org.eclipse.mdm.apicopy/src/main/java/org/eclipse/mdm/apicopy/control/ExportTask.java b/org.eclipse.mdm.apicopy/src/main/java/org/eclipse/mdm/apicopy/control/ExportTask.java
index 7a1d0a6..c35ebd7 100644
--- a/org.eclipse.mdm.apicopy/src/main/java/org/eclipse/mdm/apicopy/control/ExportTask.java
+++ b/org.eclipse.mdm.apicopy/src/main/java/org/eclipse/mdm/apicopy/control/ExportTask.java
@@ -200,7 +200,7 @@
persist(transaction, testStepDst);
TemplateTestStep tpl = entityManagerSrc.loadTemplate(testStepSrc).orElseThrow(() -> new ApiCopyException(
- "Could not retrieve TemplateTestStep for TestStep + " + testStepSrc.getID()));
+ "Could not retrieve TemplateTestStep for TestStep " + testStepSrc.getID()));
copyContext(testStepSrc, testStepDst, tpl, transaction);
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.html b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.html
index f3dbe9b..fc93595 100644
--- a/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.html
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.html
@@ -64,7 +64,7 @@
<p-splitButton icon="fa fa-arrow-circle-o-down" class="dropdown" (onClick)="saveBasketWithExtension($event, 'mdm')" (onDropdownClick)="stopEvent($event)" title="{{'basket.mdm-basket.tooltip-download-shopping-basket' | translate }}" [model]="launchers" [disabled]="isDownloadDisabled()"></p-splitButton>
<div class="fileupload btn btn-mdm" title="{{'basket.mdm-basket.tooltip-upload-shopping-basket' | translate }}" (click)=onUploadClick($event)>
<span class="fa fa-arrow-circle-o-up"></span>
- <input title="{{'basket.mdm-basket.tooltip-upload-shopping-basket' | translate }}" class="upload" name="datei" type="file" accept=".json, application/json" id="fileInput" (change)="onUploadChange($event)">
+ <input title="{{'basket.mdm-basket.tooltip-upload-shopping-basket' | translate }}" class="upload" name="datei" type="file" [accept]="allowedExtensions" id="fileInput" (change)="onUploadChange($event)">
</div>
<button type="button" type="submit" class="btn btn-mdm" (click)="showLoadModal($event)" title="{{'basket.mdm-basket.tooltip-open-shopping-basket' | translate }}"><span class="fa fa-folder-open"></span></button>
<button type="button" type="submit" class="btn btn-mdm btn-outline-secondary" (click)="showSaveModal($event)" title="{{'basket.mdm-basket.tooltip-save-shopping-basket' | translate }}"><span class="fa fa-floppy-o"></span></button>
@@ -88,6 +88,8 @@
</accordion>
</div>
+<p-confirmDialog header="Information" icon="pi pi-exclamation-triangle" ></p-confirmDialog>
+
<div bsModal #lgLoadModal="bs-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="SelectSearchComponents" aria-hidden="true" (keyup.enter)="loadBasket()">
<div class="modal-dialog modal-md">
<div class="modal-content">
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.ts
index 195cfd2..c94b34c 100644
--- a/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.ts
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.component.ts
@@ -38,6 +38,10 @@
import { TranslateService } from '@ngx-translate/core';
import { streamTranslate, TRANSLATE } from '../core/mdm-core.module';
+import {ConfirmationService} from 'primeng/api';
+import { Observable } from 'rxjs/Observable';
+import { forkJoin, throwError } from 'rxjs';
+import { map, flatMap } from 'rxjs/operators';
@Component({
selector: 'mdm-basket',
@@ -49,6 +53,8 @@
@Output() onSelect = new EventEmitter<Node>();
@Input() activeNode: Node;
+ exportableTypes = ['Project', 'Pool', 'Test', 'TestStep', 'Measurement'];
+
basketName = '';
basketContent: SearchResult = new SearchResult();
basket = 'Warenkorb';
@@ -70,6 +76,7 @@
{label: 'ATFX', command: (event) => this.saveBasketAsATFX(event.originalEvent)}
];
launchers: MenuItem[] = [];
+ allowedExtensions: string[] = [];
@ViewChild(TableviewComponent)
tableViewComponent: TableviewComponent;
@@ -88,7 +95,8 @@
private sanitizer: DomSanitizer,
private nodeService: NodeService,
private notificationService: MDMNotificationService,
- private translateService: TranslateService) {
+ private translateService: TranslateService,
+ private confirmationService: ConfirmationService) {
}
removeSelected() {
@@ -106,10 +114,16 @@
));
this._basketService.getFileExtensions()
- .map(launchers => launchers.map(l => <MenuItem> {
- label: l.label,
- command: (event) => this.saveBasketWithExtension(event.originalEvent, l.extension) }))
- .subscribe(l => this.launchers = this.fixedLaunchers.concat(l) );
+ .map(launchers => <{ menuItems: MenuItem[], extensions: string[] }>{
+ menuItems: launchers.map((l: { label: string; extension: string; }) => <MenuItem> {
+ label: l.label,
+ command: (event) => this.saveBasketWithExtension(event.originalEvent, l.extension) }),
+ extensions: launchers.map((l: { extension: string; }) => '.' + l.extension).join(',')
+ })
+ .subscribe(({menuItems, extensions}) => {
+ this.launchers = this.fixedLaunchers.concat(menuItems);
+ this.allowedExtensions = [ 'application/xml' ].concat(extensions);
+ });
this.setItems(this._basketService.items);
@@ -249,11 +263,51 @@
.subscribe(blob => this.saveAsFile(blob, extension, 'shoppingbasket'));
}
+ isExportable(item: MDMItem) {
+ return this.exportableTypes.indexOf(item.type) >= 0;
+ }
+
saveBasketAsATFX(event: any) {
if (event) {
event.stopPropagation();
}
- let downloadContent = new Basket(this.basketName, this._basketService.getItems());
+ const notExportableItems = this._basketService.getItems().filter(i => !this.isExportable(i));
+ const exportableItems = this._basketService.getItems().filter(i => this.isExportable(i)).map(i => i.type).join(', ');
+
+ if (exportableItems.length === 0) {
+ // No exportable item in shopping basket -> show message and cancel export
+ const msg = this.translateService.instant('basket.mdm-basket.msg-atfx-only-supports-types',
+ { types: this.exportableTypes.join(', ') })
+ + ' ' + this.translateService.instant('basket.mdm-basket.msg-only-not-exportable-types-selected',
+ { types: notExportableItems.map(i => i.type).join(', ') });
+
+ this.confirmationService.confirm({
+ message: msg,
+ acceptLabel: 'OK',
+ rejectVisible: false,
+ });
+ } else if (notExportableItems.length > 0) {
+ // shopping basket contains not exportable items -> show message and ask if export should continue
+ const msg = this.translateService.instant('basket.mdm-basket.msg-atfx-only-supports-types',
+ { types: this.exportableTypes.join(', ') })
+ + ' ' + this.translateService.instant('basket.mdm-basket.msg-not-exportable-types-continue',
+ { types: exportableItems });
+
+ this.confirmationService.confirm({
+ message: msg,
+ acceptLabel: 'Yes',
+ rejectLabel: 'No',
+ icon: 'pi pi-question-circle',
+ accept: () => this.exportItemsToAtfx(this._basketService.getItems().filter(i => this.isExportable(i))),
+ rejectVisible: true,
+ });
+ } else {
+ this.exportItemsToAtfx(this._basketService.getItems())
+ }
+ }
+
+ exportItemsToAtfx(basket: MDMItem[]) {
+ let downloadContent = new Basket(this.basketName, basket);
this._basketService.getBasketAsAtfx(downloadContent)
.map(atfx => new Blob([atfx], { type: 'application/xml' }))
@@ -293,19 +347,67 @@
private onUploadEvent(fileInput: any) {
if (fileInput.files[0]) {
- let file = fileInput.files[0];
- let reader = new FileReader();
- reader.onloadend = (event) => {
- let upload: Basket = deserialize(Basket, <string> reader.result);
- this.loadBasket(upload);
- fileInput.value = '';
- };
- reader.readAsText(file);
+ const readFile = (blob: Blob): Observable<string> => Observable.create(o => {
+ if (!(blob instanceof Blob)) {
+ o.error(new Error('Parameter `blob` must be an instance of Blob.'));
+ return;
+ }
+
+ const fileReader = new FileReader();
+ fileReader.onload = () => o.next(fileReader.result);
+ fileReader.onloadend = () => o.complete();
+ fileReader.onabort = err => o.error(err);
+ fileReader.onerror = err => o.error(err);
+ return fileReader.readAsText(blob);
+ });
+
+ readFile(fileInput.files[0]).pipe(
+ flatMap(xml => this.parseXmlShoppingBasket(xml))
+ ).subscribe(
+ basket => this.loadBasket(basket),
+ err => this.notificationService.notifyWarn('Could not load shopping basket from file.', err)
+ );
+ }
}
+
+ private parseXmlShoppingBasket(xml: string) {
+ let oParser = new DOMParser();
+ let oDOM = oParser.parseFromString(xml, 'application/xml');
+ // print the name of the root element or error message
+ if (oDOM.documentElement.tagName !== 'shoppingbasket') {
+ if (oDOM.documentElement.innerText) {
+ return throwError(new Error('Error while parsing shopping basket: ' + oDOM.documentElement.innerText));
+ } else {
+ return throwError(new Error('Error while parsing shopping basket: XML is missing shoppingbasket root tag'));
+ }
+ }
+
+ // load the name of the shopping basket
+ let name = '';
+ let nameTags = oDOM.documentElement.getElementsByTagName('name');
+ if (nameTags.length > 0) {
+ name = nameTags[0].textContent;
+ }
+
+ // load the business objectes defined by the resturi values in the xml file
+ let items: Observable<Node[]>[] = [];
+ let uris = oDOM.documentElement.getElementsByTagName('resturi');
+ if (uris.length === 0) {
+ return throwError(new Error('No resturis found!'));
+ }
+ for (let uri of <any>uris) {
+ let url = uri.textContent;
+ items.push(this.nodeService.getNodesByAbsoluteUrl(url));
+ }
+
+ // map business objects to MDMItems and load basket
+ return forkJoin(items).pipe(
+ map(n => n.map(x => new MDMItem(x[0].sourceName, x[0].type, x[0].id))),
+ map(i => new Basket(name, i)));
}
private addData(rows: Row[]) {
- this.basketContent.rows.push(...rows);
+ this.basketContent.rows.push(... rows);
// copy and reasign to get new object refference triggering angular change detection in tableview.
this.basketContent = Object.assign({}, this.basketContent);
}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.module.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.module.ts
index a78281d..d74cbd6 100644
--- a/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.module.ts
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/basket/mdm-basket.module.ts
@@ -17,15 +17,17 @@
import { MDMCoreModule } from '../core/mdm-core.module';
import { TableViewModule } from '../tableview/tableview.module';
-import {SplitButtonModule} from 'primeng/primeng';
+import {SplitButtonModule, ConfirmDialogModule, ConfirmationService} from 'primeng/primeng';
@NgModule({
imports: [
MDMCoreModule,
TableViewModule,
- SplitButtonModule
+ SplitButtonModule,
+ ConfirmDialogModule,
],
declarations: [ MDMBasketComponent ],
exports: [ MDMBasketComponent],
+ providers: [ ConfirmationService ],
})
export class MDMBasketModule { }
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/node.service.ts b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/node.service.ts
index f57de6d..cfebb86 100644
--- a/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/node.service.ts
+++ b/org.eclipse.mdm.application/src/main/webapp/src/app/navigator/node.service.ts
@@ -137,6 +137,10 @@
return this.getNode(this._nodeUrl + url);
}
+ getNodesByAbsoluteUrl(url: string) {
+ return this.getNode(url);
+ }
+
getNodeByRelation(sourceName: string, type: string, id: string) {
return this.getNode(this._nodeUrl + '/' + sourceName + '/' + this.typeToUrl(type) + '/' + id);
}
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/i18n/de.json b/org.eclipse.mdm.application/src/main/webapp/src/assets/i18n/de.json
index 906aeec..0bd55fb 100644
--- a/org.eclipse.mdm.application/src/main/webapp/src/assets/i18n/de.json
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/i18n/de.json
@@ -57,7 +57,10 @@
"tooltip-no-name-set": "Kein Name ausgewählt",
"tooltip-open-shopping-basket": "Warenkorb öffnen",
"tooltip-save-shopping-basket": "Warenkorb speichern",
- "tooltip-upload-shopping-basket": "Warenkorb hochladen"
+ "tooltip-upload-shopping-basket": "Warenkorb hochladen",
+ "msg-atfx-only-supports-types": "Der ATFX Export unterstützt nur Elemente vom Typ: {{types}}.",
+ "msg-only-not-exportable-types-selected": "Aktuell sind nur Elemente vom Typ {{types}} selektiert!",
+ "msg-not-exportable-types-continue": "Einige Elemente sind vom Typ {{types}} und werden nicht exportiert. Sollen die übrigen Elemente exportiert werden?"
}
},
"core": {
diff --git a/org.eclipse.mdm.application/src/main/webapp/src/assets/i18n/en.json b/org.eclipse.mdm.application/src/main/webapp/src/assets/i18n/en.json
index 2ef7534..973741b 100644
--- a/org.eclipse.mdm.application/src/main/webapp/src/assets/i18n/en.json
+++ b/org.eclipse.mdm.application/src/main/webapp/src/assets/i18n/en.json
@@ -57,7 +57,10 @@
"tooltip-no-name-set": "No name set",
"tooltip-open-shopping-basket": "Open shopping basket",
"tooltip-save-shopping-basket": "Save shopping basket",
- "tooltip-upload-shopping-basket": "Upload shopping basket"
+ "tooltip-upload-shopping-basket": "Upload shopping basket",
+ "msg-atfx-only-supports-types": "ATFX export only supports export of types: {{types}}.",
+ "msg-only-not-exportable-types-selected": "Currently only items of type {{types}} are selected!",
+ "msg-not-exportable-types-continue": "Some items are of type {{types}} and will not be exported. Continue exporting supported types?"
}
},
"core": {
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/MDMEntity.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/MDMEntity.java
index 7c722c3..43b972f 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/MDMEntity.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/MDMEntity.java
@@ -28,9 +28,8 @@
import org.eclipse.mdm.businessobjects.utils.Serializer;
import org.eclipse.mdm.businessobjects.utils.ServiceUtils;
-import io.vavr.collection.HashMap;
-
import io.swagger.v3.oas.annotations.media.Schema;
+import io.vavr.collection.HashMap;
/**
* MDMEntity (Entity for a business object (contains a list of
@@ -58,8 +57,6 @@
/** list of relations to transfer */
private List<MDMRelation> relations;
- private String status;
-
/**
* Constructor.
*
@@ -257,10 +254,17 @@
io.vavr.collection.List.ofAll(entry._2).map(Deletable::getID).asJava()))
// append related entities from the mutable store
.appendAll(io.vavr.collection.List.ofAll(core.getMutableStore().getCurrent()).
- // convert list to map: use simple class name as this is the MDM entity type
- groupBy(e -> e.getClass().getSimpleName())
+ // convert list to map: use simple class name as this is the MDM entity type in
+ // conjunction with the ContextType
+ groupBy(e -> e.getClass().getSimpleName() + "_" + ServiceUtils.getContextType(e))
// create relation for every entry
- .map(entry -> new MDMRelation(null, MDMRelation.RelationType.MUTABLE, entry._1,
+ .map(entry -> new MDMRelation(null, MDMRelation.RelationType.MUTABLE,
+ entry._2.get(0).getClass().getSimpleName(),
+ ServiceUtils.getContextType(entry._2.get(0)), entry._2.map(Entity::getID).asJava())))
+ .appendAll(io.vavr.collection.List.ofAll(core.getPermanentStore().getCurrent())
+ .groupBy(e -> e.getClass().getSimpleName())
+ // create relation for every entry
+ .map(entry -> new MDMRelation(null, MDMRelation.RelationType.PARENT, entry._1,
ServiceUtils.getContextType(entry._2.get(0)), entry._2.map(Entity::getID).asJava())))
.asJava();
}
diff --git a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/MDMRelation.java b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/MDMRelation.java
index ea5b84e..d4ca79e 100644
--- a/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/MDMRelation.java
+++ b/org.eclipse.mdm.businessobjects/src/main/java/org/eclipse/mdm/businessobjects/entity/MDMRelation.java
@@ -38,7 +38,7 @@
private List<String> ids;
public enum RelationType {
- CHILDREN, MUTABLE;
+ CHILDREN, MUTABLE, PARENT;
}
/**
diff --git a/org.eclipse.mdm.openatfx/build.gradle b/org.eclipse.mdm.openatfx/build.gradle
index 38e4910..5568ded 100644
--- a/org.eclipse.mdm.openatfx/build.gradle
+++ b/org.eclipse.mdm.openatfx/build.gradle
@@ -19,7 +19,7 @@
import de.undercouch.gradle.tasks.download.Download
-version '0.7.4'
+def atfxVersion = '0.7.4'
description = 'Downloads openATFX and publishes it to the local maven repository'
dependencies {
@@ -29,7 +29,6 @@
// actually we just need the org.asam.ods.* classes
compile "org.eclipse.mdm:org.eclipse.mdm.api.odsadapter:${version}"
-
testCompile 'org.apache.commons:commons-math:2.2'
}
@@ -38,24 +37,34 @@
exclude group: 'javax.xml.stream', module: 'stax-api'
}
+jar {
+ manifest {
+ attributes(
+ 'Implementation-Title' : 'openATFX',
+ 'Implementation-Version' : atfxVersion,
+ 'Implementation-URL' : 'https://sourceforge.net/projects/openatfx'
+ )
+ }
+}
+
task downloadOpenATFX(type: Download) {
acceptAnyCertificate true
overwrite false
- // use mirror subdomain of 'https://sourceforge.net/projects/openatfx/files/openatfx-${version}-jars.zip/download'
+ // use mirror subdomain of 'https://sourceforge.net/projects/openatfx/files/openatfx-${atfxVersion}-jars.zip/download'
// because gradle has problems establishing a ssl connection with sourceforge directly
- src "https://liquidtelecom.dl.sourceforge.net/project/openatfx/openatfx-${version}-jars.zip"
- dest file("${buildDir}/openatfx-${version}-jars.zip")
+ src "https://liquidtelecom.dl.sourceforge.net/project/openatfx/openatfx-${atfxVersion}-jars.zip"
+ dest file("${buildDir}/openatfx-${atfxVersion}-jars.zip")
outputs.file dest
}
task unzipOpenATFX(dependsOn: downloadOpenATFX, type: Copy) {
from zipTree(downloadOpenATFX.dest)
into buildDir
- outputs.dir file("${buildDir}/openatfx-${version}")
+ outputs.dir file("${buildDir}/openatfx-${atfxVersion}")
}
task copySource(dependsOn: unzipOpenATFX, type: Copy) {
- from file("${buildDir}/openatfx-${version}/src")
+ from file("${buildDir}/openatfx-${atfxVersion}/src")
into 'src'
}
diff --git a/release_notes.md b/release_notes.md
index 2daf0e9..abec736 100644
--- a/release_notes.md
+++ b/release_notes.md
@@ -4,6 +4,15 @@
* [mdmbl Eclipse Git Repositories](http://git.eclipse.org/c/?q=mdmbl)
* [mdmbl nightly builds - last stable version](http://download.eclipse.org/mdmbl/nightly_master/?d)
+## Version 5.1.0 ##
+
+### Bugzilla Bugs fixed ###
+
+* [561163](https://bugs.eclipse.org/bugs/show_bug.cgi?id=561163) - Possible NPE in OdsEntityManager#loadTemplate()
+* [553867](https://bugs.eclipse.org/bugs/show_bug.cgi?id=553867) - Upload shopping basket should use xml format
+* [553699](https://bugs.eclipse.org/bugs/show_bug.cgi?id=553699) - Provide a user friendly error message if unsupported business objects are exported to atfx.
+* [560003](https://bugs.eclipse.org/bugs/show_bug.cgi?id=560003) - Relations in Response not shown correctly
+
## Version 5.1.0RC1 ##
New Features: