diff --git a/services/src/main/java/org/jboss/windup/web/services/model/AnalysisContext.java b/services/src/main/java/org/jboss/windup/web/services/model/AnalysisContext.java index d4d2babeeb..78fbb7d754 100644 --- a/services/src/main/java/org/jboss/windup/web/services/model/AnalysisContext.java +++ b/services/src/main/java/org/jboss/windup/web/services/model/AnalysisContext.java @@ -55,7 +55,7 @@ public class AnalysisContext implements Serializable @Fetch(FetchMode.SELECT) private Collection advancedOptions; -// @JsonProperty(access = JsonProperty.Access.WRITE_ONLY) + //@JsonProperty(access = JsonProperty.Access.WRITE_ONLY) @OneToOne(optional = false) private ApplicationGroup applicationGroup; diff --git a/services/src/main/java/org/jboss/windup/web/services/service/ApplicationGroupService.java b/services/src/main/java/org/jboss/windup/web/services/service/ApplicationGroupService.java index d462cb9d35..45e8cab3b6 100644 --- a/services/src/main/java/org/jboss/windup/web/services/service/ApplicationGroupService.java +++ b/services/src/main/java/org/jboss/windup/web/services/service/ApplicationGroupService.java @@ -23,14 +23,18 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.logging.Logger; import java.util.stream.Collectors; import java.util.stream.Stream; +import org.jboss.windup.util.exception.WindupException; /** * @author David Klingenberg */ public class ApplicationGroupService { + private static final Logger LOG = Logger.getLogger(ApplicationGroupService.class.getName()); + @PersistenceContext private EntityManager entityManager; @@ -83,24 +87,27 @@ public ApplicationGroup createDefaultApplicationGroup(MigrationProject project) return this.createApplicationGroup(ApplicationGroup.DEFAULT_NAME, project); } - public ApplicationGroup update(ApplicationGroup updatedGroup) + public ApplicationGroup update(ApplicationGroup updatedGroupDTO) { - ApplicationGroup original = this.entityManager.find(ApplicationGroup.class, updatedGroup.getId()); - original.setTitle(updatedGroup.getTitle()); - this.updateApplications(original, updatedGroup.getApplications()); + if (updatedGroupDTO.getId() == null) + throw new WindupException("Group ID is null."); - this.entityManager.merge(original); + ApplicationGroup original = this.entityManager.find(ApplicationGroup.class, updatedGroupDTO.getId()); + original.setTitle(updatedGroupDTO.getTitle()); + this.setIncludedApplications(original, updatedGroupDTO.getApplications()); + this.entityManager.merge(original); return original; } - public ApplicationGroup updateApplications(ApplicationGroup group, Collection newApplications) + private ApplicationGroup setIncludedApplications(ApplicationGroup group, Collection newApplicationSet) { - Set selectedApplicationIds = newApplications.stream() + Set includedApplicationIds = newApplicationSet.stream() .map(RegisteredApplication::getId) .collect(Collectors.toSet()); + LOG.fine("Updating App Group #" + group.getId() + ", includedApplicationIds: " + String.join(", ", includedApplicationIds.stream().map((id) -> "" + id).collect(Collectors.toList()))); - Collection applications = this.getAllApplicationsByIds(selectedApplicationIds); + Collection applications = this.getAllApplicationsByIds(includedApplicationIds); group.setApplications(new HashSet<>(applications)); applications.forEach(application -> { application.addApplicationGroup(group); diff --git a/ui/src/main/webapp/src/app/app.module.ts b/ui/src/main/webapp/src/app/app.module.ts index 76fd7d5578..d1972d9c9a 100644 --- a/ui/src/main/webapp/src/app/app.module.ts +++ b/ui/src/main/webapp/src/app/app.module.ts @@ -105,6 +105,7 @@ import {MomentModule} from "angular2-moment"; import {FileUploadModule, FileUploader} from "ng2-file-upload"; import {WizardComponent} from "./components/wizard.component"; import {DurationPipe} from "./components/duration.pipe"; +import {CheckboxesComponent} from "./components/reusable/checkboxes.component"; /** * Load all mapping data from the generated files. @@ -163,6 +164,7 @@ initializeModelMappingData(); RulesModalComponent, TechnologyComponent, + CheckboxesComponent, UploadQueueComponent, UploadProgressbarComponent, CustomRuleSelectionComponent, diff --git a/ui/src/main/webapp/src/app/components/analysis-context-form.component.html b/ui/src/main/webapp/src/app/components/analysis-context-form.component.html index dbea0629d7..d525ab919c 100644 --- a/ui/src/main/webapp/src/app/components/analysis-context-form.component.html +++ b/ui/src/main/webapp/src/app/components/analysis-context-form.component.html @@ -5,6 +5,7 @@

Loading...

+
@@ -16,8 +17,8 @@

Loading...

- -
+ +
-
-
diff --git a/ui/src/main/webapp/src/app/components/reusable/checkboxes.component.html b/ui/src/main/webapp/src/app/components/reusable/checkboxes.component.html new file mode 100644 index 0000000000..ced6afe606 --- /dev/null +++ b/ui/src/main/webapp/src/app/components/reusable/checkboxes.component.html @@ -0,0 +1,17 @@ + + +
+ + +
diff --git a/ui/src/main/webapp/src/app/components/reusable/checkboxes.component.ts b/ui/src/main/webapp/src/app/components/reusable/checkboxes.component.ts new file mode 100644 index 0000000000..dd4504c56e --- /dev/null +++ b/ui/src/main/webapp/src/app/components/reusable/checkboxes.component.ts @@ -0,0 +1,184 @@ +import {Component, OnInit, Input, ElementRef, SimpleChange, Output, EventEmitter, NgZone, OnChanges} from "@angular/core"; +import {Package} from "windup-services"; +import * as $ from "jquery"; +import {RegisteredApplication} from "windup-services"; +import {isString} from "util"; +import {handleError} from "typings/dist/support/cli"; + +export type ItemType = any; + +@Component({ + templateUrl: './checkboxes.component.html', + selector: 'wu-checkboxes' +}) +export class CheckboxesComponent implements OnInit, OnChanges +{ + /** + * The name of this checkboxes group. + */ + @Input() + groupName: string = "checkboxes"; + + /** + * Callbacks + */ + @Input() + valueCallback: (item: any) => string + = app => { throw new Error("valueCallback not yet defined." + + " This means Angular didn't keep the order of setting @Inputs."); }; + + @Input() + labelCallback: (item: ItemType) => string; + + /** + * All available options. + */ + @Input() + set options(options: ItemType[]){ + this._options = options; + console.log("set options()", options); + if (!options) + return; // May be loaded async. + if (!Array.isArray(options)) + throw new Error("Invalid @Input value for options: " + JSON.stringify(options)); + + options.forEach( + option => this.valueToOptionMap.set(this.valueCallback(option), option) + ); + } + get options(): ItemType[]{ + return this._options; + } + _options: ItemType[]; + + // For faster lookup of what option was clicked + private valueToOptionMap: Map = new Map(); + + + /** + * This can be either the values or a subset of options. + */ + @Input() //@Output() + checkedOptions: string[] | ItemType[] = []; + + @Output() + checkedOptionsChange = new EventEmitter(); + + shouldBeChecked(option: ItemType): boolean { + //console.log("shouldBeChecked()", option); + + if (!this.checkedOptions) + return false; + + if ((this.checkedOptions).indexOf(option) != -1) + return true; + + if ((this.checkedOptions).indexOf(this.valueCallback(option)) != -1) + return true; + + return false; + } + + /** + * Determines whether the checked options are returned as string values only (see valueCallback), + * or as a subset of options (object references). + * This is useful when checkedOptions is empty and we don't know which of these to set. + */ + @Input() + checkedOptionsAreValuesOnly: boolean | null = null; + + private initCheckedOptionsAreValuesOnly() { + if (!this.checkedOptions || this.checkedOptions.length == 0) + return; // Leave as is. + + this.checkedOptionsAreValuesOnly = isString(this.checkedOptions[0]); + } + + /// Does this duplicate checkedOptionsChange? + //@Output() + //onCheckedChange = new EventEmitter(); + + + + private component: CheckboxesComponent; + private rootElement; + + public constructor(element: ElementRef, private _zone: NgZone) { + this.component = this; + this.rootElement = element.nativeElement; + } + + public ngOnChanges(changes: {[options: string]: SimpleChange}): any { + console.warn("onChanges", changes['options']); + + /* /// This is handled by the setter? + if (changes.hasOwnProperty('options')) { + let newOptions: CheckboxData[] = changes['options'].currentValue; + this.options = newOptions; + } + */ + + if (changes.hasOwnProperty('checkedOptions')) { + this.checkedOptions = changes['checkedOptions'].currentValue; + } + + ///this.setChangeHandlersToCheckboxes(); + } + + public ngOnInit() { + //this.setChangeHandersToCheckboxes(); + } + + private setChangeHandlersToCheckboxes() { + console.log("setChangeHandersToCheckboxes() called"); + /*setTimeout(() => { + $(this.rootElement).find(":checkbox") + .off() + .change((event, data) => this.updateCheckedValues(event, data)); + }, 2000);*/ + } + + handleCheckboxChange(option: ItemType, $event) { + console.log("handleCheckboxChange() called", option, $event); + //$event.target.checked; + this.updateCheckedValues(); + } + + private updateCheckedValues(): void { + //this._zone.run(() => { + this.checkedOptions = this.getCheckedValues(); + this.checkedOptionsChange.emit(this.checkedOptions); + //this.onCheckedChange.emit(this.checkedOptions); + //}); + console.log("updateCheckedValues() done", this.checkedOptions); + } + + private getCheckedValues(): string[] | ItemType[] { + let component = this; + let values = $(this.rootElement).find(":checkbox:checked").map( + function (i, domElement): string { + if (!domElement['checked']) + return null; + let val = domElement["value"]; + + if (component.checkedOptionsAreValuesOnly) + return val; + + let clickedOption = component.valueToOptionMap.get(val); + if (!clickedOption) + return console.warn("Unknown option clicked", val), null; + else + return clickedOption; + } + ).get(); // http://api.jquery.com/map/ + return values; + } + +} + +export interface CheckboxData { + value: string, + label: string, + data: any, + checked: boolean +} diff --git a/ui/src/main/webapp/src/app/services/keycloak.service.ts b/ui/src/main/webapp/src/app/services/keycloak.service.ts index ef61e65192..a616e3068b 100644 --- a/ui/src/main/webapp/src/app/services/keycloak.service.ts +++ b/ui/src/main/webapp/src/app/services/keycloak.service.ts @@ -60,11 +60,8 @@ export class KeycloakService { let keyCloakPromise = this.keyCloak.init(options); let realPromise = this.transformKeycloakPromise(keyCloakPromise); - realPromise.then(success => console.log('Keycloak promise success', success)) - .catch(error => console.error('Keycloak promise error')); - + realPromise.then(success => true).catch(error => console.error("Keycloak promise error", error)); this.initObservable = Observable.fromPromise(realPromise); - return this.initObservable; }