mirror of
https://github.com/danieldemus/openhab-core.git
synced 2026-07-29 12:34:22 +02:00
Persistence aliases (#4363)
Signed-off-by: Mark Herwege <mark.herwege@telenet.be>
This commit is contained in:
+67
-18
@@ -16,9 +16,13 @@ import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.annotation.security.RolesAllowed;
|
||||
import javax.ws.rs.Consumes;
|
||||
@@ -104,6 +108,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
* @author Lyubomir Papazov - Change java.util.Date references to be of type java.time.ZonedDateTime
|
||||
* @author Markus Rathgeb - Migrated to JAX-RS Whiteboard Specification
|
||||
* @author Wouter Born - Migrated to OpenAPI annotations
|
||||
* @author Mark Herwege - Implement aliases
|
||||
*/
|
||||
@Component
|
||||
@JaxrsResource
|
||||
@@ -181,9 +186,10 @@ public class PersistenceResource implements RESTResource {
|
||||
PersistenceService service = persistenceServiceRegistry.get(serviceId);
|
||||
if (service != null) {
|
||||
List<PersistenceStrategy> strategies = service.getDefaultStrategies();
|
||||
List<PersistenceItemConfiguration> configs = List.of(
|
||||
new PersistenceItemConfiguration(List.of(new PersistenceAllConfig()), null, strategies, null));
|
||||
configuration = new PersistenceServiceConfiguration(serviceId, configs, strategies, strategies,
|
||||
List<PersistenceItemConfiguration> configs = List
|
||||
.of(new PersistenceItemConfiguration(List.of(new PersistenceAllConfig()), strategies, null));
|
||||
Map<String, String> aliases = Map.of();
|
||||
configuration = new PersistenceServiceConfiguration(serviceId, configs, aliases, strategies, strategies,
|
||||
List.of());
|
||||
editable = true;
|
||||
}
|
||||
@@ -368,6 +374,9 @@ public class PersistenceResource implements RESTResource {
|
||||
// If serviceId is null, then use the default service
|
||||
PersistenceService service;
|
||||
String effectiveServiceId = serviceId != null ? serviceId : persistenceServiceRegistry.getDefaultId();
|
||||
if (effectiveServiceId == null) {
|
||||
return null;
|
||||
}
|
||||
service = persistenceServiceRegistry.get(effectiveServiceId);
|
||||
|
||||
if (service == null) {
|
||||
@@ -416,6 +425,8 @@ public class PersistenceResource implements RESTResource {
|
||||
|
||||
ItemHistoryDTO dto = new ItemHistoryDTO();
|
||||
dto.name = itemName;
|
||||
PersistenceServiceConfiguration config = persistenceServiceConfigurationRegistry.get(effectiveServiceId);
|
||||
String alias = config != null ? config.getAliases().get(itemName) : null;
|
||||
|
||||
// If "boundary" is true then we want to get one value before and after the requested period
|
||||
// This is necessary for values that don't change often otherwise data will start after the start of the graph
|
||||
@@ -427,7 +438,7 @@ public class PersistenceResource implements RESTResource {
|
||||
filterBeforeStart.setEndDate(dateTimeBegin);
|
||||
filterBeforeStart.setPageSize(1);
|
||||
filterBeforeStart.setOrdering(Ordering.DESCENDING);
|
||||
result = qService.query(filterBeforeStart);
|
||||
result = qService.query(filterBeforeStart, alias);
|
||||
if (result.iterator().hasNext()) {
|
||||
dto.addData(dateTimeBegin.toInstant().toEpochMilli(), result.iterator().next().getState());
|
||||
quantity++;
|
||||
@@ -446,7 +457,7 @@ public class PersistenceResource implements RESTResource {
|
||||
filter.setBeginDate(dateTimeBegin);
|
||||
filter.setEndDate(dateTimeEnd);
|
||||
filter.setOrdering(Ordering.ASCENDING);
|
||||
result = qService.query(filter);
|
||||
result = qService.query(filter, alias);
|
||||
Iterator<HistoricItem> it = result.iterator();
|
||||
|
||||
// Iterate through the data
|
||||
@@ -478,7 +489,7 @@ public class PersistenceResource implements RESTResource {
|
||||
filterAfterEnd.setBeginDate(dateTimeEnd);
|
||||
filterAfterEnd.setPageSize(1);
|
||||
filterAfterEnd.setOrdering(Ordering.ASCENDING);
|
||||
result = qService.query(filterAfterEnd);
|
||||
result = qService.query(filterAfterEnd, alias);
|
||||
if (result.iterator().hasNext()) {
|
||||
dto.addData(dateTimeEnd.toInstant().toEpochMilli(), result.iterator().next().getState());
|
||||
quantity++;
|
||||
@@ -544,26 +555,60 @@ public class PersistenceResource implements RESTResource {
|
||||
private Response getServiceItemList(@Nullable String serviceId) {
|
||||
// If serviceId is null, then use the default service
|
||||
PersistenceService service;
|
||||
if (serviceId == null) {
|
||||
service = persistenceServiceRegistry.getDefault();
|
||||
} else {
|
||||
service = persistenceServiceRegistry.get(serviceId);
|
||||
String effectiveServiceId = serviceId != null ? serviceId : persistenceServiceRegistry.getDefaultId();
|
||||
if (effectiveServiceId == null) {
|
||||
logger.debug("Persistence service not found '{}'.", effectiveServiceId);
|
||||
return JSONResponse.createErrorResponse(Status.BAD_REQUEST,
|
||||
"Persistence service not found: " + effectiveServiceId);
|
||||
}
|
||||
service = persistenceServiceRegistry.get(effectiveServiceId);
|
||||
|
||||
if (service == null) {
|
||||
logger.debug("Persistence service not found '{}'.", serviceId);
|
||||
return JSONResponse.createErrorResponse(Status.BAD_REQUEST, "Persistence service not found: " + serviceId);
|
||||
logger.debug("Persistence service not found '{}'.", effectiveServiceId);
|
||||
return JSONResponse.createErrorResponse(Status.BAD_REQUEST,
|
||||
"Persistence service not found: " + effectiveServiceId);
|
||||
}
|
||||
|
||||
if (!(service instanceof QueryablePersistenceService)) {
|
||||
logger.debug("Persistence service not queryable '{}'.", serviceId);
|
||||
logger.debug("Persistence service not queryable '{}'.", effectiveServiceId);
|
||||
return JSONResponse.createErrorResponse(Status.BAD_REQUEST,
|
||||
"Persistence service not queryable: " + serviceId);
|
||||
"Persistence service not queryable: " + effectiveServiceId);
|
||||
}
|
||||
|
||||
QueryablePersistenceService qService = (QueryablePersistenceService) service;
|
||||
|
||||
return JSONResponse.createResponse(Status.OK, qService.getItemInfo(), "");
|
||||
PersistenceServiceConfiguration config = persistenceServiceConfigurationRegistry.get(effectiveServiceId);
|
||||
Map<String, String> aliases = config != null ? config.getAliases() : Map.of();
|
||||
Set<PersistenceItemInfo> itemInfo = qService.getItemInfo().stream().map(info -> {
|
||||
String alias = aliases.get(info.getName());
|
||||
if (alias != null) {
|
||||
return new PersistenceItemInfo() {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return alias;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Integer getCount() {
|
||||
return info.getCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Date getEarliest() {
|
||||
return info.getEarliest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Date getLatest() {
|
||||
return info.getLatest();
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return info;
|
||||
}
|
||||
}).collect(Collectors.toSet());
|
||||
return JSONResponse.createResponse(Status.OK, itemInfo, "");
|
||||
}
|
||||
|
||||
private Response deletePersistenceItemData(@Nullable String serviceId, String itemName, @Nullable String timeBegin,
|
||||
@@ -601,13 +646,15 @@ public class PersistenceResource implements RESTResource {
|
||||
// This is necessary for values that don't change often otherwise data will start after the start of the graph
|
||||
// (or not at all if there's no change during the graph period)
|
||||
FilterCriteria filter = new FilterCriteria();
|
||||
PersistenceServiceConfiguration config = persistenceServiceConfigurationRegistry.get(serviceId);
|
||||
String alias = config != null ? config.getAliases().get(itemName) : null;
|
||||
filter.setItemName(itemName);
|
||||
filter.setBeginDate(dateTimeBegin);
|
||||
filter.setEndDate(dateTimeEnd);
|
||||
|
||||
ModifiablePersistenceService mService = (ModifiablePersistenceService) service;
|
||||
try {
|
||||
mService.remove(filter);
|
||||
mService.remove(filter, alias);
|
||||
} catch (IllegalArgumentException e) {
|
||||
return JSONResponse.createErrorResponse(Status.BAD_REQUEST, "Invalid filter parameters.");
|
||||
}
|
||||
@@ -620,7 +667,7 @@ public class PersistenceResource implements RESTResource {
|
||||
String effectiveServiceId = serviceId != null ? serviceId : persistenceServiceRegistry.getDefaultId();
|
||||
|
||||
PersistenceService service = persistenceServiceRegistry.get(effectiveServiceId);
|
||||
if (service == null) {
|
||||
if (effectiveServiceId == null || service == null) {
|
||||
logger.warn("Persistence service not found '{}'.", effectiveServiceId);
|
||||
return JSONResponse.createErrorResponse(Status.BAD_REQUEST,
|
||||
"Persistence service not found: " + effectiveServiceId);
|
||||
@@ -658,7 +705,9 @@ public class PersistenceResource implements RESTResource {
|
||||
}
|
||||
|
||||
ModifiablePersistenceService mService = (ModifiablePersistenceService) service;
|
||||
mService.store(item, dateTime, state);
|
||||
PersistenceServiceConfiguration config = persistenceServiceConfigurationRegistry.get(effectiveServiceId);
|
||||
String alias = config != null ? config.getAliases().get(itemName) : null;
|
||||
mService.store(item, dateTime, state, alias);
|
||||
|
||||
persistenceManager.handleExternalPersistenceDataChange(mService, item);
|
||||
|
||||
|
||||
+3
-2
@@ -17,7 +17,7 @@ import static org.hamcrest.CoreMatchers.not;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.collection.IsCollectionWithSize.hasSize;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.*;
|
||||
import static org.mockito.Mockito.*;
|
||||
|
||||
import java.time.ZoneId;
|
||||
@@ -58,6 +58,7 @@ import org.openhab.core.types.UnDefType;
|
||||
* Tests for PersistenceItem Restresource
|
||||
*
|
||||
* @author Stefan Triller - Initial contribution
|
||||
* @author Mark Herwege - Implement aliases
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@@ -112,7 +113,7 @@ public class PersistenceResourceTest {
|
||||
});
|
||||
}
|
||||
|
||||
when(pServiceMock.query(any())).thenReturn(items);
|
||||
when(pServiceMock.query(any(), any())).thenReturn(items);
|
||||
|
||||
when(persistenceServiceRegistryMock.get(PERSISTENCE_SERVICE_ID)).thenReturn(pServiceMock);
|
||||
when(timeZoneProviderMock.getTimeZone()).thenReturn(ZoneId.systemDefault());
|
||||
|
||||
+6
-1
@@ -11,6 +11,7 @@ PersistenceModel:
|
||||
'}'
|
||||
('Filters' '{' filters+=Filter* '}')?
|
||||
('Items' '{' configs+=PersistenceConfiguration* '}')?
|
||||
('Aliases' '{' aliases+=AliasConfiguration* '}')?
|
||||
;
|
||||
|
||||
Strategy:
|
||||
@@ -57,7 +58,7 @@ NotIncludeFilter:
|
||||
|
||||
PersistenceConfiguration:
|
||||
items+=(AllConfig | ItemConfig | GroupConfig | ItemExcludeConfig | GroupExcludeConfig)
|
||||
(',' items+=(AllConfig | ItemConfig | GroupConfig | ItemExcludeConfig | GroupExcludeConfig))* ('->' alias=STRING)?
|
||||
(',' items+=(AllConfig | ItemConfig | GroupConfig | ItemExcludeConfig | GroupExcludeConfig))*
|
||||
((':' ('strategy' '=' strategies+=[Strategy|ID] (',' strategies+=[Strategy|ID])*)?
|
||||
('filter' '=' filters+=[Filter|ID] (',' filters+=[Filter|ID])*)?)
|
||||
| ';')
|
||||
@@ -84,6 +85,10 @@ GroupExcludeConfig:
|
||||
'!' groupExclude=ID '*'
|
||||
;
|
||||
|
||||
AliasConfiguration:
|
||||
item=ID '->' alias=(ID|STRING)
|
||||
;
|
||||
|
||||
DECIMAL returns ecore::EBigDecimal :
|
||||
'-'? INT ('.' INT)?
|
||||
;
|
||||
|
||||
+15
-3
@@ -13,6 +13,7 @@
|
||||
package org.openhab.core.model.persistence.internal;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -24,6 +25,7 @@ import org.openhab.core.common.registry.AbstractProvider;
|
||||
import org.openhab.core.model.core.EventType;
|
||||
import org.openhab.core.model.core.ModelRepository;
|
||||
import org.openhab.core.model.core.ModelRepositoryChangeListener;
|
||||
import org.openhab.core.model.persistence.persistence.AliasConfiguration;
|
||||
import org.openhab.core.model.persistence.persistence.AllConfig;
|
||||
import org.openhab.core.model.persistence.persistence.CronStrategy;
|
||||
import org.openhab.core.model.persistence.persistence.EqualsFilter;
|
||||
@@ -71,6 +73,7 @@ import org.slf4j.LoggerFactory;
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
* @author Markus Rathgeb - Move non-model logic to core.persistence
|
||||
* @author Jan N. Klug - Refactored to {@link PersistenceServiceConfigurationProvider}
|
||||
* @author Mark Herwege - Separate alias handling
|
||||
*/
|
||||
@Component(immediate = true, service = PersistenceServiceConfigurationProvider.class)
|
||||
@NonNullByDefault
|
||||
@@ -112,8 +115,9 @@ public class PersistenceModelManager extends AbstractProvider<PersistenceService
|
||||
|
||||
if (model != null) {
|
||||
PersistenceServiceConfiguration newConfiguration = new PersistenceServiceConfiguration(serviceName,
|
||||
mapConfigs(model.getConfigs()), mapStrategies(model.getDefaults()),
|
||||
mapStrategies(model.getStrategies()), mapFilters(model.getFilters()));
|
||||
mapConfigs(model.getConfigs()), mapAliases(model.getAliases()),
|
||||
mapStrategies(model.getDefaults()), mapStrategies(model.getStrategies()),
|
||||
mapFilters(model.getFilters()));
|
||||
PersistenceServiceConfiguration oldConfiguration = configurations.put(serviceName,
|
||||
newConfiguration);
|
||||
if (oldConfiguration == null) {
|
||||
@@ -167,10 +171,18 @@ public class PersistenceModelManager extends AbstractProvider<PersistenceService
|
||||
items.add(new PersistenceItemExcludeConfig(itemExcludeConfig.getItemExclude()));
|
||||
}
|
||||
}
|
||||
return new PersistenceItemConfiguration(items, config.getAlias(), mapStrategies(config.getStrategies()),
|
||||
return new PersistenceItemConfiguration(items, mapStrategies(config.getStrategies()),
|
||||
mapFilters(config.getFilters()));
|
||||
}
|
||||
|
||||
private Map<String, String> mapAliases(List<AliasConfiguration> aliases) {
|
||||
final Map<String, String> map = new HashMap<>();
|
||||
for (final AliasConfiguration alias : aliases) {
|
||||
map.put(alias.getItem(), alias.getAlias());
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private List<PersistenceStrategy> mapStrategies(List<Strategy> strategies) {
|
||||
final List<PersistenceStrategy> lst = new LinkedList<>();
|
||||
for (final Strategy strategy : strategies) {
|
||||
|
||||
+16
-2
@@ -35,8 +35,8 @@ import org.openhab.core.types.State;
|
||||
* a filter.
|
||||
*
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
* @author Lyubomir Papazov - Deprecate methods using java.util and add methods
|
||||
* that use Java8's ZonedDateTime
|
||||
* @author Lyubomir Papazov - Deprecate methods using java.util and add methods that use Java8's ZonedDateTime
|
||||
* @author Mark Herwege - Copy constructor
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class FilterCriteria {
|
||||
@@ -91,6 +91,20 @@ public class FilterCriteria {
|
||||
/** Filter result to only contain entries that evaluate to true with the given operator and state */
|
||||
private @Nullable State state;
|
||||
|
||||
public FilterCriteria() {
|
||||
}
|
||||
|
||||
public FilterCriteria(FilterCriteria filter) {
|
||||
this.itemName = filter.itemName;
|
||||
this.beginDate = filter.beginDate;
|
||||
this.endDate = filter.endDate;
|
||||
this.pageNumber = filter.pageNumber;
|
||||
this.pageSize = filter.pageSize;
|
||||
this.operator = filter.operator;
|
||||
this.ordering = filter.ordering;
|
||||
this.state = filter.state;
|
||||
}
|
||||
|
||||
public @Nullable String getItemName() {
|
||||
return itemName;
|
||||
}
|
||||
|
||||
+26
@@ -25,6 +25,7 @@ import org.openhab.core.types.State;
|
||||
* and then periodically provide it to the server to be accommodated.
|
||||
*
|
||||
* @author Chris Jackson - Initial contribution
|
||||
* @author Mark Herwege - Implement aliases
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface ModifiablePersistenceService extends QueryablePersistenceService {
|
||||
@@ -70,10 +71,35 @@ public interface ModifiablePersistenceService extends QueryablePersistenceServic
|
||||
* Removes data associated with an item from a persistence service.
|
||||
* If all data is removed for the specified item, the persistence service should free any resources associated with
|
||||
* the item (e.g. remove any tables or delete files from the storage).
|
||||
* If the persistence service implementing this method supports aliases for item names, the default implementation
|
||||
* of {@link #remove(FilterCriteria, String)} should be overriden as well.
|
||||
*
|
||||
* @param filter the filter to apply to the data removal. ItemName can not be null.
|
||||
* @return true if the query executed successfully
|
||||
* @throws IllegalArgumentException if item name is null.
|
||||
*/
|
||||
boolean remove(FilterCriteria filter) throws IllegalArgumentException;
|
||||
|
||||
/**
|
||||
* Removes data associated with an item from a persistence service.
|
||||
* If all data is removed for the specified item, the persistence service should free any resources associated with
|
||||
* the item (e.g. remove any tables or delete files from the storage).
|
||||
* Persistence services supporting aliases, and relying on lookups in the item registry, should override the default
|
||||
* implementation from this interface.
|
||||
*
|
||||
* @param filter the filter to apply to the data removal. ItemName can not be null.
|
||||
* @param alias for item name in database
|
||||
* @return true if the query executed successfully
|
||||
* @throws IllegalArgumentException if item name is null.
|
||||
*/
|
||||
default boolean remove(FilterCriteria filter, @Nullable String alias) throws IllegalArgumentException {
|
||||
// Default implementation changes the filter to have the alias as itemName.
|
||||
// This gives correct results as long as the persistence service does not rely on a lookup in the item registry
|
||||
// (in which case the item will not be found).
|
||||
if (alias != null) {
|
||||
FilterCriteria aliasFilter = new FilterCriteria(filter).setItemName(alias);
|
||||
return remove(aliasFilter);
|
||||
}
|
||||
return remove(filter);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -25,15 +25,15 @@ import org.openhab.core.persistence.strategy.PersistenceStrategy;
|
||||
* This class holds the configuration of a persistence strategy for specific items.
|
||||
*
|
||||
* @author Markus Rathgeb - Initial contribution
|
||||
* @author Mark Herwege - Extract alias configuration
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public record PersistenceItemConfiguration(List<PersistenceConfig> items, @Nullable String alias,
|
||||
List<PersistenceStrategy> strategies, List<PersistenceFilter> filters) {
|
||||
public record PersistenceItemConfiguration(List<PersistenceConfig> items, List<PersistenceStrategy> strategies,
|
||||
List<PersistenceFilter> filters) {
|
||||
|
||||
public PersistenceItemConfiguration(final List<PersistenceConfig> items, @Nullable final String alias,
|
||||
public PersistenceItemConfiguration(final List<PersistenceConfig> items,
|
||||
@Nullable final List<PersistenceStrategy> strategies, @Nullable final List<PersistenceFilter> filters) {
|
||||
this.items = items;
|
||||
this.alias = alias;
|
||||
this.strategies = Objects.requireNonNullElse(strategies, List.of());
|
||||
this.filters = Objects.requireNonNullElse(filters, List.of());
|
||||
}
|
||||
|
||||
+54
-4
@@ -15,6 +15,8 @@ package org.openhab.core.persistence;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
@@ -29,23 +31,68 @@ import org.openhab.core.types.UnDefType;
|
||||
* @author Kai Kreuzer - Initial contribution
|
||||
* @author Chris Jackson - Added getItems method
|
||||
* @author Mark Herwege - Added methods to retrieve lastUpdate, lastChange and lastState from persistence
|
||||
* @author Mark Herwege - Implement aliases
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public interface QueryablePersistenceService extends PersistenceService {
|
||||
|
||||
/**
|
||||
* Queries the {@link PersistenceService} for historic data with a given {@link FilterCriteria}.
|
||||
* If the persistence service implementing this class supports using aliases for item names, the default
|
||||
* implementation of {@link #query(FilterCriteria, String)} should be overridden as well.
|
||||
*
|
||||
* @param filter the filter to apply to the query
|
||||
* @return a time series of items
|
||||
*/
|
||||
Iterable<HistoricItem> query(FilterCriteria filter);
|
||||
|
||||
/**
|
||||
* Queries the {@link PersistenceService} for historic data with a given {@link FilterCriteria}.
|
||||
* If the persistence service implementing this interface supports aliases and relies on item registry lookups, the
|
||||
* default implementation should be overridden to query the database with the aliased name.
|
||||
*
|
||||
* @param filter the filter to apply to the query
|
||||
* @param alias for item name in database
|
||||
* @return a time series of items
|
||||
*/
|
||||
default Iterable<HistoricItem> query(FilterCriteria filter, @Nullable String alias) {
|
||||
// Default implementation changes the filter to have the alias as itemName and sets it back in the returned
|
||||
// result.
|
||||
// This gives correct results as long as the persistence service does not rely on a lookup in the item registry
|
||||
// (in which case the item will not be found).
|
||||
String itemName = filter.getItemName();
|
||||
if (itemName != null && alias != null) {
|
||||
FilterCriteria aliasFilter = new FilterCriteria(filter).setItemName(alias);
|
||||
return StreamSupport.stream(query(aliasFilter).spliterator(), false).map(hi -> new HistoricItem() {
|
||||
|
||||
@Override
|
||||
public ZonedDateTime getTimestamp() {
|
||||
return hi.getTimestamp();
|
||||
}
|
||||
|
||||
@Override
|
||||
public State getState() {
|
||||
return hi.getState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return itemName;
|
||||
}
|
||||
}).collect(Collectors.toList());
|
||||
}
|
||||
return query(filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a set of {@link PersistenceItemInfo} about items that are stored in the persistence service. This allows
|
||||
* the persistence service to return information about items that are no long available as an
|
||||
* {@link org.openhab.core.items.Item} in
|
||||
* openHAB. If it is not possible to retrieve the information an empty set should be returned.
|
||||
* {@link org.openhab.core.items.Item} in openHAB. If it is not possible to retrieve the information an empty set
|
||||
* should be returned.
|
||||
*
|
||||
* Note that implementations for method callers that this method may return an alias for an existing item if the
|
||||
* database does not store the mapping between item name and alias or the reverse mapping is not implemented in the
|
||||
* persistence service.
|
||||
*
|
||||
* @return a set of information about the persisted items
|
||||
*/
|
||||
@@ -58,9 +105,12 @@ public interface QueryablePersistenceService extends PersistenceService {
|
||||
* persisted state. Persistence services can override this default implementation with a more specific or efficient
|
||||
* algorithm.
|
||||
*
|
||||
* @param itemName name of item
|
||||
* @param alias alias of item
|
||||
*
|
||||
* @return a {@link PersistedItem} or null if the item has not been persisted
|
||||
*/
|
||||
default @Nullable PersistedItem persistedItem(String itemName) {
|
||||
default @Nullable PersistedItem persistedItem(String itemName, @Nullable String alias) {
|
||||
State currentState = UnDefType.NULL;
|
||||
State previousState = null;
|
||||
ZonedDateTime lastUpdate = null;
|
||||
@@ -69,7 +119,7 @@ public interface QueryablePersistenceService extends PersistenceService {
|
||||
int pageNumber = 0;
|
||||
FilterCriteria filter = new FilterCriteria().setItemName(itemName).setEndDate(ZonedDateTime.now())
|
||||
.setOrdering(Ordering.DESCENDING).setPageSize(1000).setPageNumber(pageNumber);
|
||||
Iterable<HistoricItem> items = query(filter);
|
||||
Iterable<HistoricItem> items = query(filter, alias);
|
||||
while (items != null) {
|
||||
Iterator<HistoricItem> it = items.iterator();
|
||||
int itemCount = 0;
|
||||
|
||||
-2
@@ -16,7 +16,6 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.eclipse.jdt.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* The {@link org.openhab.core.persistence.dto.PersistenceItemConfigurationDTO} is used for transferring persistence
|
||||
@@ -29,5 +28,4 @@ public class PersistenceItemConfigurationDTO {
|
||||
public Collection<String> items = List.of();
|
||||
public Collection<String> strategies = List.of();
|
||||
public Collection<String> filters = List.of();
|
||||
public @Nullable String alias;
|
||||
}
|
||||
|
||||
+2
@@ -14,6 +14,7 @@ package org.openhab.core.persistence.dto;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
|
||||
@@ -26,6 +27,7 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
public class PersistenceServiceConfigurationDTO {
|
||||
public String serviceId = "";
|
||||
public Collection<PersistenceItemConfigurationDTO> configs = List.of();
|
||||
public Map<String, String> aliases = Map.of();
|
||||
public Collection<String> defaults = List.of();
|
||||
public Collection<PersistenceCronStrategyDTO> cronStrategies = List.of();
|
||||
public Collection<PersistenceFilterDTO> thresholdFilters = List.of();
|
||||
|
||||
+30
-10
@@ -41,6 +41,8 @@ import org.openhab.core.persistence.ModifiablePersistenceService;
|
||||
import org.openhab.core.persistence.PersistenceService;
|
||||
import org.openhab.core.persistence.PersistenceServiceRegistry;
|
||||
import org.openhab.core.persistence.QueryablePersistenceService;
|
||||
import org.openhab.core.persistence.registry.PersistenceServiceConfiguration;
|
||||
import org.openhab.core.persistence.registry.PersistenceServiceConfigurationRegistry;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.types.TimeSeries;
|
||||
import org.openhab.core.types.TypeParser;
|
||||
@@ -73,12 +75,15 @@ import org.slf4j.LoggerFactory;
|
||||
public class PersistenceExtensions {
|
||||
|
||||
private static @Nullable PersistenceServiceRegistry registry;
|
||||
private static @Nullable PersistenceServiceConfigurationRegistry configRegistry;
|
||||
private static @Nullable TimeZoneProvider timeZoneProvider;
|
||||
|
||||
@Activate
|
||||
public PersistenceExtensions(@Reference PersistenceServiceRegistry registry,
|
||||
@Reference PersistenceServiceConfigurationRegistry configRegistry,
|
||||
@Reference TimeZoneProvider timeZoneProvider) {
|
||||
PersistenceExtensions.registry = registry;
|
||||
PersistenceExtensions.configRegistry = configRegistry;
|
||||
PersistenceExtensions.timeZoneProvider = timeZoneProvider;
|
||||
}
|
||||
|
||||
@@ -109,7 +114,7 @@ public class PersistenceExtensions {
|
||||
}
|
||||
PersistenceService service = getService(effectiveServiceId);
|
||||
if (service != null) {
|
||||
service.store(item);
|
||||
service.store(item, getAlias(item, effectiveServiceId));
|
||||
return;
|
||||
}
|
||||
LoggerFactory.getLogger(PersistenceExtensions.class)
|
||||
@@ -148,7 +153,7 @@ public class PersistenceExtensions {
|
||||
}
|
||||
PersistenceService service = getService(effectiveServiceId);
|
||||
if (service instanceof ModifiablePersistenceService modifiableService) {
|
||||
modifiableService.store(item, timestamp, state);
|
||||
modifiableService.store(item, timestamp, state, getAlias(item, effectiveServiceId));
|
||||
return;
|
||||
}
|
||||
LoggerFactory.getLogger(PersistenceExtensions.class)
|
||||
@@ -226,8 +231,9 @@ public class PersistenceExtensions {
|
||||
internalRemoveAllStatesBetween(item, timeSeries.getBegin().atZone(timeZone),
|
||||
timeSeries.getEnd().atZone(timeZone), serviceId);
|
||||
}
|
||||
String alias = getAlias(item, effectiveServiceId);
|
||||
timeSeries.getStates()
|
||||
.forEach(s -> modifiableService.store(item, s.timestamp().atZone(timeZone), s.state()));
|
||||
.forEach(s -> modifiableService.store(item, s.timestamp().atZone(timeZone), s.state(), alias));
|
||||
return;
|
||||
}
|
||||
LoggerFactory.getLogger(PersistenceExtensions.class)
|
||||
@@ -316,10 +322,11 @@ public class PersistenceExtensions {
|
||||
if (service instanceof QueryablePersistenceService qService) {
|
||||
FilterCriteria filter = new FilterCriteria();
|
||||
filter.setEndDate(timestamp);
|
||||
String alias = getAlias(item, effectiveServiceId);
|
||||
filter.setItemName(item.getName());
|
||||
filter.setPageSize(1);
|
||||
filter.setOrdering(Ordering.DESCENDING);
|
||||
Iterable<HistoricItem> result = qService.query(filter);
|
||||
Iterable<HistoricItem> result = qService.query(filter, alias);
|
||||
if (result.iterator().hasNext()) {
|
||||
return result.iterator().next();
|
||||
}
|
||||
@@ -453,6 +460,7 @@ public class PersistenceExtensions {
|
||||
PersistenceService service = getService(effectiveServiceId);
|
||||
if (service instanceof QueryablePersistenceService qService) {
|
||||
FilterCriteria filter = new FilterCriteria();
|
||||
String alias = getAlias(item, effectiveServiceId);
|
||||
filter.setItemName(item.getName());
|
||||
if (forward) {
|
||||
filter.setBeginDate(ZonedDateTime.now());
|
||||
@@ -465,7 +473,7 @@ public class PersistenceExtensions {
|
||||
int startPage = 0;
|
||||
filter.setPageNumber(startPage);
|
||||
|
||||
Iterable<HistoricItem> items = qService.query(filter);
|
||||
Iterable<HistoricItem> items = qService.query(filter, alias);
|
||||
while (items != null) {
|
||||
Iterator<HistoricItem> itemIterator = items.iterator();
|
||||
int itemCount = 0;
|
||||
@@ -496,7 +504,7 @@ public class PersistenceExtensions {
|
||||
}
|
||||
if (itemCount == filter.getPageSize()) {
|
||||
filter.setPageNumber(++startPage);
|
||||
items = qService.query(filter);
|
||||
items = qService.query(filter, alias);
|
||||
} else {
|
||||
items = null;
|
||||
}
|
||||
@@ -625,6 +633,7 @@ public class PersistenceExtensions {
|
||||
PersistenceService service = getService(effectiveServiceId);
|
||||
if (service instanceof QueryablePersistenceService qService) {
|
||||
FilterCriteria filter = new FilterCriteria();
|
||||
String alias = getAlias(item, effectiveServiceId);
|
||||
filter.setItemName(item.getName());
|
||||
if (forward) {
|
||||
filter.setBeginDate(ZonedDateTime.now());
|
||||
@@ -637,7 +646,7 @@ public class PersistenceExtensions {
|
||||
int startPage = 0;
|
||||
filter.setPageNumber(startPage);
|
||||
|
||||
Iterable<HistoricItem> items = qService.query(filter);
|
||||
Iterable<HistoricItem> items = qService.query(filter, alias);
|
||||
while (items != null) {
|
||||
Iterator<HistoricItem> itemIterator = items.iterator();
|
||||
int itemCount = 0;
|
||||
@@ -650,7 +659,7 @@ public class PersistenceExtensions {
|
||||
}
|
||||
if (itemCount == filter.getPageSize()) {
|
||||
filter.setPageNumber(++startPage);
|
||||
items = qService.query(filter);
|
||||
items = qService.query(filter, alias);
|
||||
} else {
|
||||
items = null;
|
||||
}
|
||||
@@ -2547,10 +2556,11 @@ public class PersistenceExtensions {
|
||||
} else {
|
||||
filter.setEndDate(ZonedDateTime.now());
|
||||
}
|
||||
String alias = getAlias(item, effectiveServiceId);
|
||||
filter.setItemName(item.getName());
|
||||
filter.setOrdering(Ordering.ASCENDING);
|
||||
|
||||
return qService.query(filter);
|
||||
return qService.query(filter, alias);
|
||||
} else {
|
||||
LoggerFactory.getLogger(PersistenceExtensions.class)
|
||||
.warn("There is no queryable persistence service registered with the id '{}'", effectiveServiceId);
|
||||
@@ -2663,10 +2673,11 @@ public class PersistenceExtensions {
|
||||
} else {
|
||||
filter.setEndDate(ZonedDateTime.now());
|
||||
}
|
||||
String alias = getAlias(item, effectiveServiceId);
|
||||
filter.setItemName(item.getName());
|
||||
filter.setOrdering(Ordering.ASCENDING);
|
||||
|
||||
mService.remove(filter);
|
||||
mService.remove(filter, alias);
|
||||
} else {
|
||||
LoggerFactory.getLogger(PersistenceExtensions.class)
|
||||
.warn("There is no modifiable persistence service registered with the id '{}'", effectiveServiceId);
|
||||
@@ -2743,6 +2754,15 @@ public class PersistenceExtensions {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static @Nullable String getAlias(Item item, String serviceId) {
|
||||
PersistenceServiceConfigurationRegistry reg = configRegistry;
|
||||
if (reg != null) {
|
||||
PersistenceServiceConfiguration config = reg.get(serviceId);
|
||||
return config != null ? config.getAliases().get(item.getName()) : null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static @Nullable DecimalType getItemValue(Item item) {
|
||||
Item baseItem = item instanceof GroupItem groupItem ? groupItem.getBaseItem() : item;
|
||||
if (baseItem instanceof NumberItem numberItem) {
|
||||
|
||||
+16
-8
@@ -188,7 +188,7 @@ public class PersistenceManagerImpl implements ItemRegistryChangeListener, State
|
||||
.filter(itemConfig -> itemConfig.filters().stream().allMatch(filter -> filter.apply(item)))
|
||||
.forEach(itemConfig -> {
|
||||
itemConfig.filters().forEach(filter -> filter.persisted(item));
|
||||
container.getPersistenceService().store(item, itemConfig.alias());
|
||||
container.getPersistenceService().store(item, container.getAlias(item));
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -353,7 +353,7 @@ public class PersistenceManagerImpl implements ItemRegistryChangeListener, State
|
||||
ZonedDateTime end = timeSeries.getEnd().atZone(ZoneId.systemDefault());
|
||||
FilterCriteria removeFilter = new FilterCriteria().setItemName(item.getName())
|
||||
.setBeginDate(begin).setEndDate(end);
|
||||
service.remove(removeFilter);
|
||||
service.remove(removeFilter, container.getAlias(item));
|
||||
ScheduledCompletableFuture<?> forecastJob = container.forecastJobs.get(item.getName());
|
||||
if (forecastJob != null && forecastJob.getScheduledTime().isAfter(begin)
|
||||
&& forecastJob.getScheduledTime().isBefore(end)) {
|
||||
@@ -478,12 +478,17 @@ public class PersistenceManagerImpl implements ItemRegistryChangeListener, State
|
||||
}).stream());
|
||||
}
|
||||
|
||||
public @Nullable String getAlias(Item item) {
|
||||
return configuration.getAliases().get(item.getName());
|
||||
}
|
||||
|
||||
private PersistenceServiceConfiguration getDefaultConfig() {
|
||||
List<PersistenceStrategy> strategies = persistenceService.getDefaultStrategies();
|
||||
List<PersistenceItemConfiguration> configs = List
|
||||
.of(new PersistenceItemConfiguration(List.of(new PersistenceAllConfig()), null, strategies, null));
|
||||
return new PersistenceServiceConfiguration(persistenceService.getId(), configs, strategies, strategies,
|
||||
List.of());
|
||||
.of(new PersistenceItemConfiguration(List.of(new PersistenceAllConfig()), strategies, null));
|
||||
Map<String, String> aliases = Map.of();
|
||||
return new PersistenceServiceConfiguration(persistenceService.getId(), configs, aliases, strategies,
|
||||
strategies, List.of());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -550,6 +555,7 @@ public class PersistenceManagerImpl implements ItemRegistryChangeListener, State
|
||||
|
||||
private void restoreItemStateIfPossible(Item item) {
|
||||
QueryablePersistenceService queryService = (QueryablePersistenceService) persistenceService;
|
||||
String alias = getAlias(item);
|
||||
|
||||
PersistedItem persistedItem = safeCaller.create(queryService, QueryablePersistenceService.class)
|
||||
.onTimeout(
|
||||
@@ -558,8 +564,9 @@ public class PersistenceManagerImpl implements ItemRegistryChangeListener, State
|
||||
.onException(e -> logger.error(
|
||||
"Exception occurred while querying persistence service '{}' to restore '{}': {}",
|
||||
queryService.getId(), item.getName(), e.getMessage(), e))
|
||||
.build().persistedItem(item.getName());
|
||||
.build().persistedItem(item.getName(), alias);
|
||||
if (persistedItem == null) {
|
||||
// in case of an exception or timeout, the safe caller returns null
|
||||
return;
|
||||
}
|
||||
GenericItem genericItem = (GenericItem) item;
|
||||
@@ -590,6 +597,7 @@ public class PersistenceManagerImpl implements ItemRegistryChangeListener, State
|
||||
public void scheduleNextPersistedForecastForItem(String itemName) {
|
||||
Item item = itemRegistry.get(itemName);
|
||||
if (item instanceof GenericItem) {
|
||||
String alias = getAlias(item);
|
||||
QueryablePersistenceService queryService = (QueryablePersistenceService) persistenceService;
|
||||
FilterCriteria filter = new FilterCriteria().setItemName(itemName).setBeginDate(ZonedDateTime.now())
|
||||
.setOrdering(ASCENDING);
|
||||
@@ -598,7 +606,7 @@ public class PersistenceManagerImpl implements ItemRegistryChangeListener, State
|
||||
queryService.getId(), SafeCaller.DEFAULT_TIMEOUT))
|
||||
.onException(e -> logger.error("Exception occurred while querying persistence service '{}': {}",
|
||||
queryService.getId(), e.getMessage(), e))
|
||||
.build().query(filter).iterator();
|
||||
.build().query(filter, alias).iterator();
|
||||
while (result.hasNext()) {
|
||||
HistoricItem next = result.next();
|
||||
Instant timestamp = next.getInstant();
|
||||
@@ -624,7 +632,7 @@ public class PersistenceManagerImpl implements ItemRegistryChangeListener, State
|
||||
if (itemConfig.filters().stream().allMatch(filter -> filter.apply(item))) {
|
||||
long startTime = System.nanoTime();
|
||||
itemConfig.filters().forEach(filter -> filter.persisted(item));
|
||||
persistenceService.store(item, itemConfig.alias());
|
||||
persistenceService.store(item, getAlias(item));
|
||||
logger.trace("Storing item '{}' with persistence service '{}' took {}ms", item.getName(),
|
||||
configuration.getUID(), TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime));
|
||||
}
|
||||
|
||||
+15
-2
@@ -14,6 +14,7 @@ package org.openhab.core.persistence.registry;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.eclipse.jdt.annotation.NonNullByDefault;
|
||||
import org.openhab.core.common.registry.Identifiable;
|
||||
@@ -25,20 +26,23 @@ import org.openhab.core.persistence.strategy.PersistenceStrategy;
|
||||
* The {@link PersistenceServiceConfiguration} represents the configuration for a persistence service.
|
||||
*
|
||||
* @author Jan N. Klug - Initial contribution
|
||||
* @author Mark Herwege - Implement aliases
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PersistenceServiceConfiguration implements Identifiable<String> {
|
||||
private final String serviceId;
|
||||
private final List<PersistenceItemConfiguration> configs;
|
||||
private final Map<String, String> aliases;
|
||||
private final List<PersistenceStrategy> defaults;
|
||||
private final List<PersistenceStrategy> strategies;
|
||||
private final List<PersistenceFilter> filters;
|
||||
|
||||
public PersistenceServiceConfiguration(String serviceId, Collection<PersistenceItemConfiguration> configs,
|
||||
Collection<PersistenceStrategy> defaults, Collection<PersistenceStrategy> strategies,
|
||||
Collection<PersistenceFilter> filters) {
|
||||
Map<String, String> aliases, Collection<PersistenceStrategy> defaults,
|
||||
Collection<PersistenceStrategy> strategies, Collection<PersistenceFilter> filters) {
|
||||
this.serviceId = serviceId;
|
||||
this.configs = List.copyOf(configs);
|
||||
this.aliases = Map.copyOf(aliases);
|
||||
this.defaults = List.copyOf(defaults);
|
||||
this.strategies = List.copyOf(strategies);
|
||||
this.filters = List.copyOf(filters);
|
||||
@@ -58,6 +62,15 @@ public class PersistenceServiceConfiguration implements Identifiable<String> {
|
||||
return configs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the item aliases.
|
||||
*
|
||||
* @return a map of items to aliases
|
||||
*/
|
||||
public Map<String, String> getAliases() {
|
||||
return aliases;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default strategies.
|
||||
*
|
||||
|
||||
+6
-3
@@ -46,6 +46,7 @@ import org.openhab.core.persistence.strategy.PersistenceStrategy;
|
||||
* The {@link PersistenceServiceConfigurationDTOMapper} is a utility class to map persistence configurations for storage
|
||||
*
|
||||
* @author Jan N. Klug - Initial contribution
|
||||
* @author Mark Herwege - Implement aliases
|
||||
*/
|
||||
@NonNullByDefault
|
||||
public class PersistenceServiceConfigurationDTOMapper {
|
||||
@@ -60,6 +61,7 @@ public class PersistenceServiceConfigurationDTOMapper {
|
||||
dto.serviceId = persistenceServiceConfiguration.getUID();
|
||||
dto.configs = persistenceServiceConfiguration.getConfigs().stream()
|
||||
.map(PersistenceServiceConfigurationDTOMapper::mapPersistenceItemConfig).toList();
|
||||
dto.aliases = Map.copyOf(persistenceServiceConfiguration.getAliases());
|
||||
dto.defaults = persistenceServiceConfiguration.getDefaults().stream().map(PersistenceStrategy::getName)
|
||||
.toList();
|
||||
dto.cronStrategies = filterList(persistenceServiceConfiguration.getStrategies(), PersistenceCronStrategy.class,
|
||||
@@ -100,10 +102,12 @@ public class PersistenceServiceConfigurationDTOMapper {
|
||||
.map(str -> stringToPersistenceStrategy(str, strategyMap, dto.serviceId)).toList();
|
||||
List<PersistenceFilter> filters = config.filters.stream()
|
||||
.map(str -> stringToPersistenceFilter(str, filterMap, dto.serviceId)).toList();
|
||||
return new PersistenceItemConfiguration(items, config.alias, strategies, filters);
|
||||
return new PersistenceItemConfiguration(items, strategies, filters);
|
||||
}).toList();
|
||||
|
||||
return new PersistenceServiceConfiguration(dto.serviceId, configs, defaults, strategyMap.values(),
|
||||
Map<String, String> aliases = Map.copyOf(dto.aliases);
|
||||
|
||||
return new PersistenceServiceConfiguration(dto.serviceId, configs, aliases, defaults, strategyMap.values(),
|
||||
filterMap.values());
|
||||
}
|
||||
|
||||
@@ -171,7 +175,6 @@ public class PersistenceServiceConfigurationDTOMapper {
|
||||
.toList();
|
||||
itemDto.strategies = config.strategies().stream().map(PersistenceStrategy::getName).toList();
|
||||
itemDto.filters = config.filters().stream().map(PersistenceFilter::getName).toList();
|
||||
itemDto.alias = config.alias();
|
||||
return itemDto;
|
||||
}
|
||||
|
||||
|
||||
+8
-2
@@ -16,6 +16,7 @@ import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.closeTo;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.openhab.core.persistence.extensions.TestPersistenceService.*;
|
||||
|
||||
@@ -52,6 +53,7 @@ import org.openhab.core.library.unit.Units;
|
||||
import org.openhab.core.persistence.HistoricItem;
|
||||
import org.openhab.core.persistence.PersistenceService;
|
||||
import org.openhab.core.persistence.PersistenceServiceRegistry;
|
||||
import org.openhab.core.persistence.registry.PersistenceServiceConfigurationRegistry;
|
||||
import org.openhab.core.types.State;
|
||||
|
||||
/**
|
||||
@@ -64,6 +66,7 @@ import org.openhab.core.types.State;
|
||||
* @author Mark Herwege - lastChange and nextChange methods
|
||||
* @author Mark Herwege - handle persisted GroupItem with QuantityType
|
||||
* @author Mark Herwege - add median methods
|
||||
* @author Mark Herwege - Implement aliases
|
||||
*/
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@MockitoSettings(strictness = Strictness.LENIENT)
|
||||
@@ -81,6 +84,8 @@ public class PersistenceExtensionsTest {
|
||||
private @Mock @NonNullByDefault({}) UnitProvider unitProviderMock;
|
||||
private @Mock @NonNullByDefault({}) TimeZoneProvider timeZoneProviderMock;
|
||||
|
||||
private @Mock @NonNullByDefault({}) PersistenceServiceConfigurationRegistry persistenceServiceConfigurationRegistryMock;
|
||||
|
||||
private @NonNullByDefault({}) GenericItem numberItem, quantityItem, groupQuantityItem, switchItem;
|
||||
|
||||
@BeforeEach
|
||||
@@ -109,6 +114,7 @@ public class PersistenceExtensionsTest {
|
||||
when(itemRegistryMock.get(TEST_SWITCH)).thenReturn(switchItem);
|
||||
when(itemRegistryMock.get(TEST_GROUP_QUANTITY_NUMBER)).thenReturn(groupQuantityItem);
|
||||
|
||||
when(persistenceServiceConfigurationRegistryMock.get(anyString())).thenReturn(null);
|
||||
when(timeZoneProviderMock.getTimeZone()).thenReturn(ZoneId.systemDefault());
|
||||
|
||||
new PersistenceExtensions(new PersistenceServiceRegistry() {
|
||||
@@ -135,7 +141,7 @@ public class PersistenceExtensionsTest {
|
||||
public @Nullable PersistenceService get(@Nullable String serviceId) {
|
||||
return TestPersistenceService.SERVICE_ID.equals(serviceId) ? testPersistenceService : null;
|
||||
}
|
||||
}, timeZoneProviderMock);
|
||||
}, persistenceServiceConfigurationRegistryMock, timeZoneProviderMock);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -3397,7 +3403,7 @@ public class PersistenceExtensionsTest {
|
||||
public @Nullable PersistenceService get(@Nullable String serviceId) {
|
||||
return TestCachedValuesPersistenceService.ID.equals(serviceId) ? persistenceService : null;
|
||||
}
|
||||
}, timeZoneProviderMock);
|
||||
}, persistenceServiceConfigurationRegistryMock, timeZoneProviderMock);
|
||||
|
||||
if (historicHours > 0) {
|
||||
ZonedDateTime beginHistory = now.minusHours(historicHours);
|
||||
|
||||
+7
-6
@@ -86,6 +86,7 @@ import org.openhab.core.types.UnDefType;
|
||||
* The {@link PersistenceManagerTest} contains tests for the {@link PersistenceManagerImpl}
|
||||
*
|
||||
* @author Jan N. Klug - Initial contribution
|
||||
* @author Mark Herwege - Implement aliases
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
@@ -185,8 +186,8 @@ public class PersistenceManagerTest {
|
||||
when(itemRegistryMock.getItems()).thenReturn(List.of(TEST_ITEM, TEST_ITEM2, TEST_ITEM3, TEST_GROUP_ITEM));
|
||||
when(persistenceServiceMock.getId()).thenReturn(TEST_PERSISTENCE_SERVICE_ID);
|
||||
when(queryablePersistenceServiceMock.getId()).thenReturn(TEST_QUERYABLE_PERSISTENCE_SERVICE_ID);
|
||||
when(queryablePersistenceServiceMock.query(any())).thenReturn(List.of(TEST_HISTORIC_ITEM));
|
||||
when(queryablePersistenceServiceMock.persistedItem(any())).thenReturn(TEST_PERSISTED_ITEM);
|
||||
when(queryablePersistenceServiceMock.query(any(), any())).thenReturn(List.of(TEST_HISTORIC_ITEM));
|
||||
when(queryablePersistenceServiceMock.persistedItem(any(), any())).thenReturn(TEST_PERSISTED_ITEM);
|
||||
when(modifiablePersistenceServiceMock.getId()).thenReturn(TEST_MODIFIABLE_PERSISTENCE_SERVICE_ID);
|
||||
|
||||
manager = new PersistenceManagerImpl(cronSchedulerMock, schedulerMock, itemRegistryMock, safeCallerMock,
|
||||
@@ -367,7 +368,7 @@ public class PersistenceManagerTest {
|
||||
assertThat(TEST_GROUP_ITEM.getState(), is(TEST_STATE));
|
||||
assertThat(TEST_GROUP_ITEM.getLastState(), is(TEST_LAST_STATE));
|
||||
|
||||
verify(queryablePersistenceServiceMock, times(3)).persistedItem(any());
|
||||
verify(queryablePersistenceServiceMock, times(3)).persistedItem(any(), any());
|
||||
|
||||
ZonedDateTime lastStateUpdate = TEST_ITEM.getLastStateUpdate();
|
||||
assertNotNull(lastStateUpdate);
|
||||
@@ -398,7 +399,7 @@ public class PersistenceManagerTest {
|
||||
assertThat(TEST_GROUP_ITEM.getState(), is(TEST_STATE));
|
||||
assertThat(TEST_GROUP_ITEM.getLastState(), is(TEST_LAST_STATE));
|
||||
|
||||
verify(queryablePersistenceServiceMock, times(2)).persistedItem(any());
|
||||
verify(queryablePersistenceServiceMock, times(2)).persistedItem(any(), any());
|
||||
|
||||
ZonedDateTime lastStateUpdate = TEST_ITEM.getLastStateUpdate();
|
||||
assertNotNull(lastStateUpdate);
|
||||
@@ -578,7 +579,7 @@ public class PersistenceManagerTest {
|
||||
PersistenceStrategy strategy, @Nullable PersistenceFilter filter) {
|
||||
List<PersistenceFilter> filters = filter != null ? List.of(filter) : List.of();
|
||||
|
||||
PersistenceItemConfiguration itemConfiguration = new PersistenceItemConfiguration(itemConfigs, null,
|
||||
PersistenceItemConfiguration itemConfiguration = new PersistenceItemConfiguration(itemConfigs,
|
||||
List.of(strategy), filters);
|
||||
|
||||
List<PersistenceStrategy> strategies = PersistenceStrategy.Globals.STRATEGIES.containsValue(strategy)
|
||||
@@ -586,7 +587,7 @@ public class PersistenceManagerTest {
|
||||
: List.of(strategy);
|
||||
|
||||
PersistenceServiceConfiguration serviceConfiguration = new PersistenceServiceConfiguration(serviceId,
|
||||
List.of(itemConfiguration), List.of(), strategies, filters);
|
||||
List.of(itemConfiguration), Map.of(), List.of(), strategies, filters);
|
||||
manager.added(serviceConfiguration);
|
||||
|
||||
return serviceConfiguration;
|
||||
|
||||
+15
-6
@@ -49,6 +49,8 @@ import org.openhab.core.persistence.HistoricItem;
|
||||
import org.openhab.core.persistence.PersistenceService;
|
||||
import org.openhab.core.persistence.PersistenceServiceRegistry;
|
||||
import org.openhab.core.persistence.QueryablePersistenceService;
|
||||
import org.openhab.core.persistence.registry.PersistenceServiceConfiguration;
|
||||
import org.openhab.core.persistence.registry.PersistenceServiceConfigurationRegistry;
|
||||
import org.openhab.core.types.State;
|
||||
import org.openhab.core.ui.chart.ChartProvider;
|
||||
import org.openhab.core.ui.internal.chart.ChartServlet;
|
||||
@@ -68,6 +70,7 @@ import org.slf4j.LoggerFactory;
|
||||
* @author Holger Reichert - Support for themes, DPI, legend hiding
|
||||
* @author Christoph Weitkamp - Consider default persistence service
|
||||
* @author Jan N. Klug - Add y-axis label formatter
|
||||
* @author Mark Herwege - Implement aliases
|
||||
*/
|
||||
@NonNullByDefault
|
||||
@Component(immediate = true)
|
||||
@@ -110,12 +113,15 @@ public class DefaultChartProvider implements ChartProvider {
|
||||
|
||||
private final ItemUIRegistry itemUIRegistry;
|
||||
private final PersistenceServiceRegistry persistenceServiceRegistry;
|
||||
private final PersistenceServiceConfigurationRegistry persistenceServiceConfigurationRegistry;
|
||||
|
||||
@Activate
|
||||
public DefaultChartProvider(final @Reference ItemUIRegistry itemUIRegistry,
|
||||
final @Reference PersistenceServiceRegistry persistenceServiceRegistry) {
|
||||
final @Reference PersistenceServiceRegistry persistenceServiceRegistry,
|
||||
final @Reference PersistenceServiceConfigurationRegistry persistenceServiceConfigurationRegistry) {
|
||||
this.itemUIRegistry = itemUIRegistry;
|
||||
this.persistenceServiceRegistry = persistenceServiceRegistry;
|
||||
this.persistenceServiceConfigurationRegistry = persistenceServiceConfigurationRegistry;
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Available themes for default chart provider: {}", String.join(", ", CHART_THEMES.keySet()));
|
||||
@@ -188,8 +194,8 @@ public class DefaultChartProvider implements ChartProvider {
|
||||
// axis
|
||||
styler.setAxisTickLabelsFont(chartTheme.getAxisTickLabelsFont(dpi));
|
||||
styler.setAxisTickLabelsColor(chartTheme.getAxisTickLabelsColor());
|
||||
styler.setXAxisMin((double) startTime.toInstant().toEpochMilli());
|
||||
styler.setXAxisMax((double) endTime.toInstant().toEpochMilli());
|
||||
styler.setXAxisMin(startTime.toInstant().toEpochMilli());
|
||||
styler.setXAxisMax(endTime.toInstant().toEpochMilli());
|
||||
int yAxisSpacing = Math.max(height / 10, chartTheme.getAxisTickLabelsFont(dpi).getSize());
|
||||
if (yAxisDecimalPattern != null) {
|
||||
styler.setYAxisDecimalPattern(yAxisDecimalPattern);
|
||||
@@ -333,10 +339,13 @@ public class DefaultChartProvider implements ChartProvider {
|
||||
// after the start of the graph (or not at all if there's no change during the graph period)
|
||||
filter = new FilterCriteria();
|
||||
filter.setEndDate(timeBegin);
|
||||
filter.setItemName(item.getName());
|
||||
String itemName = item.getName();
|
||||
PersistenceServiceConfiguration config = persistenceServiceConfigurationRegistry.get(service.getId());
|
||||
String alias = config != null ? config.getAliases().get(itemName) : null;
|
||||
filter.setItemName(itemName);
|
||||
filter.setPageSize(1);
|
||||
filter.setOrdering(Ordering.DESCENDING);
|
||||
result = service.query(filter);
|
||||
result = service.query(filter, alias);
|
||||
if (result.iterator().hasNext()) {
|
||||
HistoricItem historicItem = result.iterator().next();
|
||||
|
||||
@@ -352,7 +361,7 @@ public class DefaultChartProvider implements ChartProvider {
|
||||
filter.setOrdering(Ordering.ASCENDING);
|
||||
|
||||
// Get the data from the persistence store
|
||||
result = service.query(filter);
|
||||
result = service.query(filter, alias);
|
||||
|
||||
// Iterate through the data
|
||||
for (HistoricItem historicItem : result) {
|
||||
|
||||
Reference in New Issue
Block a user