diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/dto/usage/IntervalBlockDto.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/dto/usage/IntervalBlockDto.java
index bc0d657c..a608a6f2 100644
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/dto/usage/IntervalBlockDto.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/dto/usage/IntervalBlockDto.java
@@ -20,54 +20,36 @@
package org.greenbuttonalliance.espi.common.dto.usage;
import jakarta.xml.bind.annotation.*;
-import java.time.OffsetDateTime;
import java.util.List;
/**
* IntervalBlock DTO record for JAXB XML marshalling/unmarshalling.
- *
+ *
* Represents a time sequence of readings of the same ReadingType.
* Contains a date/time interval and a collection of interval readings.
* Supports Atom protocol XML wrapping.
+ *
+ * Field order strictly matches espi.xsd IntervalBlock element sequence.
+ *
+ * @see NAESB ESPI 4.0
*/
@XmlRootElement(name = "IntervalBlock", namespace = "http://naesb.org/espi")
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "IntervalBlock", namespace = "http://naesb.org/espi", propOrder = {
- "published", "updated", "relatedLinks", "selfLink", "upLink", "description", "interval", "intervalReadings"
+ "interval", "intervalReadings"
})
public record IntervalBlockDto(
-
+
@XmlTransient
Long id,
@XmlTransient
- //@XmlAttribute(name = "mRID")
String uuid,
-
- @XmlElement(name = "published")
- OffsetDateTime published,
-
- @XmlElement(name = "updated")
- OffsetDateTime updated,
-
- @XmlElement(name = "link")
- @XmlElementWrapper(name = "relatedLinks")
- List relatedLinks,
-
- @XmlElement(name = "selfLink")
- String selfLink,
-
- @XmlElement(name = "upLink")
- String upLink,
-
- @XmlElement(name = "description")
- String description,
-
+
@XmlElement(name = "interval")
DateTimeIntervalDto interval,
-
+
@XmlElement(name = "IntervalReading")
- // @XmlElementWrapper(name = "IntervalReadings")
List intervalReadings
) {
@@ -75,21 +57,21 @@ public record IntervalBlockDto(
* Default constructor for JAXB.
*/
public IntervalBlockDto() {
- this(null, null, null, null, null, null, null, null, null, null);
+ this(null, null, null, null);
}
-
+
/**
* Minimal constructor for basic interval block data.
*/
public IntervalBlockDto(String uuid, DateTimeIntervalDto interval) {
- this(null, uuid, null, null, null, null, null, null, interval, null);
+ this(null, uuid, interval, null);
}
-
+
/**
* Constructor with interval and readings.
*/
public IntervalBlockDto(String uuid, DateTimeIntervalDto interval, List intervalReadings) {
- this(null, uuid, null, null, null, null, null, null, interval, intervalReadings);
+ this(null, uuid, interval, intervalReadings);
}
/**
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/usage/IntervalBlockMapper.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/usage/IntervalBlockMapper.java
index 00fd21e5..1cd4101d 100644
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/usage/IntervalBlockMapper.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/mapper/usage/IntervalBlockMapper.java
@@ -26,7 +26,6 @@
import org.greenbuttonalliance.espi.common.mapper.DateTimeMapper;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
-import org.mapstruct.MappingTarget;
/**
* MapStruct mapper for converting between IntervalBlockEntity and IntervalBlockDto.
@@ -45,18 +44,12 @@ public interface IntervalBlockMapper {
/**
* Converts an IntervalBlockEntity to an IntervalBlockDto.
* Maps all related entities to their corresponding DTOs.
- *
+ *
* @param entity the interval block entity
* @return the interval block DTO
*/
- @Mapping(target = "id", ignore = true) // DTO id field not used
+ @Mapping(target = "id", ignore = true) // DTO id field not used (legacy field)
@Mapping(target = "uuid", source = "id", qualifiedByName = "uuidToString")
- @Mapping(target = "published", source = "published", qualifiedByName = "localToOffset")
- @Mapping(target = "updated", source = "updated", qualifiedByName = "localToOffset")
- @Mapping(target = "relatedLinks", ignore = true) // Links handled separately
- @Mapping(target = "selfLink", ignore = true)
- @Mapping(target = "upLink", ignore = true)
- @Mapping(target = "description", source = "description")
@Mapping(target = "interval", source = "interval")
@Mapping(target = "intervalReadings", source = "intervalReadings")
IntervalBlockDto toDto(IntervalBlockEntity entity);
@@ -64,37 +57,20 @@ public interface IntervalBlockMapper {
/**
* Converts an IntervalBlockDto to an IntervalBlockEntity.
* Maps all related DTOs to their corresponding entities.
- *
+ *
* @param dto the interval block DTO
* @return the interval block entity
*/
@Mapping(target = "id", source = "uuid", qualifiedByName = "stringToUuid")
@Mapping(target = "created", ignore = true)
@Mapping(target = "updated", ignore = true)
- @Mapping(target = "published", source = "published", qualifiedByName = "offsetToLocal")
+ @Mapping(target = "published", ignore = true)
@Mapping(target = "upLink", ignore = true)
@Mapping(target = "selfLink", ignore = true)
@Mapping(target = "relatedLinks", ignore = true)
- @Mapping(target = "description", source = "description")
+ @Mapping(target = "description", ignore = true)
@Mapping(target = "interval", source = "interval")
@Mapping(target = "intervalReadings", source = "intervalReadings")
@Mapping(target = "meterReading", ignore = true) // Relationships handled separately
IntervalBlockEntity toEntity(IntervalBlockDto dto);
-
- /**
- * Updates an existing IntervalBlockEntity with data from an IntervalBlockDto.
- * Useful for merge operations where the entity ID should be preserved.
- *
- * @param dto the source DTO
- * @param entity the target entity to update
- */
- @Mapping(target = "id", ignore = true)
- @Mapping(target = "created", ignore = true)
- @Mapping(target = "updated", ignore = true)
- @Mapping(target = "published", source = "published", qualifiedByName = "offsetToLocal")
- @Mapping(target = "upLink", ignore = true)
- @Mapping(target = "selfLink", ignore = true)
- @Mapping(target = "relatedLinks", ignore = true)
- @Mapping(target = "meterReading", ignore = true) // Relationships handled separately
- void updateEntity(IntervalBlockDto dto, @MappingTarget IntervalBlockEntity entity);
}
\ No newline at end of file
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/IntervalBlockRepository.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/IntervalBlockRepository.java
index acaef220..cb7f24e9 100755
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/IntervalBlockRepository.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/repositories/usage/IntervalBlockRepository.java
@@ -21,54 +21,23 @@
import org.greenbuttonalliance.espi.common.domain.usage.IntervalBlockEntity;
import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
-import org.springframework.transaction.annotation.Transactional;
import java.util.List;
-import java.util.Optional;
import java.util.UUID;
@Repository
public interface IntervalBlockRepository extends JpaRepository {
- // JpaRepository provides: save(), findById(), findAll(), deleteById(), etc.
-
- // findById is already provided by JpaRepository
- // Optional findById(UUID id) is inherited
-
@Query("SELECT i.id FROM IntervalBlockEntity i")
List findAllIds();
- @Modifying
- @Transactional
- @Query("DELETE FROM IntervalBlockEntity i WHERE i.id = :id")
- void deleteById(@Param("id") UUID id);
-
- @Modifying
- @Transactional
- @Query("DELETE FROM IntervalBlockEntity i WHERE i.id = :uuid")
- void deleteByUuid(@Param("uuid") UUID uuid);
-
- // Custom method for createOrReplaceByUUID - should be implemented in service layer
-
@Query("SELECT i FROM IntervalBlockEntity i WHERE i.meterReading.id = :meterReadingId")
List findAllByMeterReadingId(@Param("meterReadingId") UUID meterReadingId);
@Query("SELECT i.id FROM IntervalBlockEntity i WHERE i.meterReading.usagePoint.id = :usagePointId")
List findAllIdsByUsagePointId(@Param("usagePointId") UUID usagePointId);
- @Query("SELECT DISTINCT i.id FROM UsagePointEntity u, MeterReadingEntity m, IntervalBlockEntity i WHERE u.retailCustomer.id = :o1Id AND m.usagePoint.id = :o2Id AND i.meterReading.id = :o3Id")
- List findAllIdsByXpath3(@Param("o1Id") UUID o1Id, @Param("o2Id") UUID o2Id, @Param("o3Id") UUID o3Id);
-
- @Query("SELECT DISTINCT i.id FROM UsagePointEntity u, MeterReadingEntity m, IntervalBlockEntity i WHERE u.retailCustomer.id = :o1Id AND m.usagePoint.id = :o2Id AND i.meterReading.id = :o3Id AND i.id = :o4Id")
- Optional findIdByXpath(@Param("o1Id") UUID o1Id, @Param("o2Id") UUID o2Id, @Param("o3Id") UUID o3Id, @Param("o4Id") UUID o4Id);
-
- @Query("SELECT i FROM IntervalBlockEntity i WHERE i.meterReading = :meterReading")
- List findByMeterReadingEntity(@Param("meterReading") org.greenbuttonalliance.espi.common.domain.usage.MeterReadingEntity meterReading);
-
- @Query("SELECT i FROM IntervalBlockEntity i WHERE i.selfLink.href = :uri")
- Optional findByUri(@Param("uri") String uri);
}
diff --git a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/IntervalBlockServiceImpl.java b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/IntervalBlockServiceImpl.java
index 2bce317a..3725dbba 100755
--- a/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/IntervalBlockServiceImpl.java
+++ b/openespi-common/src/main/java/org/greenbuttonalliance/espi/common/service/impl/IntervalBlockServiceImpl.java
@@ -88,14 +88,15 @@ public void delete(IntervalBlockEntity intervalBlock) {
@Override
public List findAllByMeterReading(MeterReadingEntity meterReading) {
- // TODO: Implement findAllByMeterReading query in repository
- return intervalBlockRepository.findByMeterReadingEntity(meterReading);
+ return intervalBlockRepository.findAllByMeterReadingId(meterReading.getId());
}
@Override
public IntervalBlockEntity findByURI(String uri) {
- // TODO: Implement findByURI query in repository
- return intervalBlockRepository.findByUri(uri).orElse(null);
+ // Note: findByURI removed from repository (non-indexed query on self_link_href)
+ // URI-based lookup should use ID-based queries instead
+ log.warn("findByURI is deprecated - use findById with extracted UUID instead");
+ return null;
}
@Override
diff --git a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/IntervalBlockRepositoryTest.java b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/IntervalBlockRepositoryTest.java
index b0ea7ba7..7fc07aa0 100644
--- a/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/IntervalBlockRepositoryTest.java
+++ b/openespi-common/src/test/java/org/greenbuttonalliance/espi/common/repositories/usage/IntervalBlockRepositoryTest.java
@@ -253,27 +253,6 @@ void shouldDeleteIntervalBlockByIdUsingCustomMethod() {
assertThat(intervalBlockRepository.existsById(intervalBlockId)).isFalse();
}
- @Test
- @DisplayName("Should delete interval block by UUID")
- void shouldDeleteIntervalBlockByUuid() {
- // Arrange
- IntervalBlockEntity intervalBlock = TestDataBuilders.createValidIntervalBlock();
- intervalBlock.setDescription("Interval Block for UUID Delete");
- IntervalBlockEntity saved = intervalBlockRepository.save(intervalBlock);
- UUID intervalBlockId = saved.getId();
- flushAndClear();
-
- // Verify it exists
- assertThat(intervalBlockRepository.existsById(intervalBlockId)).isTrue();
-
- // Act
- intervalBlockRepository.deleteByUuid(intervalBlockId);
- flushAndClear();
-
- // Assert
- assertThat(intervalBlockRepository.existsById(intervalBlockId)).isFalse();
- }
-
@Test
@DisplayName("Should find all interval blocks by meter reading ID")
void shouldFindAllIntervalBlocksByMeterReadingId() {
@@ -338,136 +317,12 @@ void shouldFindAllIntervalBlockIdsByUsagePointId() {
assertThat(intervalBlockIds).contains(savedIntervalBlocks.get(0).getId(), savedIntervalBlocks.get(1).getId());
}
- @Test
- @DisplayName("Should find all interval block IDs by xpath3")
- void shouldFindAllIntervalBlockIdsByXpath3() {
- // Arrange
- RetailCustomerEntity retailCustomer = TestDataBuilders.createValidRetailCustomer();
- retailCustomer.setUsername("customer@xpath3.com");
- RetailCustomerEntity savedCustomer = retailCustomerRepository.save(retailCustomer);
-
- UsagePointEntity usagePoint = TestDataBuilders.createValidUsagePoint();
- usagePoint.setDescription("Usage Point for Xpath3");
- usagePoint.setRetailCustomer(savedCustomer);
- UsagePointEntity savedUsagePoint = usagePointRepository.save(usagePoint);
-
- MeterReadingEntity meterReading = TestDataBuilders.createValidMeterReadingWithUsagePoint(savedUsagePoint);
- meterReading.setDescription("Meter Reading for Xpath3");
- MeterReadingEntity savedMeterReading = meterReadingRepository.save(meterReading);
-
- IntervalBlockEntity intervalBlock1 = TestDataBuilders.createValidIntervalBlockWithMeterReading(savedMeterReading);
- intervalBlock1.setDescription("Interval Block 1 for Xpath3");
- IntervalBlockEntity intervalBlock2 = TestDataBuilders.createValidIntervalBlockWithMeterReading(savedMeterReading);
- intervalBlock2.setDescription("Interval Block 2 for Xpath3");
-
- List savedIntervalBlocks = intervalBlockRepository.saveAll(List.of(intervalBlock1, intervalBlock2));
- flushAndClear();
-
- // Act
- List intervalBlockIds = intervalBlockRepository.findAllIdsByXpath3(
- savedCustomer.getId(),
- savedUsagePoint.getId(),
- savedMeterReading.getId()
- );
-
- // Assert
- assertThat(intervalBlockIds).hasSize(2);
- assertThat(intervalBlockIds).contains(savedIntervalBlocks.get(0).getId(), savedIntervalBlocks.get(1).getId());
- }
-
- @Test
- @DisplayName("Should find interval block ID by xpath")
- void shouldFindIntervalBlockIdByXpath() {
- // Arrange
- RetailCustomerEntity retailCustomer = TestDataBuilders.createValidRetailCustomer();
- retailCustomer.setUsername("customer@xpath.com");
- RetailCustomerEntity savedCustomer = retailCustomerRepository.save(retailCustomer);
-
- UsagePointEntity usagePoint = TestDataBuilders.createValidUsagePoint();
- usagePoint.setDescription("Usage Point for Xpath");
- usagePoint.setRetailCustomer(savedCustomer);
- UsagePointEntity savedUsagePoint = usagePointRepository.save(usagePoint);
-
- MeterReadingEntity meterReading = TestDataBuilders.createValidMeterReadingWithUsagePoint(savedUsagePoint);
- meterReading.setDescription("Meter Reading for Xpath");
- MeterReadingEntity savedMeterReading = meterReadingRepository.save(meterReading);
-
- IntervalBlockEntity intervalBlock = TestDataBuilders.createValidIntervalBlockWithMeterReading(savedMeterReading);
- intervalBlock.setDescription("Interval Block for Xpath");
- IntervalBlockEntity savedIntervalBlock = intervalBlockRepository.save(intervalBlock);
- flushAndClear();
-
- // Act
- Optional result = intervalBlockRepository.findIdByXpath(
- savedCustomer.getId(),
- savedUsagePoint.getId(),
- savedMeterReading.getId(),
- savedIntervalBlock.getId()
- );
-
- // Assert
- assertThat(result).isPresent();
- assertThat(result.get()).isEqualTo(savedIntervalBlock.getId());
- }
-
- @Test
- @DisplayName("Should find interval blocks by meter reading entity")
- void shouldFindIntervalBlocksByMeterReadingEntity() {
- // Arrange
- MeterReadingEntity meterReading = TestDataBuilders.createValidMeterReading();
- meterReading.setDescription("Meter Reading for Entity Query");
- MeterReadingEntity savedMeterReading = meterReadingRepository.save(meterReading);
-
- IntervalBlockEntity intervalBlock1 = TestDataBuilders.createValidIntervalBlockWithMeterReading(savedMeterReading);
- intervalBlock1.setDescription("Interval Block 1 for Entity Query");
- IntervalBlockEntity intervalBlock2 = TestDataBuilders.createValidIntervalBlockWithMeterReading(savedMeterReading);
- intervalBlock2.setDescription("Interval Block 2 for Entity Query");
-
- intervalBlockRepository.saveAll(List.of(intervalBlock1, intervalBlock2));
- flushAndClear();
-
- // Act
- List results = intervalBlockRepository.findByMeterReadingEntity(savedMeterReading);
-
- // Assert
- assertThat(results).hasSize(2);
- assertThat(results).extracting(IntervalBlockEntity::getDescription)
- .contains("Interval Block 1 for Entity Query", "Interval Block 2 for Entity Query");
- }
-
- @Test
- @DisplayName("Should find interval block by URI")
- void shouldFindIntervalBlockByUri() {
- // Arrange
- IntervalBlockEntity intervalBlock = TestDataBuilders.createValidIntervalBlock();
- intervalBlock.setDescription("Interval Block with URI");
-
- LinkType selfLink = new LinkType();
- selfLink.setHref("/espi/1_1/resource/IntervalBlock/123");
- selfLink.setRel("self");
- intervalBlock.setSelfLink(selfLink);
-
- intervalBlockRepository.save(intervalBlock);
- flushAndClear();
-
- // Act
- Optional result = intervalBlockRepository.findByUri("/espi/1_1/resource/IntervalBlock/123");
-
- // Assert
- assertThat(result).isPresent();
- assertThat(result.get().getDescription()).isEqualTo("Interval Block with URI");
- assertThat(result.get().getSelfLink().getHref()).isEqualTo("/espi/1_1/resource/IntervalBlock/123");
- }
-
@Test
@DisplayName("Should handle empty results gracefully")
void shouldHandleEmptyResultsGracefully() {
// Act & Assert
assertThat(intervalBlockRepository.findAllByMeterReadingId(UUID.randomUUID())).isEmpty();
assertThat(intervalBlockRepository.findAllIdsByUsagePointId(UUID.randomUUID())).isEmpty();
- assertThat(intervalBlockRepository.findAllIdsByXpath3(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID())).isEmpty();
- assertThat(intervalBlockRepository.findIdByXpath(UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID(), UUID.randomUUID())).isEmpty();
- assertThat(intervalBlockRepository.findByUri("nonexistent-uri")).isEmpty();
}
}