Skip to content

Commit

Permalink
persistence aliases
Browse files Browse the repository at this point in the history
Signed-off-by: Mark Herwege <mark.herwege@telenet.be>
  • Loading branch information
mherwege committed Aug 27, 2024
1 parent 8d54cce commit bc6d66a
Show file tree
Hide file tree
Showing 16 changed files with 185 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;

import javax.annotation.security.RolesAllowed;
import javax.ws.rs.Consumes;
Expand Down Expand Up @@ -179,9 +180,10 @@ public Response httpGetPersistenceServiceConfiguration(@Context HttpHeaders head
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;
}
Expand Down Expand Up @@ -363,6 +365,9 @@ private Response getItemHistoryDTO(@Nullable String serviceId, String itemName,
// 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) {
Expand Down Expand Up @@ -411,6 +416,8 @@ private Response getItemHistoryDTO(@Nullable String serviceId, String itemName,

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
Expand All @@ -422,7 +429,7 @@ private Response getItemHistoryDTO(@Nullable String serviceId, String itemName,
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++;
Expand All @@ -441,7 +448,7 @@ private Response getItemHistoryDTO(@Nullable String serviceId, String itemName,
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
Expand Down Expand Up @@ -472,7 +479,7 @@ private Response getItemHistoryDTO(@Nullable String serviceId, String itemName,
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++;
Expand Down Expand Up @@ -570,13 +577,15 @@ private Response deletePersistenceItemData(@Nullable String serviceId, String it
// 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.");
}
Expand All @@ -589,7 +598,7 @@ private Response putItemState(@Nullable String serviceId, String itemName, Strin
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);
Expand Down Expand Up @@ -627,7 +636,9 @@ private Response putItemState(@Nullable String serviceId, String itemName, Strin
}

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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
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;
Expand Down Expand Up @@ -108,7 +108,7 @@ public String getName() {
});
}

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());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ PersistenceModel:
'}'
('Filters' '{' filters+=Filter* '}')?
('Items' '{' configs+=PersistenceConfiguration* '}')?
('Aliases' '{' aliases+=AliasConfiguration* '}')?
;

Strategy:
Expand Down Expand Up @@ -56,7 +57,7 @@ NotIncludeFilter:


PersistenceConfiguration:
items+=(AllConfig | ItemConfig | GroupConfig) (',' items+=(AllConfig | ItemConfig | GroupConfig))* ('->' alias=STRING)?
items+=(AllConfig | ItemConfig | GroupConfig) (',' items+=(AllConfig | ItemConfig | GroupConfig))*
((':' ('strategy' '=' strategies+=[Strategy|ID] (',' strategies+=[Strategy|ID])*)?
('filter' '=' filters+=[Filter|ID] (',' filters+=[Filter|ID])*)?)
| ';')
Expand All @@ -75,6 +76,11 @@ GroupConfig:
group=ID '*'
;


AliasConfiguration:
item=ID '->' alias=STRING
;

DECIMAL returns ecore::EBigDecimal :
INT ('.' INT)?
;
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -24,6 +25,7 @@
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;
Expand Down Expand Up @@ -67,6 +69,7 @@
* @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
Expand Down Expand Up @@ -98,14 +101,17 @@ public void modelChanged(String modelName, EventType type) {
String serviceName = serviceName(modelName);
if (type == EventType.REMOVED) {
PersistenceServiceConfiguration removed = configurations.remove(serviceName);
notifyListenersAboutRemovedElement(removed);
if (removed != null) {
notifyListenersAboutRemovedElement(removed);
}
} else {
final PersistenceModel model = (PersistenceModel) modelRepository.getModel(modelName);

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) {
Expand Down Expand Up @@ -155,10 +161,18 @@ private PersistenceItemConfiguration mapConfig(PersistenceConfiguration config)
items.add(new PersistenceItemConfig(itemConfig.getItem()));
}
}
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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,29 @@ 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 should override the default implementation from this interface that
* ignores aliases when querying.
*
* @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 ignores alias
return remove(filter);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,15 @@
* 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());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import java.util.Set;

import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;

/**
* A queryable persistence service which can be used to store and retrieve
Expand All @@ -28,17 +29,36 @@ 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 overriden 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, the default implementation should be
* overriden 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 ignores alias
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 of this method may return an alias for an existing item if the database does not store
* the mapping between item name and alias.
*
* @return a set of information about the persisted items
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
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
Expand All @@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.eclipse.jdt.annotation.NonNullByDefault;

Expand All @@ -26,6 +27,7 @@
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();
Expand Down
Loading

0 comments on commit bc6d66a

Please sign in to comment.