implemented in-place-search for properties
This commit is contained in:
parent
ec8adf8643
commit
08f1c6f93f
9
src/main/angular/src/app/api/ISearchService.ts
Normal file
9
src/main/angular/src/app/api/ISearchService.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import {KeyValuePair} from "./KeyValuePair";
|
||||||
|
|
||||||
|
export interface ISearchService {
|
||||||
|
|
||||||
|
get(id: string, next: (results: KeyValuePair) => void, error: (error: any) => void): void;
|
||||||
|
|
||||||
|
search(term: string, next: (results: KeyValuePair[]) => void, error: (error: any) => void): void;
|
||||||
|
|
||||||
|
}
|
||||||
26
src/main/angular/src/app/api/KeyValuePair.ts
Normal file
26
src/main/angular/src/app/api/KeyValuePair.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import {validateStringNotEmptyNotNull} from "./validators";
|
||||||
|
|
||||||
|
export class KeyValuePair {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly key: string,
|
||||||
|
readonly value: string,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJson(json: any): KeyValuePair {
|
||||||
|
return new KeyValuePair(
|
||||||
|
validateStringNotEmptyNotNull(json['key']),
|
||||||
|
validateStringNotEmptyNotNull(json['value']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static trackBy(index: number, item: KeyValuePair): string {
|
||||||
|
return item.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static compareKey(a: KeyValuePair, b: KeyValuePair): number {
|
||||||
|
return a.value.localeCompare(b.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
import {TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {PropertyService} from './property.service';
|
||||||
|
|
||||||
|
describe('PropertyService', () => {
|
||||||
|
let service: PropertyService;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({});
|
||||||
|
service = TestBed.inject(PropertyService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be created', () => {
|
||||||
|
expect(service).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
54
src/main/angular/src/app/api/property/property.service.ts
Normal file
54
src/main/angular/src/app/api/property/property.service.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {ApiService, NO_OP} from "../api.service";
|
||||||
|
import {validateNumberNotNull, validateStringNotEmptyNotNull} from "../validators";
|
||||||
|
import {ISearchService} from "../ISearchService";
|
||||||
|
import {KeyValuePair} from "../KeyValuePair";
|
||||||
|
|
||||||
|
export class Property {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public name: string,
|
||||||
|
public type: string,
|
||||||
|
public value: number,
|
||||||
|
) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromJson(json: any): Property {
|
||||||
|
return new Property(
|
||||||
|
validateStringNotEmptyNotNull(json['name']),
|
||||||
|
validateStringNotEmptyNotNull(json['type']),
|
||||||
|
validateNumberNotNull(json['value']),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static trackBy(index: number, item: Property): string {
|
||||||
|
return item.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static compareName(a: Property, b: Property): number {
|
||||||
|
return a.name.localeCompare(b.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable({
|
||||||
|
providedIn: 'root'
|
||||||
|
})
|
||||||
|
export class PropertyService implements ISearchService {
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
readonly api: ApiService,
|
||||||
|
) {
|
||||||
|
// nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
get(id: string, next: (results: KeyValuePair) => void, error: (error: any) => void): void {
|
||||||
|
this.api.postReturnItem("property/getById", id, KeyValuePair.fromJson, next, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
search(term: string, next: (results: KeyValuePair[]) => void = NO_OP, error: (error: any) => void = NO_OP): void {
|
||||||
|
this.api.postReturnList("property/searchLike", term, KeyValuePair.fromJson, next, error);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -4,9 +4,9 @@
|
|||||||
Zeitpläne
|
Zeitpläne
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="item breadcrumb" [routerLink]="['/Schedule', {id: dataService.schedule.id}]" routerLinkActive="itemActive" *ngIf="dataService.schedule">
|
<!-- <div class="item breadcrumb" [routerLink]="['/Schedule', {id: dataService.schedule.id}]" routerLinkActive="itemActive" *ngIf="dataService.schedule">-->
|
||||||
{{dataService.schedule.name}}
|
<!-- {{dataService.schedule.name}}-->
|
||||||
</div>
|
<!-- </div>-->
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@ -10,6 +10,7 @@ import {ScheduleListComponent} from './pages/schedule-list/schedule-list.compone
|
|||||||
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
||||||
import {NumberComponent} from './shared/number/number.component';
|
import {NumberComponent} from './shared/number/number.component';
|
||||||
import {ScheduleComponent} from "./pages/schedule/schedule.component";
|
import {ScheduleComponent} from "./pages/schedule/schedule.component";
|
||||||
|
import {SearchComponent} from './shared/search/search.component';
|
||||||
|
|
||||||
@NgModule({
|
@NgModule({
|
||||||
declarations: [
|
declarations: [
|
||||||
@ -18,6 +19,7 @@ import {ScheduleComponent} from "./pages/schedule/schedule.component";
|
|||||||
ScheduleComponent,
|
ScheduleComponent,
|
||||||
ScheduleListComponent,
|
ScheduleListComponent,
|
||||||
NumberComponent,
|
NumberComponent,
|
||||||
|
SearchComponent,
|
||||||
],
|
],
|
||||||
imports: [
|
imports: [
|
||||||
BrowserModule,
|
BrowserModule,
|
||||||
|
|||||||
@ -14,7 +14,7 @@
|
|||||||
<app-edit-field [initial]="schedule.name" (valueChange)="set(null, 'name', $event)"></app-edit-field>
|
<app-edit-field [initial]="schedule.name" (valueChange)="set(null, 'name', $event)"></app-edit-field>
|
||||||
</td>
|
</td>
|
||||||
<td colspan="5">
|
<td colspan="5">
|
||||||
<app-edit-field [initial]="schedule.propertyName" (valueChange)="set(null, 'propertyName', $event)"></app-edit-field>
|
<app-search [searchService]="propertyService" [initial]="schedule.propertyName" (valueChange)="set(null, 'propertyName', $event)"></app-search>
|
||||||
</td>
|
</td>
|
||||||
<td colspan="9">
|
<td colspan="9">
|
||||||
<select [(ngModel)]="schedule.propertyType" (ngModelChange)="set(null,'propertyType', schedule.propertyType)">
|
<select [(ngModel)]="schedule.propertyType" (ngModelChange)="set(null,'propertyType', schedule.propertyType)">
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import {ScheduleEntryService} from "../../api/schedule/entry/schedule-entry.serv
|
|||||||
import {faCheckCircle, faCircle, faTimesCircle} from '@fortawesome/free-regular-svg-icons';
|
import {faCheckCircle, faCircle, faTimesCircle} from '@fortawesome/free-regular-svg-icons';
|
||||||
import {ActivatedRoute} from "@angular/router";
|
import {ActivatedRoute} from "@angular/router";
|
||||||
import {DataService} from "../../data.service";
|
import {DataService} from "../../data.service";
|
||||||
|
import {PropertyService} from "../../api/property/property.service";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-schedule',
|
selector: 'app-schedule',
|
||||||
@ -26,6 +27,7 @@ export class ScheduleComponent implements OnInit {
|
|||||||
readonly scheduleService: ScheduleService,
|
readonly scheduleService: ScheduleService,
|
||||||
readonly scheduleEntryService: ScheduleEntryService,
|
readonly scheduleEntryService: ScheduleEntryService,
|
||||||
readonly dataService: DataService,
|
readonly dataService: DataService,
|
||||||
|
readonly propertyService: PropertyService,
|
||||||
) {
|
) {
|
||||||
// nothing
|
// nothing
|
||||||
}
|
}
|
||||||
|
|||||||
20
src/main/angular/src/app/shared/search/search.component.html
Normal file
20
src/main/angular/src/app/shared/search/search.component.html
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<div
|
||||||
|
*ngIf="!searching"
|
||||||
|
(click)="startSearch()"
|
||||||
|
[class.empty]="!selected"
|
||||||
|
>
|
||||||
|
{{selected?.value ? selected?.value : "-LEER-"}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<input
|
||||||
|
#input
|
||||||
|
type="text"
|
||||||
|
*ngIf="searching"
|
||||||
|
[(ngModel)]="term"
|
||||||
|
(ngModelChange)="changed()"
|
||||||
|
(keydown)="inputKeyPress($event)"
|
||||||
|
>
|
||||||
|
|
||||||
|
<div #resultList *ngIf="searching" class="resultList">
|
||||||
|
<div *ngFor="let result of results" class="result" (click)="select(result)">{{result.value}}</div>
|
||||||
|
</div>
|
||||||
14
src/main/angular/src/app/shared/search/search.component.less
Normal file
14
src/main/angular/src/app/shared/search/search.component.less
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
.resultList {
|
||||||
|
position: absolute;
|
||||||
|
background-color: lightgray;
|
||||||
|
min-width: 200px;
|
||||||
|
border: 1px solid black;
|
||||||
|
|
||||||
|
.result {
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result:hover {
|
||||||
|
background-color: lightyellow;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import {ComponentFixture, TestBed} from '@angular/core/testing';
|
||||||
|
|
||||||
|
import {SearchComponent} from './search.component';
|
||||||
|
|
||||||
|
describe('SearchComponent', () => {
|
||||||
|
let component: SearchComponent;
|
||||||
|
let fixture: ComponentFixture<SearchComponent>;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
await TestBed.configureTestingModule({
|
||||||
|
declarations: [SearchComponent]
|
||||||
|
})
|
||||||
|
.compileComponents();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
fixture = TestBed.createComponent(SearchComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create', () => {
|
||||||
|
expect(component).toBeTruthy();
|
||||||
|
});
|
||||||
|
});
|
||||||
100
src/main/angular/src/app/shared/search/search.component.ts
Normal file
100
src/main/angular/src/app/shared/search/search.component.ts
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
import {Component, ElementRef, EventEmitter, Input, OnInit, Output, ViewChild} from '@angular/core';
|
||||||
|
import {KeyValuePair} from "../../api/KeyValuePair";
|
||||||
|
import {ISearchService} from "../../api/ISearchService";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'app-search',
|
||||||
|
templateUrl: './search.component.html',
|
||||||
|
styleUrls: ['./search.component.less']
|
||||||
|
})
|
||||||
|
export class SearchComponent<T> implements OnInit {
|
||||||
|
|
||||||
|
private timeout: number | undefined;
|
||||||
|
|
||||||
|
@ViewChild('input')
|
||||||
|
input2?: ElementRef;
|
||||||
|
|
||||||
|
@ViewChild('input')
|
||||||
|
input?: HTMLInputElement;
|
||||||
|
|
||||||
|
@ViewChild('resultList')
|
||||||
|
resultList?: HTMLDivElement;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
searchService!: ISearchService;
|
||||||
|
|
||||||
|
@Input()
|
||||||
|
initial!: string;
|
||||||
|
|
||||||
|
@Output()
|
||||||
|
valueChange: EventEmitter<string> = new EventEmitter<string>();
|
||||||
|
|
||||||
|
term: string = "";
|
||||||
|
|
||||||
|
results: KeyValuePair[] = [];
|
||||||
|
|
||||||
|
selected?: KeyValuePair;
|
||||||
|
|
||||||
|
searching: boolean = false;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
}
|
||||||
|
|
||||||
|
ngOnInit(): void {
|
||||||
|
this.searchService.get(this.initial, result => this.selected = result, _ => _);
|
||||||
|
}
|
||||||
|
|
||||||
|
changed(): void {
|
||||||
|
this.clearTimeout();
|
||||||
|
this.timeout = setTimeout(() => this.doSearch(), 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
private clearTimeout(): void {
|
||||||
|
if (this.timeout) {
|
||||||
|
clearTimeout(this.timeout);
|
||||||
|
this.timeout = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startSearch(): void {
|
||||||
|
this.term = this.initial;
|
||||||
|
if (this.resultList && this.input) {
|
||||||
|
this.resultList.style.left = this.input.style.left;
|
||||||
|
}
|
||||||
|
this.searching = true;
|
||||||
|
setTimeout(() => this.input2?.nativeElement.focus(), 0);
|
||||||
|
this.doSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
inputKeyPress($event: KeyboardEvent): void {
|
||||||
|
switch ($event.key) {
|
||||||
|
case 'Enter':
|
||||||
|
this.doSearch();
|
||||||
|
break;
|
||||||
|
case 'Escape':
|
||||||
|
this.cancelSearch();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
doSearch(): void {
|
||||||
|
this.clearTimeout();
|
||||||
|
if (!this.term) {
|
||||||
|
this.results = [];
|
||||||
|
} else {
|
||||||
|
this.searchService.search(this.term, results => this.results = results, _ => _);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelSearch(): void {
|
||||||
|
setTimeout(() => this.searching = false, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
select(result: KeyValuePair): void {
|
||||||
|
console.log(result);
|
||||||
|
this.searching = false;
|
||||||
|
this.selected = result;
|
||||||
|
this.valueChange.emit(this.selected?.key);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -78,7 +78,7 @@ public class DemoDataService {
|
|||||||
createSunset(scheduleSchlafzimmerRollladen, Zenith.CIVIL, 0, 100);
|
createSunset(scheduleSchlafzimmerRollladen, Zenith.CIVIL, 0, 100);
|
||||||
scheduleRepository.save(scheduleSchlafzimmerRollladen);
|
scheduleRepository.save(scheduleSchlafzimmerRollladen);
|
||||||
|
|
||||||
final Schedule scheduleFlurRollladen = createSchedule("Rollläden Flur", flur_rollladen_position_anfahren, PropertyType.SHUTTER);
|
final Schedule scheduleFlurRollladen = createSchedule("Rollladen Flur", flur_rollladen_position_anfahren, PropertyType.SHUTTER);
|
||||||
createSunrise(scheduleFlurRollladen, Zenith.CIVIL, 0, 0);
|
createSunrise(scheduleFlurRollladen, Zenith.CIVIL, 0, 0);
|
||||||
createSunset(scheduleFlurRollladen, Zenith.CIVIL, 0, 100);
|
createSunset(scheduleFlurRollladen, Zenith.CIVIL, 0, 100);
|
||||||
scheduleRepository.save(scheduleFlurRollladen);
|
scheduleRepository.save(scheduleFlurRollladen);
|
||||||
|
|||||||
@ -21,17 +21,23 @@ public class KnxGroup {
|
|||||||
@Setter(AccessLevel.NONE)
|
@Setter(AccessLevel.NONE)
|
||||||
private Long id;
|
private Long id;
|
||||||
|
|
||||||
|
@Setter(AccessLevel.NONE)
|
||||||
@Column(nullable = false, unique = true)
|
@Column(nullable = false, unique = true)
|
||||||
private int addressRaw;
|
private int addressRaw;
|
||||||
|
|
||||||
|
@Setter(AccessLevel.NONE)
|
||||||
@Column(nullable = false, unique = true)
|
@Column(nullable = false, unique = true)
|
||||||
private String addressStr;
|
private String addressStr;
|
||||||
|
|
||||||
|
@Setter(AccessLevel.NONE)
|
||||||
|
@Column(nullable = false, unique = true)
|
||||||
|
private String propertyName;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private String dpt;
|
private String dpt;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
private String name;
|
private String title;
|
||||||
|
|
||||||
@Column(nullable = false)
|
@Column(nullable = false)
|
||||||
@Enumerated(EnumType.STRING)
|
@Enumerated(EnumType.STRING)
|
||||||
@ -69,15 +75,11 @@ public class KnxGroup {
|
|||||||
public void setAddress(final GroupAddress groupAddress) {
|
public void setAddress(final GroupAddress groupAddress) {
|
||||||
this.addressRaw = groupAddress.getRawAddress();
|
this.addressRaw = groupAddress.getRawAddress();
|
||||||
this.addressStr = groupAddress.toString();
|
this.addressStr = groupAddress.toString();
|
||||||
|
this.propertyName = "knx.group." + groupAddress.getMainGroup() + "." + groupAddress.getMiddleGroup() + "." + groupAddress.getSubGroup8();
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupAddress getAddress() {
|
public GroupAddress getAddress() {
|
||||||
return new GroupAddress(addressRaw);
|
return new GroupAddress(addressRaw);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getPropertyName() {
|
|
||||||
final GroupAddress address = getAddress();
|
|
||||||
return "knx.group." + address.getMainGroup() + "." + address.getMiddleGroup() + "." + address.getSubGroup8();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -34,7 +34,7 @@ public class KnxGroupDto {
|
|||||||
addressStr = knxGroup.getAddressStr();
|
addressStr = knxGroup.getAddressStr();
|
||||||
propertyName = knxGroup.getPropertyName();
|
propertyName = knxGroup.getPropertyName();
|
||||||
dpt = knxGroup.getDpt();
|
dpt = knxGroup.getDpt();
|
||||||
name = knxGroup.getName();
|
name = knxGroup.getTitle();
|
||||||
propertyType = knxGroup.getPropertyType();
|
propertyType = knxGroup.getPropertyType();
|
||||||
|
|
||||||
booleanValue = knxGroup.getBooleanValue();
|
booleanValue = knxGroup.getBooleanValue();
|
||||||
|
|||||||
@ -22,4 +22,8 @@ public interface KnxGroupRepository extends CrudRepository<KnxGroup, Long> {
|
|||||||
|
|
||||||
boolean existsByAddressRaw(int rawAddress);
|
boolean existsByAddressRaw(int rawAddress);
|
||||||
|
|
||||||
|
List<KnxGroup> findAllByPropertyNameLikeIgnoreCaseOrTitleLikeIgnoreCase(String propertyNameLike, final String titleLike);
|
||||||
|
|
||||||
|
Optional<KnxGroup> findByPropertyName(String propertyName);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,7 @@ import org.springframework.stereotype.Service;
|
|||||||
import tuwien.auto.calimero.GroupAddress;
|
import tuwien.auto.calimero.GroupAddress;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -66,14 +67,25 @@ public class KnxGroupSetService implements IPropertyOwner {
|
|||||||
return knxGroupRepository.findByAddressRaw(parseGroupAddress(propertyName).getRawAddress()).map(KnxGroup::getNumberValue).orElse(null);
|
return knxGroupRepository.findByAddressRaw(parseGroupAddress(propertyName).getRawAddress()).map(KnxGroup::getNumberValue).orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Optional<PropertyDto> findPropertyByName(final String propertyName) {
|
||||||
|
return knxGroupRepository.findByPropertyName(propertyName).map(this::toPropertyDto);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<PropertyDto> findAllProperties() {
|
public List<PropertyDto> findAllProperties() {
|
||||||
return knxGroupRepository.findAll().stream().map(this::toPropertyDto).collect(Collectors.toList());
|
return knxGroupRepository.findAll().stream().map(this::toPropertyDto).collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<PropertyDto> findAllPropertiesLike(final String like) {
|
||||||
|
return knxGroupRepository.findAllByPropertyNameLikeIgnoreCaseOrTitleLikeIgnoreCase(like, like).stream().map(this::toPropertyDto).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
private PropertyDto toPropertyDto(final KnxGroup knxGroup) {
|
private PropertyDto toPropertyDto(final KnxGroup knxGroup) {
|
||||||
return new PropertyDto(
|
return new PropertyDto(
|
||||||
knxGroup.getName(),
|
knxGroup.getPropertyName(),
|
||||||
|
knxGroup.getTitle(),
|
||||||
knxGroup.getBooleanValue(),
|
knxGroup.getBooleanValue(),
|
||||||
knxGroup.getNumberValue(),
|
knxGroup.getNumberValue(),
|
||||||
knxGroup.getValueTimestamp()
|
knxGroup.getValueTimestamp()
|
||||||
|
|||||||
@ -113,7 +113,7 @@ public class KnxGroupWriteService {
|
|||||||
trans.setAddress(address);
|
trans.setAddress(address);
|
||||||
trans.setDpt(dpt);
|
trans.setDpt(dpt);
|
||||||
trans.setMultiGroup(multiGroup);
|
trans.setMultiGroup(multiGroup);
|
||||||
trans.setName(name);
|
trans.setTitle(name);
|
||||||
trans.setPropertyType(type);
|
trans.setPropertyType(type);
|
||||||
trans.getRead().setAble(readable);
|
trans.getRead().setAble(readable);
|
||||||
return new KnxGroupDto(knxGroupRepository.save(trans));
|
return new KnxGroupDto(knxGroupRepository.save(trans));
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package de.ph87.homeautomation.property;
|
package de.ph87.homeautomation.property;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
public interface IPropertyOwner {
|
public interface IPropertyOwner {
|
||||||
@ -15,4 +16,8 @@ public interface IPropertyOwner {
|
|||||||
|
|
||||||
List<PropertyDto> findAllProperties();
|
List<PropertyDto> findAllProperties();
|
||||||
|
|
||||||
|
List<PropertyDto> findAllPropertiesLike(final String like);
|
||||||
|
|
||||||
|
Optional<PropertyDto> findPropertyByName(final String propertyName);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,16 +1,17 @@
|
|||||||
package de.ph87.homeautomation.property;
|
package de.ph87.homeautomation.property;
|
||||||
|
|
||||||
|
import de.ph87.homeautomation.shared.ISearchController;
|
||||||
|
import de.ph87.homeautomation.shared.KeyValuePair;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.web.bind.annotation.GetMapping;
|
import org.springframework.web.bind.annotation.*;
|
||||||
import org.springframework.web.bind.annotation.RequestMapping;
|
|
||||||
import org.springframework.web.bind.annotation.RestController;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
@RestController
|
@RestController
|
||||||
@RequestMapping("property")
|
@RequestMapping("property")
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class PropertyController {
|
public class PropertyController implements ISearchController {
|
||||||
|
|
||||||
private final PropertyService propertyService;
|
private final PropertyService propertyService;
|
||||||
|
|
||||||
@ -19,4 +20,15 @@ public class PropertyController {
|
|||||||
return propertyService.findAll();
|
return propertyService.findAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("getById")
|
||||||
|
public KeyValuePair getById(@RequestBody final String id) {
|
||||||
|
final PropertyDto propertyDto = propertyService.getById(id);
|
||||||
|
return new KeyValuePair(propertyDto.name, propertyDto.title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PostMapping("searchLike")
|
||||||
|
public List<KeyValuePair> searchLike(@RequestBody final String term) {
|
||||||
|
return propertyService.findAllLike("%" + term + "%").stream().map(propertyDto -> new KeyValuePair(propertyDto.name, propertyDto.title)).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,14 +9,17 @@ public class PropertyDto {
|
|||||||
|
|
||||||
public final String name;
|
public final String name;
|
||||||
|
|
||||||
|
public final String title;
|
||||||
|
|
||||||
public final Boolean booleanValue;
|
public final Boolean booleanValue;
|
||||||
|
|
||||||
public final Number numberValue;
|
public final Number numberValue;
|
||||||
|
|
||||||
public final ZonedDateTime timestamp;
|
public final ZonedDateTime timestamp;
|
||||||
|
|
||||||
public PropertyDto(final String name, final Boolean booleanValue, final Number numberValue, final ZonedDateTime timestamp) {
|
public PropertyDto(final String name, final String title, final Boolean booleanValue, final Number numberValue, final ZonedDateTime timestamp) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.title = title;
|
||||||
this.booleanValue = booleanValue;
|
this.booleanValue = booleanValue;
|
||||||
this.numberValue = numberValue;
|
this.numberValue = numberValue;
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
|
|||||||
@ -1,13 +1,12 @@
|
|||||||
package de.ph87.homeautomation.property;
|
package de.ph87.homeautomation.property;
|
||||||
|
|
||||||
import de.ph87.homeautomation.shared.Helpers;
|
import de.ph87.homeautomation.shared.Helpers;
|
||||||
|
import de.ph87.office.web.NotFoundException;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
@ -30,9 +29,7 @@ public class PropertyService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private IPropertyOwner getOwnerOrThrow(final String propertyName) {
|
private IPropertyOwner getOwnerOrThrow(final String propertyName) {
|
||||||
return propertyOwners.stream()
|
return findOwner(propertyName)
|
||||||
.filter(iPropertyOwner -> iPropertyOwner.getPropertyNamePattern().matcher(propertyName).matches())
|
|
||||||
.findFirst()
|
|
||||||
.orElseThrow(() -> new RuntimeException("No IPropertyOwner found for propertyName: " + propertyName));
|
.orElseThrow(() -> new RuntimeException("No IPropertyOwner found for propertyName: " + propertyName));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,4 +37,24 @@ public class PropertyService {
|
|||||||
return propertyOwners.stream().map(IPropertyOwner::findAllProperties).reduce(new ArrayList<>(), Helpers::merge);
|
return propertyOwners.stream().map(IPropertyOwner::findAllProperties).reduce(new ArrayList<>(), Helpers::merge);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<PropertyDto> findAllLike(final String like) {
|
||||||
|
return propertyOwners.stream().map(iProperyOwner -> iProperyOwner.findAllPropertiesLike(like)).reduce(PropertyService::merge).orElse(Collections.emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static <T> List<T> merge(final List<T> a, final List<T> b) {
|
||||||
|
final ArrayList<T> c = new ArrayList<>(a);
|
||||||
|
c.addAll(b);
|
||||||
|
return c;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PropertyDto getById(final String propertyName) {
|
||||||
|
return findOwner(propertyName).flatMap(iPropertyOwner -> iPropertyOwner.findPropertyByName(propertyName)).orElseThrow(() -> new NotFoundException("Property.name=%s", propertyName));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Optional<IPropertyOwner> findOwner(final String propertyName) {
|
||||||
|
return propertyOwners.stream()
|
||||||
|
.filter(iPropertyOwner -> iPropertyOwner.getPropertyNamePattern().matcher(propertyName).matches())
|
||||||
|
.findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,11 @@
|
|||||||
|
package de.ph87.homeautomation.shared;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface ISearchController {
|
||||||
|
|
||||||
|
KeyValuePair getById(final String id);
|
||||||
|
|
||||||
|
List<KeyValuePair> searchLike(final String term);
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
package de.ph87.homeautomation.shared;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
public class KeyValuePair {
|
||||||
|
|
||||||
|
public final String key;
|
||||||
|
|
||||||
|
public final String value;
|
||||||
|
|
||||||
|
public KeyValuePair(final String key, final String value) {
|
||||||
|
this.key = key;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user