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
|
||||
</div>
|
||||
|
||||
<div class="item breadcrumb" [routerLink]="['/Schedule', {id: dataService.schedule.id}]" routerLinkActive="itemActive" *ngIf="dataService.schedule">
|
||||
{{dataService.schedule.name}}
|
||||
</div>
|
||||
<!-- <div class="item breadcrumb" [routerLink]="['/Schedule', {id: dataService.schedule.id}]" routerLinkActive="itemActive" *ngIf="dataService.schedule">-->
|
||||
<!-- {{dataService.schedule.name}}-->
|
||||
<!-- </div>-->
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
@ -10,6 +10,7 @@ import {ScheduleListComponent} from './pages/schedule-list/schedule-list.compone
|
||||
import {FontAwesomeModule} from '@fortawesome/angular-fontawesome';
|
||||
import {NumberComponent} from './shared/number/number.component';
|
||||
import {ScheduleComponent} from "./pages/schedule/schedule.component";
|
||||
import {SearchComponent} from './shared/search/search.component';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
@ -18,6 +19,7 @@ import {ScheduleComponent} from "./pages/schedule/schedule.component";
|
||||
ScheduleComponent,
|
||||
ScheduleListComponent,
|
||||
NumberComponent,
|
||||
SearchComponent,
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
<app-edit-field [initial]="schedule.name" (valueChange)="set(null, 'name', $event)"></app-edit-field>
|
||||
</td>
|
||||
<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 colspan="9">
|
||||
<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 {ActivatedRoute} from "@angular/router";
|
||||
import {DataService} from "../../data.service";
|
||||
import {PropertyService} from "../../api/property/property.service";
|
||||
|
||||
@Component({
|
||||
selector: 'app-schedule',
|
||||
@ -26,6 +27,7 @@ export class ScheduleComponent implements OnInit {
|
||||
readonly scheduleService: ScheduleService,
|
||||
readonly scheduleEntryService: ScheduleEntryService,
|
||||
readonly dataService: DataService,
|
||||
readonly propertyService: PropertyService,
|
||||
) {
|
||||
// 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);
|
||||
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);
|
||||
createSunset(scheduleFlurRollladen, Zenith.CIVIL, 0, 100);
|
||||
scheduleRepository.save(scheduleFlurRollladen);
|
||||
|
||||
@ -21,17 +21,23 @@ public class KnxGroup {
|
||||
@Setter(AccessLevel.NONE)
|
||||
private Long id;
|
||||
|
||||
@Setter(AccessLevel.NONE)
|
||||
@Column(nullable = false, unique = true)
|
||||
private int addressRaw;
|
||||
|
||||
@Setter(AccessLevel.NONE)
|
||||
@Column(nullable = false, unique = true)
|
||||
private String addressStr;
|
||||
|
||||
@Setter(AccessLevel.NONE)
|
||||
@Column(nullable = false, unique = true)
|
||||
private String propertyName;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String dpt;
|
||||
|
||||
@Column(nullable = false)
|
||||
private String name;
|
||||
private String title;
|
||||
|
||||
@Column(nullable = false)
|
||||
@Enumerated(EnumType.STRING)
|
||||
@ -69,15 +75,11 @@ public class KnxGroup {
|
||||
public void setAddress(final GroupAddress groupAddress) {
|
||||
this.addressRaw = groupAddress.getRawAddress();
|
||||
this.addressStr = groupAddress.toString();
|
||||
this.propertyName = "knx.group." + groupAddress.getMainGroup() + "." + groupAddress.getMiddleGroup() + "." + groupAddress.getSubGroup8();
|
||||
}
|
||||
|
||||
public GroupAddress getAddress() {
|
||||
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();
|
||||
propertyName = knxGroup.getPropertyName();
|
||||
dpt = knxGroup.getDpt();
|
||||
name = knxGroup.getName();
|
||||
name = knxGroup.getTitle();
|
||||
propertyType = knxGroup.getPropertyType();
|
||||
|
||||
booleanValue = knxGroup.getBooleanValue();
|
||||
|
||||
@ -22,4 +22,8 @@ public interface KnxGroupRepository extends CrudRepository<KnxGroup, Long> {
|
||||
|
||||
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 java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<PropertyDto> findPropertyByName(final String propertyName) {
|
||||
return knxGroupRepository.findByPropertyName(propertyName).map(this::toPropertyDto);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<PropertyDto> findAllProperties() {
|
||||
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) {
|
||||
return new PropertyDto(
|
||||
knxGroup.getName(),
|
||||
knxGroup.getPropertyName(),
|
||||
knxGroup.getTitle(),
|
||||
knxGroup.getBooleanValue(),
|
||||
knxGroup.getNumberValue(),
|
||||
knxGroup.getValueTimestamp()
|
||||
|
||||
@ -113,7 +113,7 @@ public class KnxGroupWriteService {
|
||||
trans.setAddress(address);
|
||||
trans.setDpt(dpt);
|
||||
trans.setMultiGroup(multiGroup);
|
||||
trans.setName(name);
|
||||
trans.setTitle(name);
|
||||
trans.setPropertyType(type);
|
||||
trans.getRead().setAble(readable);
|
||||
return new KnxGroupDto(knxGroupRepository.save(trans));
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package de.ph87.homeautomation.property;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public interface IPropertyOwner {
|
||||
@ -15,4 +16,8 @@ public interface IPropertyOwner {
|
||||
|
||||
List<PropertyDto> findAllProperties();
|
||||
|
||||
List<PropertyDto> findAllPropertiesLike(final String like);
|
||||
|
||||
Optional<PropertyDto> findPropertyByName(final String propertyName);
|
||||
|
||||
}
|
||||
|
||||
@ -1,16 +1,17 @@
|
||||
package de.ph87.homeautomation.property;
|
||||
|
||||
import de.ph87.homeautomation.shared.ISearchController;
|
||||
import de.ph87.homeautomation.shared.KeyValuePair;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@RestController
|
||||
@RequestMapping("property")
|
||||
@RequiredArgsConstructor
|
||||
public class PropertyController {
|
||||
public class PropertyController implements ISearchController {
|
||||
|
||||
private final PropertyService propertyService;
|
||||
|
||||
@ -19,4 +20,15 @@ public class PropertyController {
|
||||
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 title;
|
||||
|
||||
public final Boolean booleanValue;
|
||||
|
||||
public final Number numberValue;
|
||||
|
||||
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.title = title;
|
||||
this.booleanValue = booleanValue;
|
||||
this.numberValue = numberValue;
|
||||
this.timestamp = timestamp;
|
||||
|
||||
@ -1,13 +1,12 @@
|
||||
package de.ph87.homeautomation.property;
|
||||
|
||||
import de.ph87.homeautomation.shared.Helpers;
|
||||
import de.ph87.office.web.NotFoundException;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
@Slf4j
|
||||
@Service
|
||||
@ -30,9 +29,7 @@ public class PropertyService {
|
||||
}
|
||||
|
||||
private IPropertyOwner getOwnerOrThrow(final String propertyName) {
|
||||
return propertyOwners.stream()
|
||||
.filter(iPropertyOwner -> iPropertyOwner.getPropertyNamePattern().matcher(propertyName).matches())
|
||||
.findFirst()
|
||||
return findOwner(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);
|
||||
}
|
||||
|
||||
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